package com.malk.boyang.service.impl;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.malk.boyang.service.BoyangService;
import com.malk.boyang.utils.HTTPHelper;
import com.malk.server.common.McR;
import com.malk.server.dingtalk.DDConf;
import com.malk.server.dingtalk.DDR_New;
import com.malk.service.dingtalk.*;
import com.malk.utils.UtilHttp;
import com.malk.utils.UtilMap;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Slf4j
@Service
public class BoyangServiceImpl implements BoyangService {
    @Autowired
    private DDClient ddClient;

    @Autowired
    private DDConf ddConf;

    @Autowired
    private DDClient_Workflow ddClient_workflow;

    @Autowired
    private DDClient_Personnel ddClient_personnel;

    @Autowired
    private DDClient_Contacts ddClient_contacts;

    @Value("${dingtalk.downloadPath}")
    private String downloadPath;

    @Value("${dingtalk.operatorUnionId}")
    private String operatorUnionId;

    @Value("${dingtalk.operator}")
    private String operator;

    @Value("${dingtalk.spaceId}")
    private String spaceId;

    @Value("${dingtalk.parentDentryUuid}")
    private String parentDentryUuid;

    /*//测试环境
    final static String projectId = "1000004";// 应用ID
    final static String secret = "96Uh7CR83NkN3TA6";// 应用密钥
    final static String businessTypeCode = "889726e889ab84fea3514748d6df565c";// 钉钉对接测试业务模板
    final static String organizationCode = "b60a9c18b8cc4ecc80e30f36b4267a68";// 宁波博洋家纺集团有限公司
    final static String host = "http://122.227.225.202:9011/";// 接口调用域名
    final static String intranet_host = "http://11.0.11.62/";// 接口调用域名(内网)
    final static String signerUser = "wangze";// 签署人
    final static String cw_signerUser = "wozy";// 财务签署人
    final static String uploadFileUrlHost = "http://11.0.11.62:8199/";// 返回的上传文件url host*/

    //==================================================== （本地）e签宝对接测试-用印申请 ====================================================
//    final static String signActivityId = "802c_7eaf";//签署区设置节点
//    final static String[] activityIds = new String[]{"f0e3_8ef9","2af6_ccd3","68d8_0e65"};//签署节点：e签宝签署节点id、e签宝财务签署节点id、e签宝会签签署节点id


    //==================================================== e签宝对接测试-用印申请 ====================================================
//    final static String signActivityId = "802c_7eaf";//签署区设置节点
//    final static String[] activityIds = new String[]{"f0e3_8ef9","2af6_ccd3","b8e5_160a"};//签署节点：e签宝签署节点id、e签宝财务签署节点id、e签宝会签签署节点id

    //==================================================== 用印申请 ====================================================
    final static String signActivityId = "c331_1d4f";//签署区设置节点
    final static String[] activityIds = new String[]{"19b2_ec4a","bfeb_c637","433b_839a"};//签署节点：e签宝签署节点id、e签宝财务签署节点id、e签宝会签签署节点id

    //正式环境
    final static String projectId = "1000003";// 应用ID
    final static String secret = "5V6xsY3q8JWZ9Qik";// 应用密钥
    final static String businessTypeCode = "f5e84fdb04ef4ead57f37e2a60729066";// 钉钉OA对接业务模板
    final static String organizationCode = "0c8dd6496e7f4d228ec033aeef95a526";// 宁波博洋家纺集团有限公司
    final static String host = "https://dzqz.beyond-it-service.com/";// 接口调用域名
    final static String intranet_host = "http://11.0.11.82/";// 接口调用域名(内网)
    final static String signerUser = "22060093";// 签署人
    final static String cw_signerUser = "13586834789";// 财务签署人
    final static String uploadFileUrlHost = "http://11.0.11.82:8199/";// 返回的上传文件url host




    @Override
    public McR eSignSubmit(Map map) {
        String processInstanceId = UtilMap.getString(map, "processInstanceId");

        Map result = ddClient_workflow.getProcessInstanceId(ddClient.getAccessToken(), processInstanceId);

        List<Map> formComponentValues = UtilMap.getList(result, "formComponentValues");

        List<Map<String,String>> fileList = new ArrayList<>();

        String subject = "";
        String remark = "";
        String url = "https://aflow.dingtalk.com/dingtalk/web/query/pchomepage.htm?from=oflow&op=true&corpid=ding76a1e955807327f7f5bf40eda33b7ba0#/plainapproval?procInstId="+processInstanceId;
        List<String> sealTypeCodeList = new ArrayList<>();
        List<Map> companyList = new ArrayList<>();
        boolean hasFiance = false;

        for (Map formComponentValue : formComponentValues) {
            String id = UtilMap.getString(formComponentValue, "id");

            switch (id){
                case "TextField-K2AD4O5B"://文件名称
                    subject = UtilMap.getString(formComponentValue, "value");

                    //不支持: $\/:*?"<>|
                    subject = subject.replaceAll("[$/:*?\"<>]"," ");

                    break;
                case "DDSelectField_RIHDKAA2UK00"://文件类型
                    String fileType = UtilMap.getString(formComponentValue, "value");
                    break;
                case "DDSelectField_1G1RJG8MDU9S0"://合同/协议类文件
                    String htxywj = UtilMap.getString(formComponentValue, "value");
                    break;
                case "DDSelectField_28JHJ0TGPN6S"://是否需要骑缝章
                    String isQifeng = UtilMap.getString(formComponentValue, "value");
                    break;
                case "TextField_1P4TDG767SRK0"://文件份数
                    String fileQty = UtilMap.getString(formComponentValue, "value");
                    break;
                case "DDMultiSelectField_1L3KU3TLX6PS0"://印章所属公司
                    String companyStr = UtilMap.getString(formComponentValue, "extValue");
                    companyList = (List<Map>) JSONObject.parse(companyStr);
                    break;
                case "DDMultiSelectField_1FW3TFDTJ6V40"://印章类型
                    String yzlxStr = UtilMap.getString(formComponentValue, "value");
                    List<String> yzlxList = (List<String>) JSONObject.parse(yzlxStr);

                    for (String yzlx : yzlxList) {
                           switch (yzlx){
                               case "公章":sealTypeCodeList.add("PUBLIC");break;
                               case "合同章":sealTypeCodeList.add("CONTRACT");break;
                               case "财务章":sealTypeCodeList.add("FINANCE");hasFiance = true;break;
                               case "其他":sealTypeCodeList.add("COMMON");break;
                               case "人事专用章":sealTypeCodeList.add("PERSONNEL");break;
                               case "法人章":sealTypeCodeList.add("LEGAL-PERSON-SEAL");break;
                               default:break;
                           }
                    }
                    break;
                case "DDSelectField_1JL8JW2HM5PC0"://用印方式
                    String yyfs = UtilMap.getString(formComponentValue, "value");

                    if (!"电子签章".equals(yyfs)){
//                        return McR.success("非电子签章无需处理");
                    }
                    break;
                case "TextField_17O0AUUY9L6K0"://签署备注
                    remark = UtilMap.getString(formComponentValue, "value");
                    break;
                case "DDAttachment_1V002NDXYVKW0"://附件
                    String attachmentListStr = UtilMap.getString(formComponentValue, "value");
                    List<Map> attachmentList = (List<Map>) JSONObject.parse(attachmentListStr);
                    for (Map attachment : attachmentList) {
                        String fileId = UtilMap.getString(attachment, "fileId");
                        String fileName = UtilMap.getString(attachment, "fileName");

                        //下载钉盘文件
                        String filePath = downloadPath + fileName;
                        downloadDdFile(processInstanceId,fileId,filePath);

                        Map fileMap = new HashMap();
                        fileMap.put("filePath", filePath);
                        fileMap.put("fileName", fileName);

                        fileList.add(fileMap);
                    }
                    break;
            }
        }

        //查询钉钉用户信息
        String name = "";
        String mobile = "";
        String originatorUserId = UtilMap.getString(result, "originatorUserId");

        List<Map> employeeInfos = ddClient_personnel.getEmployeeInfos(ddClient.getAccessToken(), Arrays.asList(originatorUserId), ddConf.getAgentId(), Arrays.asList("sys00-name", "sys00-mobile"));
        Map employeeInfo = employeeInfos.get(0);

        List<Map> fieldDataList = UtilMap.getList(employeeInfo, "field_data_list");

        for (Map fieldData : fieldDataList) {
            String fieldCode = UtilMap.getString(fieldData, "field_code");
            List<Map> fieldValueList = UtilMap.getList(fieldData, "field_value_list");

            switch (fieldCode){
                case "sys00-name":
                    name = UtilMap.getString(fieldValueList.get(0),"value");
                    break;
                case "sys00-mobile":
                    mobile = UtilMap.getString(fieldValueList.get(0),"value").replace("+86-","");
                    break;
                default:break;
            }
        }

        String eqbUserCode = "";

        //查询钉钉用户在e签宝是否存在
        Map body = new HashMap();
        body.put("name", name);
        body.put("mobile", mobile);
        List<Map> eqbUsers = (List<Map>) eqbPost(host + "manage/v1/innerUsers/detail", body);
        if (!eqbUsers.isEmpty()){
            Map eqbUser = eqbUsers.get(0);

            eqbUserCode = UtilMap.getString(eqbUser, "userCode");
        }else {
            //新增e签宝用户
            Map eqbUser = new HashMap();
            eqbUser.put("name", name);
            eqbUser.put("mobile", mobile);
            eqbUser.put("customAccountNo", mobile);
            eqbUser.put("mainOrganizationCode", organizationCode);//默认组织账号

            Map map1 = (Map) eqbPost(host + "manage/v1/innerUsers/create",Arrays.asList(eqbUser));

            List<Map> successData = UtilMap.getList(map1, "successData");

            eqbUserCode = UtilMap.getString(successData.get(0), "userCode");
        }

        // 创建主Map
        Map<String, Object> requestMap = new HashMap<>();

        // 1. 基本字段
        requestMap.put("subject", subject);
        requestMap.put("businessNo", processInstanceId);
        requestMap.put("businessTypeCode", businessTypeCode);
        requestMap.put("remark", remark);
//        requestMap.put("redirectUrl", url);

        // 2. manualConfig 嵌套对象
        Map<String, Object> manualConfig = new HashMap<>();
        manualConfig.put("startMode", 1);
        manualConfig.put("finishMode", 0);

        // signingAreaPageConfig 子对象
        Map<String, Object> signingAreaPageConfig = new HashMap<>();
        signingAreaPageConfig.put("showStartButton", 1);
        manualConfig.put("signingAreaPageConfig", signingAreaPageConfig);

        requestMap.put("manualConfig", manualConfig);

        // 3. initiatorInfo 嵌套对象
        Map<String, Object> initiatorInfo = new HashMap<>();
        initiatorInfo.put("userCode", eqbUserCode);
        /*initiatorInfo.put("customAccountNo", "");
        initiatorInfo.put("departmentCode", "");
        initiatorInfo.put("customDepartmentNo", "");
        initiatorInfo.put("organizationCode", "");
        initiatorInfo.put("customOrgNo", "");*/

        requestMap.put("initiatorInfo", initiatorInfo);

        // 4. signFiles 列表
        List<Map<String, Object>> signFiles = new ArrayList<>();

        for (int i = 0; i < fileList.size(); i++) {
            String fileKey = uploadAndConvertEqbFile(fileList.get(i).get("filePath"), fileList.get(i).get("fileName"));

            Map<String, Object> file = new HashMap<>();
            file.put("fileKey", fileKey);
            file.put("fileOrder", i + 1);

            signFiles.add(file);
        }

        requestMap.put("signFiles", signFiles);

        // 5. signerInfos 列表
        List<Map<String, Object>> signerInfos = new ArrayList<>();

        for (int i = 0; i < companyList.size(); i++) {
            Map<String, Object> signer = new HashMap<>();
            signer.put("userType", "1");
//            signer.put("userCode", "FINANCE".equals(sealType) ? cw_signerUser : signerUser);
            signer.put("userCode", signerUser);
            signer.put("organizationCode", UtilMap.getString(companyList.get(i),"key"));
//            signer.put("organizationCode", organizationCode);
            signer.put("signNode", 1);
            if (hasFiance){
                signer.put("signMode", "1");//0：顺序签  1：无序签
                signer.put("signOrder", 1);
            }else {
                signer.put("signMode", "0");//0：顺序签  1：无序签
                signer.put("signOrder", i + 1);
            }

//            signer.put("sealTypeCode", String.join(",",sealTypeCodeList));

            signerInfos.add(signer);

            if (hasFiance){
                Map signer2 = new HashMap<>(signer);
                signer2.put("userCode", cw_signerUser);
                signer2.put("signOrder", 1);

                signerInfos.add(signer2);
            }
        }


        requestMap.put("signerInfos", signerInfos);

        // 打印结果（可选）
        System.out.println("入参："+JSONObject.toJSONString(requestMap));

        //使用文件发起签署（一步发起签署）
        Map data = (Map) eqbPost(host + "/esign-signs/v1/signFlow/createAndStart", requestMap);

        //签署区设置地址回写评论
        Map manualConfig1 = UtilMap.getMap(data, "manualConfig");

        Map signingAreaPageInfos = UtilMap.getMap(manualConfig1, "signingAreaPageInfos");

        String signingAreaPageUrl = UtilMap.getString(signingAreaPageInfos, "signingAreaPageUrl");

        String newSigningAreaPageUrl = signingAreaPageUrl.replace(intranet_host,host);

        comment(processInstanceId,originatorUserId,"签署区设置地址："+newSigningAreaPageUrl,null);

        return McR.success();
    }

    @Async
    @Override
    public void signed(String processInstanceId,String signFlowId) {
        try {
            Map result = ddClient_workflow.getProcessInstanceId(ddClient.getAccessToken(), processInstanceId);
            String originatorUserId = UtilMap.getString(result, "originatorUserId");

            /*Map userInfo = ddClient_contacts.getUserInfoById(ddClient.getAccessToken(), originatorUserId);

            String originatorUserIdUnionid = UtilMap.getString(userInfo, "unionid");*/

            // 获取签署流程进度详情
            Map data = (Map) eqbGet(host + "/esign-signs/v1/signFlow/signDetail?signFlowId=" + signFlowId);
            List<Map> signFiles = UtilMap.getList(data, "signFiles");

            for (Map signFile : signFiles) {
                String fileKey = UtilMap.getString(signFile, "fileKey");
                String signDownloadOuterUrl = UtilMap.getString(signFile, "signDownloadOuterUrl");
                String fileName = getEqbFileName(fileKey);

                int suffixIndex = fileName.lastIndexOf(".");

                String signedFileName = fileName.substring(0, suffixIndex) + "(已签署)" + fileName.substring(suffixIndex);

                //todo 方案1、评论签署后附件下载链接
//                    comment(processInstanceId,userId,"接口测试 "+ "已签署，签署文件下载地址：" + signDownloadOuterUrl,null);



                //方案2、评论签署后附件
                //签署后文件下载到本地
                downloadFile(signDownloadOuterUrl,downloadPath + signedFileName);
                //签署后文件上传到钉盘
                Map dentry = uploadDdFile(spaceId, parentDentryUuid, downloadPath + signedFileName, operatorUnionId);

                //添加权限 OA发起人可下载
                setPermissions(dentry, originatorUserId,"DOWNLOADER");

                //审批单添加评论
                comment(processInstanceId,originatorUserId,fileName + "已签署",Arrays.asList(dentry));
            }

            //自动通过节点
            List<Map> tasks = UtilMap.getList(result, "tasks");
            for (Map task : tasks) {
                String activityId = UtilMap.getString(task, "activityId");
                for (String id : activityIds) {
                    if (id.equals(activityId)){
                        long taskId = UtilMap.getLong(task, "taskId");
                        String userId = UtilMap.getString(task, "userId");

                        Map body = new HashMap();
                        body.put("processInstanceId",processInstanceId);
                        body.put("remark","签署完成自动通过");
                        body.put("result","agree");
                        body.put("actionerUserId",userId);
                        body.put("taskId",taskId);

                        UtilHttp.doPost("https://api.dingtalk.com/v1.0/workflow/processInstances/execute", ddClient.initTokenHeader(), null, body);
                    }
                }

            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void setPermissions(Map dentry, String userId,String roleId) {
        String dentryUuid = dentry.get("uuid").toString();

        //添加权限
        Map<String,Object> param3 = new HashMap<>();
        param3.put("unionId",operatorUnionId);

        List<Map> dentryPermissionList = new ArrayList<>();
        //OWNER: 拥有者   MANAGER: 管理者   EDITOR: 编辑者   DOWNLOADER: 查看下载者   READER: 仅可查看者
        dentryPermissionList.add(new HashMap<String,Object>(){{
            put("type","USER");
            put("id",userId);//发起人userid
        }});

        Map body3 = new HashMap();
        body3.put("roleId",roleId);
        body3.put("members",dentryPermissionList);

        DDR_New ddrNew3 = (DDR_New) UtilHttp.doPost("https://api.dingtalk.com/v2.0/storage/spaces/dentries/" + dentryUuid + "/permissions", ddClient.initTokenHeader(), param3, body3, DDR_New.class);
        if (ddrNew3.isSuccess()){
            log.info("权限设置成功!");
        }else {
            log.error("权限设置失败！" + ddrNew3.getCode() + " " + ddrNew3.getMessage());
        }
    }

    @Override
    public McR getOrganization(String name) {
        if (Strings.isBlank(name)){
            return McR.success(new ArrayList<>());
        }

        Map body = new HashMap();
        body.put("name",name);
        List<Map> data = (List<Map>) eqbPost(host + "manage/v1/innerOrganizations/detail",body);

        List<Map> options = new ArrayList<>();

        for (Map datum : data) {
            String organizationType = UtilMap.getString(datum, "organizationType");

            if ("COMPANY".equals(organizationType)){
                String organizationName = UtilMap.getString(datum, "name");
                String organizationCode = UtilMap.getString(datum, "organizationCode");

                Map option = new HashMap();
                option.put("label",organizationName);
                option.put("value",organizationCode);

                options.add(option);
            }
        }

        //options按label排序
        options.sort(Comparator.comparing(m -> UtilMap.getString(m, "label")));

        return McR.success(options);
    }

    @Override
    public void start(String processInstanceId,String signFlowId) {
        try {
            Map result = ddClient_workflow.getProcessInstanceId(ddClient.getAccessToken(), processInstanceId);
            String originatorUserId = UtilMap.getString(result, "originatorUserId");

            //获取签署地址列表
            Map eqbData = (Map) eqbGet(host + "/esign-signs/v1/signFlow/signUrls?signFlowId=" + signFlowId);

            List<Map> signUrlInfos = UtilMap.getList(eqbData, "signUrlInfos");

            for (Map signUrlInfo : signUrlInfos) {
                String userName = UtilMap.getString(signUrlInfo, "userName");
                String organizationName = UtilMap.getString(signUrlInfo, "organizationName");
                String signUrlShort = UtilMap.getString(signUrlInfo, "signUrlShort");

                //审批单添加评论
                comment(processInstanceId,originatorUserId,"签署人：" + userName + "，签署组织：" + organizationName + "，签署地址：" + signUrlShort,null);
            }

            //自动通过节点
            List<Map> tasks = UtilMap.getList(result, "tasks");
            for (Map task : tasks) {
                String activityId = UtilMap.getString(task, "activityId");
                //e签宝设置签署区节点id
                if (signActivityId.equals(activityId)){
                    long taskId = UtilMap.getLong(task, "taskId");
                    String userId = UtilMap.getString(task, "userId");

                    Map body = new HashMap();
                    body.put("processInstanceId",processInstanceId);
                    body.put("remark","签署区设置完成自动通过");
                    body.put("result","agree");
                    body.put("actionerUserId",userId);
                    body.put("taskId",taskId);

                    UtilHttp.doPost("https://api.dingtalk.com/v1.0/workflow/processInstances/execute", ddClient.initTokenHeader(), null, body);

                    return;
                }
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public McR retrySign(String businessId) {
        // 前12位: 年月日时分
        String datePart = businessId.substring(0, 12);
        // 16-17位: 秒
        String secondPart = businessId.substring(15, 17);

        // 组合完整时间: 年月日时分秒
        String dateTimeStr = datePart + secondPart;
        LocalDateTime localDateTime = LocalDateTime.parse(dateTimeStr, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));

        // 转换为时间戳（毫秒）
        long startTime = localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        long endTime = localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() + 1000;//应对审批编号与实际时间戳存在差异情况

        Map instanceIds = ddClient_workflow.getInstanceIds(ddClient.getAccessToken(), "PROC-7DF36B3B-FBAD-4DF6-B8CD-723FFA8DBEC5", startTime, endTime, null);

        List<String> list = UtilMap.getList(instanceIds, "list");

        if (list.size() == 1){
            String processInstanceId = list.get(0);
            Map map = new HashMap();
            map.put("processInstanceId",processInstanceId);

            return eSignSubmit(map);
        }else {
            return McR.errorUnknown("未找到符合条件的数据");
        }
    }

    //下载钉钉OA审批单附件
    private void downloadDdFile(String processInstanceId,String fileId,String downloadPath){
        try {
            Map body = new HashMap();
            body.put("processInstanceId",processInstanceId);
            body.put("fileId",fileId);

            DDR_New ddrNew = (DDR_New) UtilHttp.doPost("https://api.dingtalk.com/v1.0/workflow/processInstances/spaces/files/urls/download", ddClient.initTokenHeader(), null, body, DDR_New.class);

            //2、执行下载
            Map result = (Map) ddrNew.getResult();

            String downloadUri = UtilMap.getString(result, "downloadUri");

            downloadFile(downloadUri,downloadPath);

        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    //文件下载到本地
    private void downloadFile(String downloadUri,String downloadPath){
        try {
            URL url = new URL(downloadUri);

            HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
            int responseCode = httpConn.getResponseCode();

            // 检查HTTP响应代码是否为200
            if (responseCode == HttpURLConnection.HTTP_OK) {
                InputStream inputStream = httpConn.getInputStream();
                FileOutputStream outputStream = new FileOutputStream(downloadPath);

                byte[] buffer = new byte[4096];
                int bytesRead = -1;

                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }

                outputStream.close();
                inputStream.close();
            } else {
                System.out.println("无法下载文件。HTTP响应代码: " + responseCode);
            }
            httpConn.disconnect();
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    //上传钉盘附件
    private Map uploadDdFile(String spaceId,String parentDentryUuid, String filePath,String unionId) {
        //获取当前时间戳
        long beginTimestamp = System.currentTimeMillis();
        System.out.println("开始上传附件：" + DateUtil.format(new Date(beginTimestamp), "yyyy-MM-dd HH:mm:ss"));

        //文件
        File file = new File(filePath);

        //获取文件上传信息
        Map param = new HashMap();
        param.put("unionId",unionId);
        Map body = new HashMap();
        body.put("protocol","HEADER_SIGNATURE");
        body.put("multipart",false);

        DDR_New ddr = (DDR_New) UtilHttp.doPost("https://api.dingtalk.com/v1.0/storage/spaces/" + spaceId + "/files/uploadInfos/query", ddClient.initTokenHeader(), param, body, DDR_New.class);
        String uploadKey = ddr.getUploadKey();
        Map headerSignatureInfo = ddr.getHeaderSignatureInfo();
        Map<String,String> headers = (Map<String,String>) headerSignatureInfo.get("headers");
        List<String> resourceUrls = (List<String>) headerSignatureInfo.get("resourceUrls");
        String resourceUrl = resourceUrls.get(0);

        Map dentry = new HashMap();
        //使用OSS的header加签方式上传文件
        try {
            URL url = new URL(resourceUrl);
            HttpURLConnection connection = (HttpURLConnection)url.openConnection();
            if (headers != null) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    connection.setRequestProperty(entry.getKey(), entry.getValue());
                }
            }
            connection.setDoOutput(true);
            connection.setRequestMethod("PUT");
            connection.setUseCaches(false);
            connection.setReadTimeout(10000);
            connection.setConnectTimeout(10000);
            connection.connect();
            OutputStream out = connection.getOutputStream();
            InputStream is = new FileInputStream(file);
            byte[] b =new byte[1024];
            int temp;
            while ((temp=is.read(b))!=-1){
                out.write(b,0,temp);
            }
            out.flush();
            out.close();
            int responseCode = connection.getResponseCode();
            connection.disconnect();
            if (responseCode == 200) {
                System.out.println("上传成功");
            } else {
                System.out.println("上传失败");
            }
            //提交文件
            Map body2 = new HashMap();
            Map option = new HashMap();
            option.put("conflictStrategy","OVERWRITE");
            body2.put("uploadKey",uploadKey);
            body2.put("name",file.getName());
            body2.put("option",option);
            DDR_New ddrNew = (DDR_New) UtilHttp.doPost("https://api.dingtalk.com/v2.0/storage/spaces/files/"+parentDentryUuid+"/commit", ddClient.initTokenHeader(), param, body2, DDR_New.class);
            dentry = ddrNew.getDentry();

        }catch (IOException e){
            log.info("上传文件异常:{}",e);
        }

        long endTimestamp = System.currentTimeMillis();
        System.out.println("上传文件结束：" + DateUtil.format(new Date(endTimestamp), "yyyy-MM-dd HH:mm:ss"));
        System.out.println("上传文件耗时：" + (endTimestamp - beginTimestamp)/1000.0 + "s");
        return dentry;
    }

    //钉钉审批单添加评论
    private void comment(String procInstId,String userId,String text,List<Map> dentries){
        Map body = new HashMap<>();
        Map fileMap = new HashMap<>();

        if (Objects.nonNull(dentries)){
            List<Map> attachments= new ArrayList<>();
            for (Map dentry : dentries) {
                Map attachment = new HashMap<>();

                attachment.put("spaceId",dentry.get("spaceId").toString());
                attachment.put("fileSize",dentry.get("size").toString());
                attachment.put("fileId",dentry.get("id").toString());
                attachment.put("fileName",dentry.get("name").toString());
                attachment.put("fileType",dentry.get("type").toString());

                attachments.add(attachment);
            }
            fileMap.put("photos",null);
            fileMap.put("attachments",attachments);
        }

        body.put("processInstanceId",procInstId);
        body.put("text",text);
        body.put("commentUserId",userId);
        body.put("file",fileMap);

        UtilHttp.doPost("https://api.dingtalk.com/v1.0/workflow/processInstances/comments",ddClient.initTokenHeader(),null,body);
    }

    //e签宝-获取文件名称
    private String getEqbFileName(String fileKey){
        Map data = (Map) eqbGet(host + "/file/v1/getFileInfo?fileKey="+fileKey);

        return UtilMap.getString(data,"filename");
    }

    //e签宝-上传附件并转换为pdf
    public String uploadAndConvertEqbFile(String filePath,String fileName) {
        try {
            //获得文件后缀名
            String suffix = fileName.substring(fileName.lastIndexOf(".")+1);

            //1、生成上传文件链接
            JSONObject reqBodyObj = new JSONObject(true);
            reqBodyObj.put("requestID", UUID.randomUUID().toString().replaceAll("-",""));//请求ID，长度：32位，
            reqBodyObj.put("type", 1);//获取上传链接类型  0为同步上传，适用于100M以内的pdf文件。 1为异步上传，适用于大于100M的pdf文件，以及其他类型的文件：ofd、doc、docx、xls、xlsx、jpg、jpeg、png、zip、rar。

            Map data = (Map) eqbPost(host + "file/v1/generateUploadUrl", reqBodyObj);

            String url = UtilMap.getString(data, "url");

            url = url.replace(uploadFileUrlHost,host);


            //2、上传文件到指定链接
            Map<String,Object> body = new HashMap<>();
            body.put("file",fileName);

            Map data2 = eqbUpload(url, body, filePath, fileName);
            String fileKey = UtilMap.getString(data2, "fileKey");

            //3、判断是否为pdf
            if(!"pdf".equals(suffix)){
                //4、文档格式转换
                Map body2 = new HashMap();
                body2.put("fileKey", fileKey);
                body2.put("targetType", "pdf");

                Map map = (Map) eqbPost(host + "file/v1/fileConvert", body2);

                fileKey = UtilMap.getString(map,"convertedFileKey");
            }

            System.out.println("fileKey： " + fileKey);

            return fileKey;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    //e签宝-get请求
    private Object eqbGet(String url){
        try {
            // 构建待签名字符串
            String accept = "*/*";
            String contentType = "application/json; charset=UTF-8";

            // 构建参与请求签名计算的明文
            int index = url.indexOf("?");
            String plaintext = index == -1 ? "" : url.substring(index + 1);
            // 计算请求签名值
            String reqSignature = sign(plaintext, secret);

            // 构建请求头
            LinkedHashMap<String, String> header = new LinkedHashMap<>();
            header.put("X-timevale-project-id", projectId);
            header.put("X-timevale-signature", reqSignature);
            header.put("Accept", accept);
            header.put("Content-Type", contentType);

            // 发送GET请求
            log.info("eqb请求入参,url：{},header：{}",url,header);
            String resultStr = HTTPHelper.sendGet(url, header, "UTF-8");
            log.info("eqb请求响应：{}",resultStr);
            Map result = (Map) JSONObject.parse(resultStr);

            isSuccess(result);

            Object data = result.get("data");

            return data;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    //e签宝-post请求（application/json）
    private Object eqbPost(String url,Object body){
        try {
            // 请求Body体数据
            String reqBodyData = JSONObject.toJSONString(body);

            // 计算请求签名值
            String reqSignature = sign(reqBodyData, secret);

            // 构建请求头
            LinkedHashMap<String, String> header = new LinkedHashMap<>();

            // 构建待签名字符串
            header.put("X-timevale-project-id", projectId);
            header.put("X-timevale-signature", reqSignature);
            header.put("Accept", "*/*");
            header.put("Content-Type", "application/json; charset=UTF-8");

            log.info("eqb请求入参,url：{},reqBodyData：{},header：{}",url,reqBodyData,header);

            String resultStr = HTTPHelper.sendPOST(url, reqBodyData, header, "UTF-8");

            log.info("eqb请求响应：{}",resultStr);

            Map result = (Map) JSONObject.parse(resultStr);

            isSuccess(result);

            Object data = result.get("data");

            return data;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    //e签宝-附件上传
    private Map eqbUpload(String url,Map body,String filePath,String fileName){
        try {
            // 构建请求头
            String reqSignature = sign(JSONObject.toJSONString(body), secret);
            LinkedHashMap<String, String> header = new LinkedHashMap<>();
            // 构建待签名字符串
            header.put("X-timevale-project-id", projectId);
            header.put("X-timevale-signature", reqSignature);
            header.put("Accept", "*/*");

            log.info("eqb请求入参,url：{},filePath：{},body：{},header：{}",url,filePath,body,header);
            String resultStr = HTTPHelper.uploadFile(url,  "file", filePath, body,  header, "UTF-8");
            log.info("eqb请求响应：{}",resultStr);

            Map result = (Map) JSONObject.parse(resultStr);

            isSuccess(result);

            Map data = UtilMap.getMap(result, "data");

            return data;
        }catch (Exception e){
            throw new RuntimeException();
        }
    }


    /**
     * hmac-sha256签名
     *
     * @param content 代签名的内容
     * @param key     签名的key
     * @return 签名
     */
    private String sign(String content, String key) {
        byte[] bytes = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, key).hmac(content);
        return new String(Hex.encodeHex(bytes));
    }

    //判断e签宝接口返回是否成功
    private void isSuccess(Map result){
        int code = UtilMap.getInt(result, "code");

        String message = UtilMap.getString(result, "message");

        if (200 != code || !"成功".equals(message)){
            throw new RuntimeException(message);
        }
    }

}
