package com.bcxin.backend.domain.signature.service.impls;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bcxin.backend.domain.enums.DocumentType;
import com.bcxin.backend.domain.models.SignatureQueuesDTO;
import com.bcxin.backend.domain.models.SignatureQueuesDocument;
import com.bcxin.backend.domain.repositories.SignatureQueuesDocumentRepository;
import com.bcxin.backend.domain.repositories.SignatureQueuesRepository;
import com.bcxin.backend.domain.signature.service.BeiJingInJTLZSignature;
import com.bcxin.backend.domain.utils.FileUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.itextpdf.text.Document;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.*;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Instant;
import java.util.*;

/**
 * <b> 北京京通靓证业务 </b>
 * @author ZXF
 * @create 2024/12/16 0016 11:08
 * @version
 * @注意事项 </b>
 */
@Slf4j
@Service
public class BeiJingInJTLZServiceImpl implements BeiJingInJTLZSignature {
    private static final Logger logger = LoggerFactory.getLogger(BeiJingInJTLZServiceImpl.class);

    private final SignatureQueuesDocumentRepository signatureQueuesDocumentRepository;
    private final SignatureQueuesRepository signatureQueuesRepository;
    @Value("${myapps.domain.url}")
    String urlHead;
    @Value("${myapps.storage.root}")
    String rootPath;
    @Value("${myapps.signature.ferry_outside}")
    String ferryOutside;//外网进来文件存放地址
    @Value("${myapps.signature.ferry_inside}")
    String ferryInside;//内网出去文件存放地址

    @Autowired
    @Qualifier("primaryJdbcTemplate")
    protected JdbcTemplate jdbcTemplate;

    //保安资格证实施码
    private static String item_code_bazgz = "10001460100002888X110000";
    //保安许可证实施码
    private static String item_code_baxkz = "10001440100002888X110000";
//  文件生成后缀
    private static String file_suffix = ".bcx";

    //  写入汇总文件前缀
    private static String write_summary_document_prefix = "INJTLZDOCUMENT-";
    private static String write_summary_prefix = "INJTLZ-";
    private static String write_summary_fz_prefix = "INJTLZFZ-";

    private static int count_jtlz_document = 0;
    private static List<String> fileNames_jtlz_document = null;
    private static int count_jtlz = 0;
    private static List<String> fileNames_jtlz = null;
    private static int count_jtlz_fz = 0;
    private static List<String> fileNames_jtlz_fz = null;
    private final ThreadPoolTaskExecutor taskExecutor;
    public BeiJingInJTLZServiceImpl(SignatureQueuesDocumentRepository signatureQueuesDocumentRepository,SignatureQueuesRepository signatureQueuesRepository,ThreadPoolTaskExecutor taskExecutor) {
        this.signatureQueuesDocumentRepository = signatureQueuesDocumentRepository;
        this.signatureQueuesRepository = signatureQueuesRepository;
        this.taskExecutor = taskExecutor;
    }

    @Override
    public void initJTLZ() {
        //创建京通靓证数据上送ftp输出到外网
        try {
            createJTLZ();
        } catch (Exception e) {
            logger.error("initJTLZ 京通靓证信息", e);
        }
    }

    @Override
    public void initJTLZDocument() {
        //创建京通靓证许可证数据输出到外网
        try{
            createJTLZDocument();
        }catch (Exception e){
            logger.error("initJTLZDocument 京通靓证信息", e);
        }
    }

    @Override
    public void initJTLZFZ() {
        //已靓证证书编号发起废止
        try{
            createJTLZFZ();
        }catch (Exception e){
            logger.error("initJTLZFZ 京通靓证信息", e);
        }

    }

    @Override
    public void useJTLZ() {
        //读取政务网上送ftp输出到内网目录的处理结果，对结果进行处理
        updateJTLZ();
    }
    @Override
    public void useJTLZFZ() {
        //读取政务网上送ftp输出到内网目录的靓证废止处理结果，对结果进行处理
        updateJTLZFZ();
    }

    /**
     * <b> 批量插入符合签章条件的文书记录 </b>
     * @author ZXF
     * @create 2022/10/31 0031 13:33
     * @version
     * @注意事项 </b>
     */
    private void createJTLZ() {
        long v = System.currentTimeMillis();
        log.error("========> createJTLZ.SignatureQueuesDTO.批次 star  --------------------- ");
        //最后修改时间在5分钟内的通过记录 TODO 当前只查询资格证数据处理，还需要查询保安服务许可证数据
        Page<SignatureQueuesDTO> pageQueues = signatureQueuesRepository.pageQueuesBy5Minute(1, PageRequest.of(0, 70));
        List<SignatureQueuesDTO> queues = pageQueues.getContent();
        Snowflake snowflake = IdUtil.createSnowflake(1, 1);
        fileNames_jtlz = new ArrayList<>();
        count_jtlz = queues.size();

        for (SignatureQueuesDTO dto:queues) {
            this.taskExecutor.execute(() -> {
                try {
                    if (StringUtils.isEmpty(dto.getData()) || StringUtils.isEmpty(dto.getBusiness_id())) {
                        throw new Exception("签章记录数据缺失");
                    }
                    String sql = "SELECT item_attachment FROM tlk_certificate WHERE id = '" + dto.getBusiness_id() + "';";
                    List<String> attachment = jdbcTemplate.query(sql, new RowMapper<String>() {
                        @Override
                        public String mapRow(ResultSet rs, int rowNum) throws SQLException {
                            return rs.getString("item_attachment");
                        }
                    });
                    if (attachment.size() == 0 || StringUtils.isEmpty(attachment.get(0))) {
                        signatureQueuesRepository.updateSignature(0, new Date(), "原电子证书文件丢失，重新签章", dto.getId());
                        return;
//                        throw new Exception("电子证书不存在");
                    }
                    String pdfUrl = urlHead + attachment.get(0);
                    String path = rootPath + attachment.get(0);
                    Path path1 = Paths.get(path);
                    String fileName = path1.getFileName().toString();
                    String extension = "";
                    int dotIndex = fileName.lastIndexOf('.');
                    if (dotIndex != -1) {
                        extension = fileName.substring(dotIndex + 1);
                    }
                    log.error("========> createJTLZ.SignatureQueuesDTO.pdfUrl："+ pdfUrl);
                    String base64 = FileUtils.urlToBase64(pdfUrl);
                    if (StringUtils.isEmpty(base64)) {
                        throw new Exception("电子证书转换base64失败：" + pdfUrl);
                    }
                    SignatureData signatureData = JSONObject.parseObject(dto.getData(), SignatureData.class);
                    JSONObject data_field = new JSONObject();
                    data_field.put("ZZMC", "中华人民共和国保安员证");
                    data_field.put("CYRMC", signatureData.getName());
                    data_field.put("FZJGZZJGDM", "1111000000002888XF");//发证机构唯一标识
                    data_field.put("FZJGSSXZQHDM", "110000");//发证机构所属行政区划代码
                    data_field.put("CYRSFZJLX", "10");
                    data_field.put("CYRSFZJHM", signatureData.getIdcardno().toUpperCase());
                    data_field.put("FZRQ", DateUtil.formatDate(DateUtil.parseDate(signatureData.getFzDate())));
                    data_field.put("YXQJSRQ", "");
                    data_field.put("CSRQ", signatureData.getYear() + "年" + signatureData.getMonth() + "月" + signatureData.getDay() + "日");
                    data_field.put("ZZ", signatureData.getAddress());
                    data_field.put("ZP", "");
                    data_field.put("ZZHM", signatureData.getCertificateno());
                    data_field.put("FZJGMC", StringUtils.isEmpty(signatureData.getCertificatefrom())?"北京市公安局":signatureData.getCertificatefrom());
                    log.info("========> createJTLZ.SignatureQueuesDTO：data_field："+ JSON.toJSONString(data_field));
                    OperatorData operatorData = OperatorData.fixedOperatorData();
                    List<FileData> fileDataList = new ArrayList<>();
                    fileDataList.add(new FileData(fileName, extension, base64, "附件为" + signatureData.getName() + "（编号：" + signatureData.getCertificateno() + "）的电子资格证。"));
                    String bodyContent = license_issue("1111000000002888XF200010910300001", "保安员证核发", "DZYZ00002888XgNbYNa", data_field, operatorData, fileDataList);
                    // 内到外文件名规则：IN-JTLZ-01-1111-101.bcx（内网-京通靓证-资格证编码-itemcode-业务id）
                    String fName = "IN-JTLZ-01-" + item_code_bazgz + "-" + dto.getId() + "N" + snowflake.nextId() + file_suffix;
                    fileNames_jtlz.add(fName);
                    FileUtils.writeFile(ferryInside, fName, bodyContent);
                    signatureQueuesRepository.updateResult(new Date(),"靓证中",dto.getId());
                } catch (Exception e) {
                    log.error("========> createJTLZ.SignatureQueuesDTO.server.error: 【ID:" + dto.getId() + "】" + e.getMessage() + "    ----");
                }finally {
                    count_jtlz -=1;
                }
            });
        }
        //等待30秒，如果线程还没全部完成，则跳出循环
        int i = 0;
        while (count_jtlz > 0) {
            if(i==15){
                break;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            i++;
        }
        log.info("========> createJTLZ.SignatureQueuesDTO.批次 end  --------------------- ");
        if(fileNames_jtlz.size()>0){
            String minuteName = DateUtil.format(new Date(),"yyyyMMddHHmm");
            String writeFtpName = write_summary_prefix+minuteName+file_suffix;
            String body = String.join(",", fileNames_jtlz);
            log.info("========> createJTLZ.file：writeFtpName："+writeFtpName+" ，文件个数: " + fileNames_jtlz.size() +" ---");
            FileUtils.writeFile(ferryInside, writeFtpName, body);
        }

        log.info("========> createJTLZ.end ，总耗时："+(System.currentTimeMillis()-v)/1000+"秒----------------------------------------");
    }

    /**
     * <b> 保安服务许可证靓证 </b>
     * @author ZXF
     * @create 2022/10/31 0031 13:33
     * @version
     * @注意事项 </b>
     */
    private void createJTLZDocument() {
        long v = System.currentTimeMillis();
        log.error("========> createJTLZDocument.批次 star  --------------------- ");
        Page<SignatureQueuesDocument> pageDocumentQueues = signatureQueuesDocumentRepository.pageQueuesBy5Minute(1, PageRequest.of(0, 30));
        List<SignatureQueuesDocument> documentQueues = pageDocumentQueues.getContent();
        Snowflake snowflake = IdUtil.createSnowflake(1, 1);
        log.error("========> createJTLZDocument.size: " + documentQueues.size());
        fileNames_jtlz_document = new ArrayList<>();
        count_jtlz_document = documentQueues.size();
        for (SignatureQueuesDocument dto:documentQueues) {
            this.taskExecutor.execute(() -> {
                try {
                    SignatureQueuesDocument documentQueueF;
                    String sql = "";
                    if ("15".equals(dto.getBusiness_type())) {
                        sql = "SELECT b.item_comname,a.item_legalCardnumber,b.item_comaddress,b.item_legalname,b.item_fwfw,DATE_FORMAT(b.item_clrq, '%Y-%m-%d') AS item_clrq,DATE_FORMAT(b.item_fzrq, '%Y-%m-%d') AS item_fzrq,b.item_pzwh,b.item_certificatenum,b.item_organname,b.item_registeredcapital,a.item_security_z_file,a.item_security_f_file FROM tlk_securitysevicek a,tlk_companycertificate b WHERE a.id = b.ITEM_SOURCE_ID AND LENGTH(a.item_security_z_file)>0 AND LENGTH(a.item_security_f_file)>0 AND a.id = '" + dto.getBusiness_id() + "';";
                        documentQueueF = signatureQueuesDocumentRepository.getSignatureQueuesDocument(dto.getBusiness_id(), "16");
                    } else if ("25".equals(dto.getBusiness_type())) {
                        sql = "SELECT b.item_comname,a.ITEM_LEGAL_CARD_NUM item_legalCardnumber,b.item_comaddress,b.item_legalname,b.item_fwfw,DATE_FORMAT(b.item_clrq, '%Y-%m-%d') AS item_clrq,DATE_FORMAT(b.item_fzrq, '%Y-%m-%d') AS item_fzrq,b.item_pzwh,b.item_certificatenum,b.item_organname,b.item_registeredcapital,a.item_security_z_file,a.item_security_f_file FROM tlk_scope_namechanges a,tlk_companycertificate b WHERE a.id = b.ITEM_SOURCE_ID AND LENGTH(a.item_security_z_file)>0 AND LENGTH(a.item_security_f_file)>0 AND a.id = '" + dto.getBusiness_id() + "';";
                        documentQueueF = signatureQueuesDocumentRepository.getSignatureQueuesDocument(dto.getBusiness_id(), "26");
                    } else if ("45".equals(dto.getBusiness_type())) {
                        sql = "SELECT b.item_comname,a.ITEM_APPOINTMENTLEGALCARDNUMBER item_legalCardnumber,b.item_comaddress,b.item_legalname item_legalname,b.item_fwfw,DATE_FORMAT(b.item_clrq, '%Y-%m-%d') AS item_clrq,DATE_FORMAT(b.item_fzrq, '%Y-%m-%d') AS item_fzrq,b.item_pzwh,b.item_certificatenum,b.item_organname,b.item_registeredcapital,a.item_security_z_file,a.item_security_f_file FROM tlk_legalchangek a,tlk_companycertificate b WHERE a.id = b.ITEM_SOURCE_ID AND LENGTH(a.item_security_z_file)>0 AND LENGTH(a.item_security_f_file)>0 AND a.id = '" + dto.getBusiness_id() + "';";
                        documentQueueF = signatureQueuesDocumentRepository.getSignatureQueuesDocument(dto.getBusiness_id(), "46");
                    } else if ("55".equals(dto.getBusiness_type())) {
                        sql = "SELECT b.item_comname,a.ITEM_LEGAL_CARD_NUM item_legalCardnumber,b.item_comaddress,b.item_legalname,b.item_fwfw,DATE_FORMAT(b.item_clrq, '%Y-%m-%d') AS item_clrq,DATE_FORMAT(b.item_fzrq, '%Y-%m-%d') AS item_fzrq,b.item_pzwh,b.item_certificatenum,b.item_organname,b.item_registeredcapital,a.item_security_z_file,a.item_security_f_file FROM tlk_scope_addresschangek a,tlk_companycertificate b WHERE a.id = b.ITEM_SOURCE_ID AND LENGTH(a.item_security_z_file)>0 AND LENGTH(a.item_security_f_file)>0 AND a.id = '" + dto.getBusiness_id() + "';";
                        documentQueueF = signatureQueuesDocumentRepository.getSignatureQueuesDocument(dto.getBusiness_id(), "56");
                    } else if ("65".equals(dto.getBusiness_type())) {
                        sql = "SELECT b.item_comname,a.ITEM_LEGALIDCARDNUMBER item_legalCardnumber,b.item_comaddress,b.item_legalname,b.item_fwfw,DATE_FORMAT(b.item_clrq, '%Y-%m-%d') AS item_clrq,DATE_FORMAT(b.item_fzrq, '%Y-%m-%d') AS item_fzrq,b.item_pzwh,b.item_certificatenum,b.item_organname,b.item_registeredcapital,a.item_security_z_file,a.item_security_f_file FROM tlk_securityservicelicensechange a,tlk_companycertificate b WHERE a.id = b.ITEM_SOURCE_ID AND LENGTH(a.item_security_z_file)>0 AND LENGTH(a.item_security_f_file)>0 AND a.id = '" + dto.getBusiness_id() + "';";
                        documentQueueF = signatureQueuesDocumentRepository.getSignatureQueuesDocument(dto.getBusiness_id(), "66");
                    } else {
                        return;
                    }

                    log.error("========> createJTLZDocument.SignatureQueuesDocument.sql: " + sql);
                    List<Map<String, String>> attachment = jdbcTemplate.query(sql, new RowMapper<Map<String, String>>() {
                        @Override
                        public Map<String, String> mapRow(ResultSet rs, int rowNum) throws SQLException {
                            Map<String, String> map = Maps.newHashMap();
                            map.put("item_comname", rs.getString("item_comname"));
//                            map.put("item_uscc", rs.getString("item_uscc"));
                            map.put("item_legalCardnumber", rs.getString("item_legalCardnumber").trim());
                            map.put("item_comaddress", rs.getString("item_comaddress"));
                            map.put("item_legalname", rs.getString("item_legalname"));
                            map.put("item_fwfw", formatManagement(rs.getString("item_fwfw")));
                            map.put("item_clrq", rs.getString("item_clrq"));
                            map.put("item_fzrq", rs.getString("item_fzrq"));
                            map.put("item_pzwh", rs.getString("item_pzwh"));
                            map.put("item_certificatenum", rs.getString("item_certificatenum"));
                            map.put("item_organname", rs.getString("item_organname"));
                            map.put("item_registeredcapital", rs.getString("item_registeredcapital"));
                            map.put("item_security_z_file", rs.getString("item_security_z_file"));
                            map.put("item_security_f_file", rs.getString("item_security_f_file"));
                            return map;
                        }
                    });
                    if (attachment.size() == 0 || attachment.get(0) == null) {
                        throw new Exception("证书记录不存在");
                    }
                    Map<String, String> attachmentMap = attachment.get(0);
                    String item_security_z_file = dto.getFile_url();
                    String item_security_f_file = documentQueueF.getFile_url();

                    if (StringUtils.isEmpty(item_security_z_file) || StringUtils.isEmpty(item_security_f_file)) {
                        throw new Exception("电子证书地址不存在");
                    }
                    if (StringUtils.isEmpty(attachmentMap.get("item_legalCardnumber"))) {
                        throw new Exception("法人身份证号不存在");
                    }

                    String pathZ = rootPath + item_security_z_file;
                    String pathF = rootPath + item_security_f_file;
                    //合并正副本
                    String fileName = attachmentMap.get("item_comname") + "（编号：" + attachmentMap.get("item_pzwh") + "）的电子保安服务许可证";
                    String outpdf = rootPath + "/uploads/pdf/documenttemp/" + DateUtil.today() + "/" + fileName + ".pdf";
                    mergePdf(pathZ, pathF, outpdf);
                    //合并后文件转base64
                    String base64 = FileUtils.fileToBase64(outpdf);
                    //删除合并后文件
                    new File(outpdf).delete();

                    if (StringUtils.isEmpty(base64)) {
                        throw new Exception("电子证书正副本合并后转换base64失败：" + outpdf);
                    }
                    String certificatenum = attachmentMap.get("item_certificatenum");
                    if(StringUtils.isNotEmpty(certificatenum)){
                        if(!certificatenum.contains("京公保服")){
                            certificatenum = "京公保服"+certificatenum;
                        }
                        if(!certificatenum.contains("号")){
                            certificatenum = certificatenum+"号";
                        }
                    }
                    JSONObject data_field = new JSONObject();
                    data_field.put("ZZMC", "保安服务许可证");
                    data_field.put("FZJGZZJGDM", "1111000000002888XF");//发证机构唯一标识
                    data_field.put("FZJGSSXZQHDM", "110000");//发证机构所属行政区划代码
                    data_field.put("YXQJSRQ", "");
                    data_field.put("ZZHM", certificatenum);
                    data_field.put("CYRMC", attachmentMap.get("item_comname"));
                    data_field.put("CYRSFZJLX", "80");
                    data_field.put("CYRSFZJHM", attachmentMap.get("item_legalCardnumber"));
                    data_field.put("XKZZJH", certificatenum);
                    data_field.put("FR", attachmentMap.get("item_legalname"));
                    data_field.put("FZJGMC", attachmentMap.get("item_organname"));
                    data_field.put("FZRQ", attachmentMap.get("item_fzrq"));
                    data_field.put("ZS", attachmentMap.get("item_comaddress"));
                    data_field.put("FWFW", attachmentMap.get("item_fwfw"));
                    data_field.put("PZWH", attachmentMap.get("item_pzwh"));
                    data_field.put("ZCZB", StringUtils.isEmpty(attachmentMap.get("item_registeredcapital"))?"0.00":attachmentMap.get("item_registeredcapital"));
                    log.error("========> createJTLZDocument.SignatureQueuesDocument.data_field: " + data_field.toJSONString());
                    OperatorData operatorData = OperatorData.fixedOperatorData();
                    List<FileData> fileDataList = new ArrayList<>();
                    fileDataList.add(new FileData(fileName + ".pdf", "pdf", base64, fileName));
                    String bodyContent = license_issue("1111000000002888XF200010910200101", "保安服务公司设立许可", "DZYZ00002888XfxlBdz", data_field, operatorData, fileDataList);
                    // 内到外文件名规则：IN-JTLZ-15-1111-101.bcx（内网-京通靓证-保安服务许可证编码-itemcode-业务id）
                    String fName = "IN-JTLZ-15-" + item_code_baxkz + "-" + dto.getId()+"N"+ snowflake.nextId() + file_suffix;
                    fileNames_jtlz_document.add(fName);
                    FileUtils.writeFile(ferryInside, fName, bodyContent);
                    log.info("========> createJTLZDocument.server15.filename: " + fName + "    end----");
                } catch (Exception e) {
                    log.error("========> createJTLZDocument.server.error: " + e.getMessage() + "    ----");
                } finally {
                    count_jtlz_document -= 1;
                }
            });
        }
        //等待15秒，如果线程还没全部完成，则跳出循环
        int i = 0;
        while (count_jtlz_document > 0) {
            if(i==15){
                break;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            i++;
        }
        log.error("========> createJTLZDocument.批次 end  --------------------- ");
        if(fileNames_jtlz_document.size()>0){
            List<File> files;
            int sleepCount = 30;
            while (sleepCount > 0) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                files = FileUtils.filenames(ferryInside,"IN-JTLZ-15-");
                if(files.size()==0){
                    log.error("========> createJTLZDocument.批次 消耗至："+sleepCount+"！  --------------------- ");
                    break;
                }
                sleepCount --;
            }
            String minuteName = DateUtil.format(new Date(),"yyyyMMddHHmm");
            String writeFtpName = write_summary_document_prefix+minuteName+file_suffix;
            String body = String.join(",", fileNames_jtlz_document);
            log.error("========> createJTLZDocument.file：writeFtpName："+writeFtpName+" ，文件个数: " + fileNames_jtlz_document.size() +" ---");
            FileUtils.writeFile(ferryInside, writeFtpName, body);
        }
        log.info("========> createJTLZDocument.end ，总耗时："+(System.currentTimeMillis()-v)/1000+"秒----------------------------------------");
    }

    /**
     * <b> 2张pdf合并成一张pdf，至少需要一张pdf </b>
     * @param inpdf1 许可证正本绝对路径1
     * @param inpdf2 许可证副本绝对路径2
     * @param outpdf 输出pdf绝对路径
     * @author ZXF
     * @create 2025/02/28 0028 16:01
     * @version
     * @注意事项 </b>
     */
    private static void mergePdf(String inpdf1, String inpdf2, String outpdf) throws Exception {
        if (StringUtils.isEmpty(inpdf1)) {
            throw new FileNotFoundException("Font file not found: " + inpdf1);
        }
        File file = new File(outpdf);
        if (!file.exists()) {
            FileUtil.touch(file);
        }
        Document document = new Document(); // 横向A4纸
        PdfCopy copy = new PdfCopy(document, new FileOutputStream(outpdf));
        document.open();
        PdfReader reader1 = new PdfReader(inpdf1);
        PdfImportedPage page1 = copy.getImportedPage(reader1, 1);
        copy.addPage(page1);
        if (StringUtils.isNotEmpty(inpdf2)) {
            document.newPage();
            PdfReader reader2 = new PdfReader(inpdf2);
            PdfImportedPage page2 = copy.getImportedPage(reader2, 1);
            copy.addPage(page2);
        }
        document.close();
    }

    /**
     * <b> 经营范围转换 </b>
     * @author ZXF
     * @create 2024/12/25 0025 11:12
     * @version
     * @注意事项 </b>
     */
    private String formatManagement(String fwfw) {
        if (StringUtils.isEmpty(fwfw)) {
            return "";
        }
        StringBuffer management = new StringBuffer();
        //服务范围打印顺序  /*门卫  巡逻 守护 押运 随身护卫 安全检查 安全技术防范 安全风险评估 区域秩序维护 */
        String[] orders = new String[]{"01", "02", "03", "04", "05", "06", "07", "08", "09"};
        String[] ss = fwfw.split(";");
        for (int j = 0; j < orders.length; j++) {
            for (int i = 0; i < ss.length; i++) {
                if (orders[j].equals(ss[i])) {
                    if ("01".equals(ss[i])) {
                        management.append("门卫、");
                    } else if ("02".equals(ss[i])) {
                        management.append("巡逻、");
                    } else if ("03".equals(ss[i])) {
                        management.append("守护、");
                    } else if ("05".equals(ss[i])) {
                        management.append("随身护卫、");
                    } else if ("04".equals(ss[i])) {
                        management.append("武装押运、");
                    } else if ("07".equals(ss[i])) {
//                        management.append("区域秩序维护、");
                    } else if ("05".equals(ss[i])) {
                        management.append("安全检查、");
                    } else if ("08".equals(ss[i])) {
                        management.append("安全风险评估、");
                    } else if ("09".equals(ss[i])) {
                        management.append("安全技术防范、");
                    } else if ("10".equals(ss[i])) {
//                        management.append("其他");
                    }
                }

            }
        }
        return management.toString().substring(0, management.toString().length() - 1);
    }

    /**
     * <b> 京通靓证同步报文业务组装 </b>
     * @author ZXF
     * @create 2024/11/20 0020 17:33
     * @version
     * @注意事项 </b>
     */
    private String license_issue(String serviceCode,String serviceName,String sealCode,JSONObject data_field,OperatorData operatorData,List<FileData> fileDataList) {
        // 生成 token 的逻辑
        String license_code = "";
        try {
            JSONObject data = new JSONObject();
            JSONObject json = new JSONObject();
            json.put("service_item_code", serviceCode);// 事项编码
            json.put("service_item_name", serviceName);//事项名称
            json.put("license_group", "组别1");//照面模版组别名称
            json.put("biz_num", UUID.randomUUID().toString());
            JSONObject operator = new JSONObject();
            operator.put("account", operatorData.getAccount());
            operator.put("name", operatorData.getName());
            operator.put("identity_num", operatorData.getIdcardno());
            operator.put("role", operatorData.getRoleName());
            operator.put("service_org", operatorData.getOrgName());
            operator.put("division", operatorData.getRegionName());
            operator.put("division_code", operatorData.getRegionCode());
            json.put("operator", operator);
            json.put("data_fields", data_field);
            json.put("seal_code", sealCode);//章编码
            json.put("sign_attach", false);
            JSONArray attachments = new JSONArray();
            JSONObject attachment;
            for (FileData fileData : fileDataList){
                attachment = new JSONObject();
                attachment.put("is_show_template", "true");
                attachment.put("is_license_image", "true");
                attachment.put("file_type", fileData.getFiletype());
                attachment.put("name", fileData.getFilename());
                attachment.put("description", fileData.getDescription());
                attachment.put("file_data", fileData.getFilebase64());
                attachments.add(attachment);
            }
            json.put("attachments", attachments);
            data.put("data", json);
            String body = data.toJSONString();

            return body;
        } catch (Exception e) {
            log.error("====> 京通靓证信息推送定时任务.license_issue.error:", e);
            return license_code;
        }
    }

    /**
     * <b> 已靓证证书发起 废止 </b>
     * @author ZXF
     * @create 2022/10/31 0031 13:33
     * @version
     * @注意事项 </b>
     */
    private void createJTLZFZ() {
        //最后修改时间在5分钟内的通过记录 TODO 当查询jt_code=废止的记录
        Page<SignatureQueuesDTO> pageQueues = signatureQueuesRepository.pageQueuesByCodeFZ(1, PageRequest.of(0, 30));
        List<SignatureQueuesDTO> queues = pageQueues.getContent();
        fileNames_jtlz_fz = new ArrayList<>();
        count_jtlz_fz = queues.size();
        for (SignatureQueuesDTO dto:queues) {
            this.taskExecutor.execute(() -> {
                try {
                    if (StringUtils.isEmpty(dto.getData()) || StringUtils.isEmpty(dto.getBusiness_id())) {
                        throw new Exception("签章记录数据缺失");
                    }
                    SignatureData signatureData = JSONObject.parseObject(dto.getData(), SignatureData.class);
                    String bodyContent = license_issue_fz("1111000000002888XF200010910300001", "保安员证核发", signatureData.getCertificateno());
                    // 内到外文件名规则：IN-JTLZ-01-1111-101.bcx（内网-京通靓证-资格证编码-itemcode-业务id）
                    String fName = "IN-JTLZFZ-01-" + item_code_bazgz + "-" + dto.getId() + file_suffix;
                    fileNames_jtlz_fz.add(fName);
                    FileUtils.writeFile(ferryInside, fName, bodyContent);
                    log.error("========> createJTLZFZ.server01.filename: " + fName + "    end----");
                } catch (Exception e) {
                    log.error("========> createJTLZFZ.SignatureQueues.server.error: " + e.getMessage() + "    ----");
                }finally {
                    count_jtlz_fz -=1;
                }
            });
        }
        //等待15秒，如果线程还没全部完成，则跳出循环
        int i = 0;
        while (count_jtlz_fz > 0) {
            if(i==15){
                break;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            i++;
        }
        Page<SignatureQueuesDocument> pageDocumentQueues = signatureQueuesDocumentRepository.pageQueuesByCodeFZ(1, PageRequest.of(0, 30));
        List<SignatureQueuesDocument> documentQueues = pageDocumentQueues.getContent();
        log.error("========> createJTLZFZ.documentQueues.size: " + documentQueues.size());
        count_jtlz_fz = documentQueues.size();
        for (SignatureQueuesDocument dto:documentQueues) {
            this.taskExecutor.execute(() -> {
                try {
                    DocumentType dt = DocumentType.getByValue(Integer.parseInt(dto.getBusiness_type()));
                    String sql = "SELECT b.item_certificatenum FROM "+dt.getTableName()+" a,tlk_companycertificate b WHERE a.id = b.ITEM_SOURCE_ID AND a.id = '" + dto.getBusiness_id() + "';";
                    List<String> certificatenums = jdbcTemplate.query(sql, new RowMapper<String>() {
                        @Override
                        public String mapRow(ResultSet rs, int rowNum) throws SQLException {
                            return rs.getString("item_certificatenum");
                        }
                    });
                    if (certificatenums.size() == 0 || certificatenums.get(0) == null) {
                        throw new Exception("无有效证书编号");
                    }
                    String certificatenum = certificatenums.get(0);
                    log.error("========> createJTLZFZ.documentQueues.certificatenum: " + certificatenum);
                    String bodyContent = license_issue_fz("1111000000002888XF200010910200101", "保安服务公司设立许可", certificatenum);
                    // 内到外文件名规则：IN-JTLZ-15-1111-101.bcx（内网-京通靓证-保安服务许可证编码-itemcode-业务id）
                    String fName = "IN-JTLZFZ-15-" + item_code_baxkz + "-" + dto.getId() + file_suffix;
                    fileNames_jtlz_fz.add(fName);
                    FileUtils.writeFile(ferryInside, fName, bodyContent);
                    log.error("========> createJTLZFZ.server15.filename: " + fName + "    end----");
                } catch (Exception e) {
                    log.error("========> createJTLZFZ.SignatureQueuesDocument.server.error: businessId={}; url={}----", dto.getBusiness_id(), dto.getFile_url(), e);
                } finally {
                    count_jtlz_fz -= 1;
                }
            });
        }
        //等待15秒，如果线程还没全部完成，则跳出循环
        i = 0;
        while (count_jtlz_fz > 0) {
            if(i==15){
                break;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            i++;
        }
        if(fileNames_jtlz_fz.size()>0){
            String minuteName = DateUtil.format(new Date(),"yyyyMMddHHmm");
            String writeFtpName = write_summary_fz_prefix+minuteName+file_suffix;
            String body = String.join(",", fileNames_jtlz_fz);
            log.error("========> createJTLZ.file:writeFtpName:"+writeFtpName+" ,body: " + body +" ---");
            FileUtils.writeFile(ferryInside, writeFtpName, body);
        }
        log.error("========> createJTLZ.end ----------------------------------------");
    }

    /**
     * <b> 京通靓证(废止)同步报文业务组装 </b>
     * @author ZXF
     * @create 2024/11/20 0020 17:33
     * @version
     * @注意事项 </b>
     */
    private String license_issue_fz(String serviceCode,String serviceName,String idCode) {
        log.error("====> 京通靓证信息废止定时任务.license_issue_fz.star---------");
        // 生成 token 的逻辑
        String license_code = "";
        try {
            OperatorData operatorData = OperatorData.fixedOperatorData();
            JSONObject data = new JSONObject();
            JSONObject json = new JSONObject();
            json.put("id_code", idCode);// 证照号码
            json.put("service_item_code", serviceCode);// 事项编码
            json.put("service_item_name", serviceName);//事项名称
            json.put("biz_num", UUID.randomUUID().toString());
            JSONObject operator = new JSONObject();
            operator.put("account", operatorData.getAccount());
            operator.put("name", operatorData.getName());
            operator.put("identity_num", operatorData.getIdcardno());
            operator.put("role", operatorData.getRoleName());
            operator.put("service_org", operatorData.getOrgName());
            operator.put("division", operatorData.getRegionName());
            operator.put("division_code", operatorData.getRegionCode());
            json.put("operator", operator);
            data.put("data", json);
            String body = data.toJSONString();
            log.error("====> 京通靓证信息废止定时任务.license_issue_fz.请求报文：" + body);
            return body;
        } catch (Exception e) {
            log.error("====> 京通靓证信息废止定时任务.license_issue_fz.error:" + e.getMessage());
            return license_code;
        }
    }

    /**
     * <b> 获取新UUID文件名 </b>
     * @author ZXF
     * @create 2024/12/09 0009 15:09
     * @version
     * @注意事项 </b>
     */
    public static String fileUUIDName(String filePath) {
        Path path = Paths.get(filePath);
        String fileName = path.getFileName().toString();

        int lastDotIndex = fileName.lastIndexOf('.');
        if (lastDotIndex > 0 && lastDotIndex < fileName.length() - 1) {
            String fileExtension = UUID.randomUUID().toString()+"."+fileName.substring(lastDotIndex + 1);
            return fileExtension;
        }
        return "";
    }

    /**
     * <b> 根据文件地址重新复制一份，返回新文件地址 </b>
     * @author ZXF
     * @create 2024/11/23 0023 16:13
     * @version
     * @注意事项 </b>
     */
    private static String copyFile(String originalFilePath,String rootPath) {

        try {
            // 将字符串路径转换为Path对象
            Path sourcePath = Paths.get(rootPath+originalFilePath);

            // 获取文件名和扩展名
            String fileName = sourcePath.getFileName().toString();
            String fileExtension = getFileExtension(fileName);

            // 生成新的文件名（UUID）
            String newFileName = UUID.randomUUID().toString() + "." + fileExtension;

            // 新文件路径
            Path targetPath = sourcePath.getParent().resolve(newFileName);

            // 使用Files.copy方法复制文件
            Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
            String newPath = targetPath.toString().replace(rootPath,"");
            System.out.println("文件复制成功！新文件路径: " + newPath);
            return newPath;
        } catch (Exception e) {
            logger.error("文件复制失败", e);
        }

        return "";
    }

    // 获取文件扩展名的方法
    private static String getFileExtension(String fileName) {
        int lastIndexOfDot = fileName.lastIndexOf('.');
        if (lastIndexOfDot == -1) {
            return ""; // 没有扩展名
        }
        return fileName.substring(lastIndexOfDot + 1);
    }

    /**
     * <b> 解析靓证结果返回 TODO 外到内数据文件未开发 </b>
     * @author ZXF
     * @create 2024/11/27 0027 16:55
     * @version
     * @注意事项 </b>
     */
    private void updateJTLZ(){
        //读取外到内文件
        List<File> files = FileUtils.filenames(ferryOutside,"OUT-JTLZ-");
        log.error("====> 解析靓证结果定时任务.updateJTLZ.files：" + files.size());
        JSONObject json;
        for (File file: files){
            String filename = file.getName();
//          readFtpName文件内容（str）格式：
            String[] arr = filename.replace(file_suffix,"").split("-");
            String type = arr[2];
            String id = arr[4];
            if(id.contains("N")){
                id = id.split("N")[0];
            }
            log.error("====> 解析靓证结果定时任务.updateJTLZ.filename：" + filename);
            if(!"01".equals(type) && !"15".equals(type)){
                log.error("====> 解析靓证结果定时任务.updateJTLZ.filename：文件类型不存在");
                continue;
            }
            String content = FileUtil.readString(file, StandardCharsets.UTF_8);
            if(StringUtils.isEmpty(content)){
                log.error("====> 解析靓证结果定时任务.updateJTLZ.fail：type="+type+",id="+id+" end");
                if("01".equals(type)){
                    signatureQueuesRepository.updateJtCode("",new Date(),"靓证失败，请手动设置最后修改时间为当前进行重试",id);
                }else if("15".equals(type)){
                    signatureQueuesDocumentRepository.updateJtCode("",new Date(),"靓证失败，请手动设置最后修改时间为当前进行重试",Long.parseLong(id));
                }
                continue;
            }
            log.error("====> 解析靓证结果定时任务.updateJTLZ.result：" + content);
            FileUtil.del(file);
            log.error("====> 解析靓证结果定时任务.updateJTLZ.删除文件：" + filename);
            try{
                json = JSONObject.parseObject(content);
                //失败处理
                if("FAILURE".equals(json.getString("ack_code"))){
                    String errJson = json.getString("errors");
                    if("01".equals(type)){
                        if(errJson.contains("存在重复的电子证照")){
                            //出现重复情况，查询历史有效靓证编号给重复数据赋值
                            String finalId = id;
                            new Thread(()-> {
                                String data = signatureQueuesRepository.getData(finalId);
                                SignatureData signatureData = JSONObject.parseObject(data, SignatureData.class);
                                List<SignatureQueuesDTO> list = signatureQueuesRepository.findByDataLike("%" + signatureData.getCertificateno() + "%");
                                String jtCode = finalId + "YES";
                                for (SignatureQueuesDTO dto1 : list) {
                                    if (StringUtils.isNotEmpty(dto1.getJt_code()) && !dto1.getJt_code().contains("YES")) {
                                        jtCode = dto1.getJt_code();
                                    }
                                }
                                log.error("====> 解析靓证结果定时任务.updateJTLZ.重复的电子证照.jtCode：" + jtCode);
                                for (SignatureQueuesDTO dto1 : list) {
                                    if (StringUtils.isEmpty(dto1.getJt_code()) || dto1.getJt_code().contains("YES")) {
                                        signatureQueuesRepository.updateJtCode(jtCode, new Date(), "完成靓证:存在重复的电子证照（无需重新靓证）", dto1.getId());
                                    }
                                }
                            }).start();
                            continue;
                        }
                        if("[]".equals(errJson)){
                            signatureQueuesRepository.updateJtCode("",new Date(),"靓证失败:异常信息为空",id);
                            continue;
                        }
                        JSONArray jsonArray = JSONArray.parseArray(errJson);
                        JSONObject errObj = (JSONObject)jsonArray.get(0);
                        signatureQueuesRepository.updateJtCode("",new Date(),"靓证失败:"+errObj.getString("message"),id);
                    }else if("15".equals(type)){
                        if(errJson.contains("存在重复的电子证照")){
                            //出现重复情况，查询历史有效靓证编号给重复数据赋值
                            String finalId1 = id;
                            new Thread(()-> {
                                String jtCode = finalId1 + "YES";
                                signatureQueuesDocumentRepository.updateJtCode(jtCode, new Date(), "完成靓证:存在重复的电子证照（无需重新靓证）", Long.parseLong(finalId1));
                            }).start();
                            continue;
                        }
                        if("[]".equals(errJson)){
                            signatureQueuesDocumentRepository.updateJtCode("",new Date(),"靓证失败:异常信息为空",Long.parseLong(id));
                            continue;
                        }
                        JSONArray jsonArray = JSONArray.parseArray(errJson);
                        JSONObject errObj = (JSONObject)jsonArray.get(0);
                        signatureQueuesDocumentRepository.updateJtCode("",new Date(),"靓证失败:"+errObj.getString("message"),Long.parseLong(id));
                    }

                    continue;
                }
                //成功处理
                JSONObject data = json.getObject("data",JSONObject.class);
                String jtcode = data.getString("auth_code");
                if(StringUtils.isEmpty(jtcode)){
                    log.error("====> 解析靓证结果定时任务.updateJTLZ.fail：license_code=null,type="+type+",id="+id+" end");
                    if("01".equals(type)){
                        signatureQueuesRepository.updateJtCode("",new Date(),"靓证失败:靓证编号丢失",id);
                    }else if("15".equals(type)){
                        signatureQueuesDocumentRepository.updateJtCode("",new Date(),"靓证失败:靓证编号丢失",Long.parseLong(id));
                    }
                    continue;
                }
                if("01".equals(type)){
                    signatureQueuesRepository.updateJtCode(jtcode,new Date(),"完成靓证",id);
                }else if("15".equals(type)){
                    signatureQueuesDocumentRepository.updateJtCode(jtcode,new Date(),"完成靓证",Long.parseLong(id));
                }
                log.error("====> 解析靓证结果定时任务.updateJTLZ.success：end");
            } catch (Exception e){
                log.error("====> 解析靓证结果定时任务.updateJTLZ.fail：" + e.getMessage());
            }
        }
    }

    /**
     * <b> 解析靓证废止结果返回 </b>
     * @author ZXF
     * @create 2024/11/27 0027 16:55
     * @version
     * @注意事项 </b>
     */
    private void updateJTLZFZ(){
        //读取外到内文件
        List<File> files = FileUtils.filenames(ferryOutside,"OUT-JTLZFZ-");
        log.error("====> 解析靓证废止结果定时任务.updateJTLZFZ.files：" + files.size());
        JSONObject json;
        for (File file: files){
            String filename = file.getName();
            String[] arr = filename.replace(file_suffix,"").split("-");
            String type = arr[2];
            String id = arr[4];
            log.error("====> 解析靓证废止结果定时任务.updateJTLZFZ.filename：" + filename);
            if(!"01".equals(type) && !"15".equals(type)){
                log.error("====> 解析靓证废止结果定时任务.updateJTLZFZ.filename：文件类型不存在");
                continue;
            }
            String content = FileUtil.readString(file, StandardCharsets.UTF_8);
            if(StringUtils.isEmpty(content)){
                log.error("====> 解析靓证废止结果定时任务.updateJTLZFZ.fail：type="+type+",id="+id+" end");
                if("01".equals(type)){
                    signatureQueuesRepository.updateJtCode("废止",new Date(),"靓证废止失败，无有效返回",id);
                }else if("15".equals(type)){
                    signatureQueuesDocumentRepository.updateJtCode("废止",new Date(),"靓证废止失败，无有效返回",Long.parseLong(id));
                }
                continue;
            }
            log.error("====> 解析靓证废止结果定时任务.updateJTLZFZ.result：" + content);
            FileUtil.del(file);
            log.error("====> 解析靓证废止结果定时任务.updateJTLZFZ.删除文件：" + filename);
            try{
                json = JSONObject.parseObject(content);
                //失败处理
                if("FAILURE".equals(json.getString("ack_code"))){
                    String errJson = json.getString("errors");
                    JSONArray jsonArray = JSONArray.parseArray(errJson);
                    JSONObject errObj = (JSONObject)jsonArray.get(0);
                    String msg = errObj.getString("message");
                    if("01".equals(type)){
                        if(StringUtils.isNotEmpty(msg)&& (msg.contains("该电子证照不存在")|| msg.contains("文件不存在或内容为空"))){
                            signatureQueuesRepository.updateJtCode("",new Date(),"靓证废止失败:"+msg,id);
                        } else {
                            signatureQueuesRepository.updateJtCode("废止",new Date(),"靓证废止失败:"+msg,id);
                        }
                    }else if("15".equals(type)){
                        if(StringUtils.isNotEmpty(msg)&& (msg.contains("该电子证照不存在")|| msg.contains("文件不存在或内容为空"))){
                            signatureQueuesDocumentRepository.updateJtCode("",new Date(),"靓证废止失败:"+msg,Long.parseLong(id));
                        } else {
                            signatureQueuesDocumentRepository.updateJtCode("废止",new Date(),"靓证废止失败:"+msg,Long.parseLong(id));
                        }
                    }
                    continue;
                }else{
                    if("01".equals(type)){
                        signatureQueuesRepository.updateJtCode("",new Date(),"靓证废止成功:重新靓证",id);
                    }else if("15".equals(type)){
                        signatureQueuesDocumentRepository.updateJtCode("",new Date(),"靓证废止成功:重新靓证",Long.parseLong(id));
                    }
                }
                log.error("====> 解析靓证废止结果定时任务.updateJTLZFZ.success：end");
            } catch (Exception e){
                log.error("====> 解析靓证废止结果定时任务.updateJTLZFZ.fail：" + e.getMessage());
            }
        }
    }

    public static boolean base64ToFile(String base64String, String filePath) {
        try {
            // 解码 Base64 字符串为字节数组
            byte[] bytes = Base64.decode(base64String);

            // 获取文件对象
            File file = FileUtil.file(filePath);

            // 创建文件输出流
            try (FileOutputStream fos = new FileOutputStream(file)) {
                // 写入字节数组到文件
                IoUtil.write(fos, true, bytes);
            }

            return true;
        } catch (IOException e) {
            return false;
        }
    }

    @Data
    public static class SignatureData {
        private String name;
        private String address;
        private String headphoto;
        private String idcardno;
        private String year;
        private String month;
        private String day;
        private String certificateno;
        private String certificatefrom;
        private String isSignature;
        //发证日期
        private String fzDate;
        private String zYear;
        private String zMonth;
        private String zDay;
        private String signatureImg;
        //未签章pdf存储路径
        private String unSignaturePDF;
        //已签章pdf扫描路径
        private String signaturePDF;
    }


    @Data
    public static class OperatorData {
        private String account;//账号
        private String name;
        private String idcardno;
        private String orgName;//机构名称
        private String roleName;//角色名称
        private String regionName;//归属区域
        private String regionCode;

        public static OperatorData fixedOperatorData(){
            OperatorData operatorData = new OperatorData();
            operatorData.setAccount("003849");
            operatorData.setName("王鑫");
            operatorData.setIdcardno("110104198412242026");
            operatorData.setOrgName("北京市治安管理总队");
            operatorData.setRoleName("二级警长");
            operatorData.setRegionName("西城区");
            operatorData.setRegionCode("110102");
            return operatorData;
        }
    }


    @Data
    public static class FileData {
        private String filename;//文件名
        private String filetype;//文件类型
        private String filebase64;//文件base64
        private String description;//说明

        public FileData(String filename, String filetype, String filebase64, String description){
            this.filename = filename;
            this.filetype = filetype;
            this.filebase64 = filebase64;
            this.description = description;
        }
    }

}
