Java集成E签宝实现签署

news2025/2/1 12:54:46
完整代码:java-boot-highpin-background: 背调服务 (gitee.com)  【暂不开源】
1.在application.yml中配置appid、密钥信息,包含沙箱环境
    ```java
        esign:
            host: https://smlopenapi.esign.cn
            appId: your appId
            appSecret: your secret
    ```
2.实现电子签的主要流程在BaseAuthInfoServiceImpl里面
    1.根据模板生成word文件(word文件模板在resources里面)

    2.生成好的文件进行上传,上传分两步:具体实现看uploadMFile方法

    3.查询文件上传状态

    4.获取文件坐标

    5.创建签署流程,返回签署流程id
    
    6.最后返回页面签署路径url,返回给前端用于给用户访问签署的页面

3.代码实现:

package io.renren.modules.zhaopin.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import io.renren.common.esign.EsignFileBean;
import io.renren.common.esign.EsignHttpHelper;
import io.renren.common.esign.EsignHttpResponse;
import io.renren.common.esign.enums.EsignHeaderConstant;
import io.renren.common.esign.enums.EsignRequestType;
import io.renren.common.esign.exception.EsignDemoException;
import io.renren.common.utils.*;
import io.renren.modules.zhaopin.dao.BaseAuthInfoDao;
import io.renren.modules.zhaopin.entity.BackgroundCheckOrdersEntity;
import io.renren.modules.zhaopin.entity.BaseAuthInfoEntity;
import io.renren.modules.zhaopin.service.BackgroundCheckOrdersService;
import io.renren.modules.zhaopin.service.BaseAuthInfoService;
import io.renren.modules.zhaopin.util.HTTPHelper;
import io.renren.modules.zhaopin.util.IdWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;

@Slf4j
@Service("baseAuthInfoService")
public class BaseAuthInfoServiceImpl extends ServiceImpl<BaseAuthInfoDao, BaseAuthInfoEntity> implements BaseAuthInfoService {

    @Value("${esign.host}")
    private String host;
    @Value("${esign.appId}")
    private String appId;
    @Value("${esign.appSecret}")
    private String appSecret;
    @Value("${esign.noticeUrl}")
    private String noticeUrl;
    @Autowired
    private BackgroundCheckOrdersService backgroundCheckOrdersService;

    @Autowired
    private BaseAuthInfoService baseAuthInfoService;
    @Autowired
    private IdWorker idWorker;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<BaseAuthInfoEntity> page = this.page(
                new Query<BaseAuthInfoEntity>().getPage(params),
                new QueryWrapper<BaseAuthInfoEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 生成word文件
     *
     * @param reportId 报告id
     */
    private void generateWordFile(String reportId) {
        LambdaQueryWrapper<BackgroundCheckOrdersEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BackgroundCheckOrdersEntity::getReportId, reportId);
        BackgroundCheckOrdersEntity checkOrders = backgroundCheckOrdersService.getOne(queryWrapper);
        try {
            File file = ResourceUtils.getFile("classpath:授权声明.docx");
            Map<String, Object> params = new HashMap<>();
            // 渲染文本
            params.put("company", checkOrders.getOrgName());
            params.put("id_card", checkOrders.getIdCard());
            params.put("date", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")));

            String path = file.getPath();
            String fileDir = path.substring(0, path.lastIndexOf("授"));

            String templatePath = file.getPath(); // "D:\\zdd.docx";
            String fileName = "最终版授权声明";

            String wordPath = GeneratorWordUtils.createWord(templatePath, fileDir, fileName, params);
            System.out.println("生成文档路径:" + wordPath);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 上传本地文件
     *
     * @return
     */
    private Map<String, Object> uploadMFile() {
        String contentType = "application/pdf";

        String postUrl = "/v3/files/file-upload-url";
        String postAllUrl = host + postUrl;
        try {
            File file = ResourceUtils.getFile("classpath:最终版授权声明.docx");
            String path = file.getPath();
            String contentMD5 = ESignUtils.getFileContentMD5(path);
            // 计算签名拼接的url
            // 构建请求Body体
            JSONObject reqBodyObj = new JSONObject();
            reqBodyObj.put("contentMd5", contentMD5);
            reqBodyObj.put("contentType", contentType);
            reqBodyObj.put("convertToPDF", true);
            reqBodyObj.put("fileName", "最终版授权声明.docx");
            reqBodyObj.put("fileSize", file.length() + "");

            Map<String, Object> resultMap = getStringObjectMapPost(reqBodyObj, postUrl, postAllUrl);
            Map<String, Object> dataMap = (Map<String, Object>) resultMap.get("data");
            String fileUploadUrl = (String) dataMap.get("fileUploadUrl");

            // 文件上传
            Gson gson = new Gson();
            EsignHttpResponse uploadFileResponse = ESignUtils.uploadFile(fileUploadUrl, path, contentMD5);
            JSONObject uploadFileResponseJsonObject = gson.fromJson(uploadFileResponse.getBody(), JSONObject.class);
            String code = uploadFileResponseJsonObject.get("errCode").toString();
            System.out.println("文件上传成功,状态码:" + code);

            return resultMap;
        } catch (Exception e) {
            e.printStackTrace();
            String msg = MessageFormat.format("请求签名鉴权方式调用接口出现异常: {0}", e.getMessage());
            System.out.println(msg);
            throw new RuntimeException(e);
        }
    }



    private Map<String, Object> getStringObjectMapPost(JSONObject reqBodyObj, String postUrl, String postAllUrl) throws Exception {
        // 请求Body体数据
        String reqBodyData = reqBodyObj.toString();
        // 对请求Body体内的数据计算ContentMD5
        String contentMD5 = ESignUtils.getBodyContentMD5(reqBodyData);
        System.out.println("请求body数据:" + reqBodyData);
        System.out.println("body的md5值:" + contentMD5);

        // 构建待签名字符串
        String method = "POST";
        String accept = "*/*";
        String contentType = "application/json; charset=UTF-8";
        String date = "";
        String headers = "";

        StringBuffer sb = new StringBuffer();
        sb.append(method).append("\n").append(accept).append("\n").append(contentMD5).append("\n")
                .append(contentType).append("\n").append(date).append("\n");
        if ("".equals(headers)) {
            sb.append(headers).append(postUrl);
        } else {
            sb.append(headers).append("\n").append(postUrl);
        }

        // 构建参与请求签名计算的明文
        String plaintext = sb.toString();
        // 计算请求签名值
        String reqSignature = ESignUtils.doSignatureBase64(plaintext, appSecret);
        System.out.println("计算请求签名值:" + reqSignature);

        // 获取时间戳(精确到毫秒)
        long timeStamp = DateUtils.timeStamp();

        // 构建请求头
        LinkedHashMap<String, String> header = new LinkedHashMap<String, String>();
        header.put("X-Tsign-Open-App-Id", appId);
        header.put("X-Tsign-Open-Auth-Mode", "Signature");
        header.put("X-Tsign-Open-Ca-Timestamp", String.valueOf(timeStamp));
        header.put("Accept", accept);
        header.put("Content-Type", contentType);
        header.put("X-Tsign-Open-Ca-Signature", reqSignature);
        header.put("Content-MD5", contentMD5);
        System.out.println("header" + header);

        String result = HTTPHelper.sendPOST(postAllUrl, reqBodyData, header, "UTF-8");
        JSONObject resultObj = JSONObject.parseObject(result);
        // json转map
        Map<String, Object> resultMap = (Map<String, Object>) JSONObject.toJavaObject(resultObj, Map.class);
        System.out.println("请求返回信息: " + resultObj.toString());
        System.out.println("请求返回信息: " + resultMap.toString());
        return resultMap;
    }

    /**
     * 创建签署流程发送请求API
     *
     * @param reqBodyObj 请求体
     * @param postUrl    url
     * @param postAllUrl 全url
     * @return 返回数据
     * @throws Exception 异常处理
     */
    private Map<String, Object> getCreatePostSign(JSONObject reqBodyObj, String postUrl, String postAllUrl) throws Exception {
        // 请求Body体数据
        String reqBodyData = reqBodyObj.toString();
        // 对请求Body体内的数据计算ContentMD5
        String contentMD5 = ESignUtils.getBodyContentMD5(reqBodyData);
        System.out.println("请求body数据:" + reqBodyData);
        System.out.println("body的md5值:" + contentMD5);

        // 构建待签名字符串
        String method = "POST";
        String accept = "*/*";
        String contentType = "application/json;charset=UTF-8";
        String date = "";
        String headers = "";

        StringBuffer sb = new StringBuffer();
        sb.append(method).append("\n").append(accept).append("\n").append(contentMD5).append("\n")
                .append(contentType).append("\n").append(date).append("\n");
        if ("".equals(headers)) {
            sb.append(headers).append(postUrl);
        } else {
            sb.append(headers).append("\n").append(postUrl);
        }

        // 构建参与请求签名计算的明文
        String plaintext = sb.toString();
        // 计算请求签名值
        String reqSignature = ESignUtils.doSignatureBase64(plaintext, appSecret);
        System.out.println("计算请求签名值:" + reqSignature);

        // 获取时间戳(精确到毫秒)
        long timeStamp = DateUtils.timeStamp();

        // 构建请求头
        LinkedHashMap<String, String> header = new LinkedHashMap<String, String>();
        header.put("X-Tsign-Open-App-Id", appId);
        header.put("X-Tsign-Open-Auth-Mode", "Signature");
        header.put("X-Tsign-Open-Ca-Timestamp", String.valueOf(timeStamp));
        header.put("Accept", accept);
        header.put("Content-Type", contentType);
        header.put("X-Tsign-Open-Ca-Signature", reqSignature);
        header.put("Content-MD5", contentMD5);
        System.out.println("header" + header);

        String result = HTTPHelper.sendPOST(postAllUrl, reqBodyData, header, "UTF-8");
        JSONObject resultObj = JSONObject.parseObject(result);
        // json转map
        Map<String, Object> resultMap = (Map<String, Object>) JSONObject.toJavaObject(resultObj, Map.class);
        int code = (int) resultMap.get("code");
        if (code == 0) {
            Map<String, Object> dataMap = (Map<String, Object>) resultMap.get("data");
            // 签署流程id
            String signFlowId = (String) dataMap.get("signFlowId");
            resultMap.put("signFlowId", signFlowId);
        }
        System.out.println("请求返回信息: " + resultObj.toString());
        return resultMap;
    }

    /**
     * 签署流程id 获取签署页面链接
     *
     * @param signFlowId 签署流程id
     */
    private String getSignUrl(String signFlowId) {
        String postAllUrl = host + "/v3/sign-flow/" + signFlowId + "/sign-url";
        String postUrl = "/v3/sign-flow/" + signFlowId + "/sign-url";

        Map<String, Object> operatorMap = new HashMap<>();
        operatorMap.put("psnAccount", "16619880853");
        JSONObject reqBodyObj = new JSONObject();
        reqBodyObj.put("clientType", "ALL");
        reqBodyObj.put("needLogin", false);
        reqBodyObj.put("urlType", 2);
        reqBodyObj.put("operator", operatorMap);
        Map<String, Object> redirectConfigMap = new HashMap<>();
        redirectConfigMap.put("redirectUrl", noticeUrl);
        reqBodyObj.put("redirectConfig", redirectConfigMap);
        System.out.println("reqBodyObj" + reqBodyObj);
        try {
            Map<String, Object> map = getStringObjectMapPost(reqBodyObj, postUrl, postAllUrl);
            System.out.println(map);
            int code = (int) map.get("code");
            if (code == 0) {
                Map<String, Object> dataMap = (Map<String, Object>) map.get("data");
                String shortUrl = (String) dataMap.get("shortUrl");
                return shortUrl;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 查询文件上传状态
     *
     * @param fileId 报告id
     * @return
     */
    private Integer getFileStatus(String fileId) throws Exception {
        String getAllUrl = host + "/v3/files/" + fileId;
        String postUrl = "/v3/files/" + fileId;
        JSONObject reqBodyObj = new JSONObject();
        reqBodyObj.put("fileId", fileId);

        // GET请求时ContentMD5为""
        String contentMD5 = "";

        // 构建待签名字符串
        String method = "GET";
        String accept = "*/*";
        String contentType = "application/json; charset=UTF-8";
        String date = "";
        String headers = "";

        StringBuffer sb = new StringBuffer();
        sb.append(method).append("\n").append(accept).append("\n").append(contentMD5).append("\n")
                .append(contentType).append("\n").append(date).append("\n");
        if ("".equals(headers)) {
            sb.append(headers).append(postUrl);
        } else {
            sb.append(headers).append("\n").append(postUrl);
        }

        // 构建参与请求签名计算的明文
        String plaintext = sb.toString();
        // 计算请求签名值
        String reqSignature = ESignUtils.doSignatureBase64(plaintext, appSecret);
        System.out.println("计算请求签名值:" + reqSignature);
        // 获取时间戳(精确到毫秒)
        long timeStamp = DateUtils.timeStamp();
        // 构建请求头
        LinkedHashMap<String, String> header = new LinkedHashMap<String, String>();
        header.put("X-Tsign-Open-App-Id", appId);
        header.put("X-Tsign-Open-Auth-Mode", "Signature");
        header.put("X-Tsign-Open-Ca-Timestamp", String.valueOf(timeStamp));
        header.put("Accept", accept);
        header.put("Content-Type", contentType);
        header.put("X-Tsign-Open-Ca-Signature", reqSignature);
        header.put("Content-MD5", contentMD5);

        HashMap<String, Object> query = new HashMap<String, Object>();
        // 发送POST请求
        String result = HTTPHelper.sendGet(getAllUrl, query, header, "UTF-8");
        JSONObject resultObj = JSONObject.parseObject(result);
        System.out.println("请求返回信息: " + resultObj.toString());
        Map<String, Object> resultMap = (Map<String, Object>) JSONObject.toJavaObject(resultObj, Map.class);
        System.out.println(resultMap);
        return (Integer) resultMap.get("code");
    }

    /**
     * 获取文件坐标
     */
    private Map<String, Object> getPosition(String fileId) {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        String postUrl = "/v3/files/" + fileId + "/keyword-positions";
        String postAllUrl = host + postUrl;

        JSONObject reqBodyObj = new JSONObject();
        reqBodyObj.put("fileId", fileId);
        List<String> keywords = new ArrayList<>();
        keywords.add("本人签名");
        reqBodyObj.put("keywords", keywords);
        try {
            Map<String, Object> map = getStringObjectMapPost(reqBodyObj, postUrl, postAllUrl);
            System.out.println(map);
            Map<String, Object> dataMap = (Map<String, Object>) map.get("data");
            if (dataMap == null) {
                throw new RuntimeException("解析失败");
            }
            List keywordPositions = (List) dataMap.get("keywordPositions");
            Map<String, Object> objectMap = (Map<String, Object>) keywordPositions.get(0);
            List positions = (List) objectMap.get("positions");
            Map<String, Object> positionMap = (Map<String, Object>) positions.get(0);
            List coordinates = (List) positionMap.get("coordinates");
            Integer pageNums = (Integer) positionMap.get("pageNum");
            Map<String, Object> position = (Map<String, Object>) coordinates.get(0);
            position.put("pageNums", pageNums);
            return position;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 签署
     *
     * @param reportId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R sign(String reportId) {
        try {
            String id = idWorker.nextId() + "";
            generateWordFile(reportId);
            Map<String, Object> resultMap = uploadMFile();
            Map<String, Object> dataMap = (Map<String, Object>) resultMap.get("data");
            String fileId = (String) dataMap.get("fileId");
            String fileUploadUrl = (String) dataMap.get("fileUploadUrl");
            System.out.println(resultMap);
            /*
              1. 查看所上传文件的当前状态(转换pdf/html文件状态)文件名称和下载链接。
              2. 当返回的文件状态status值为 2 或 5 时,此文件才可以被应用到签署流程中。
             */
            Integer fileStatus = getFileStatus(fileId);
            if (fileStatus == 0) {
                Map<String, Object> position = getPosition(fileId);
                BigDecimal positionY = (BigDecimal) position.get("positionY");
                BigDecimal positionX = (BigDecimal) position.get("positionX");
                Integer pageNums = (Integer) position.get("pageNums");
                Map<String, Object> sign = createSign(fileId, reportId, positionX, positionY, pageNums);
                String signUrl = (String) sign.get("signUrl");
                String signFlowId = (String) sign.get("signFlowId");
                // 保存到数据库 fileUploadId
                BaseAuthInfoEntity baseAuthInfoEntity = new BaseAuthInfoEntity();
                baseAuthInfoEntity.setId(id);
                baseAuthInfoEntity.setOrderId(reportId);
                baseAuthInfoEntity.setAuthFlowId(signFlowId);
                baseAuthInfoEntity.setAuthUrl(signUrl);
                baseAuthInfoEntity.setCreateTime(LocalDateTime.now());
                baseAuthInfoEntity.setUpdateTime(LocalDateTime.now());
                baseAuthInfoEntity.setFileId(fileId);
                baseAuthInfoEntity.setFileUploadUrl(fileUploadUrl);
                baseAuthInfoService.save(baseAuthInfoEntity);
                return R.ok(sign);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return R.ok("签署流程失败");
    }

    /**
     * 创建签署流程
     */
    public Map<String, Object> createSign(String fileId, String reportId, BigDecimal positionX, BigDecimal positionY, Integer pageNums) {
        LambdaQueryWrapper<BackgroundCheckOrdersEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BackgroundCheckOrdersEntity::getReportId, reportId);
        BackgroundCheckOrdersEntity checkOrders = backgroundCheckOrdersService.getOne(queryWrapper);

        Map<String, Object> paramsMap = new HashMap<>();

        Map<String, Object> docsMap = new HashMap<>();
        // 待签署文件ID
        docsMap.put("fileName", "最终版授权声明.docx");
        docsMap.put("fileId", fileId);

        Map<String, Object> signFlowConfigMap = new HashMap<>();
        signFlowConfigMap.put("signFlowTitle", "企业合同签署");
        signFlowConfigMap.put("notifyUrl", "www.baidu.com");
        Map<String, Object> redirectConfigMap = new HashMap<>();
        redirectConfigMap.put("redirectUrl", "www.baidu.com");
        signFlowConfigMap.put("redirectConfig", redirectConfigMap);
        signFlowConfigMap.put("autoFinish", true);

        Map<String, Object> signersMap = new HashMap<>();
        Map<String, Object> signConfigMap = new HashMap<>();
        signConfigMap.put("forcedReadingTime", 5);
        /*
            签署方类型,0 - 个人,1 - 企业/机构,2 - 法定代表人,3 - 经办人
            若指定签署方为个人,则psnSignerInfo为必传项;
            若指定签署方为机构或法定代表人手动签署(autoSign参数为false)时,则orgSignerInfo为必传项;
            若指定签署方为经办人,在同级数组内必须还有机构类型存在,且orgSignerInfo为必传项,即:指定3 - 经办人签的前提是必须同时存在1 - 企业/机构,且经办人签属于企业合同,不在个人名下。
         */
        signersMap.put("signerType", 0);
        signersMap.put("signConfig", signConfigMap);

        Map<String, Object> psnSignerInfoMap = new HashMap<>();
        // 企业/机构名称(账号标识)
        psnSignerInfoMap.put("psnAccount", checkOrders.getHrTel());
        signersMap.put("psnSignerInfo", psnSignerInfoMap);

        List<Object> docsList = new ArrayList<>();
        docsList.add(docsMap);
        paramsMap.put("docs", docsList);
        paramsMap.put("signFlowConfig", signFlowConfigMap);
        List<Object> signersList = new ArrayList<>();
        signersList.add(signersMap);
        // signersList.add(psnSignerInfoMap);
        // paramsMap.put("signers", signersList);

        List<Object> signFieldsList = new ArrayList<>();

        Map<String, Object> signFieldsMap = new HashMap<>();
        signFieldsMap.put("fileId", fileId);
        signFieldsMap.put("customBizNum", idWorker.nextId() + "");

        Map<String, Object> normalSignFieldConfigMap = new HashMap<>();
        // 1 - 单页签章,2 - 骑缝签章
        normalSignFieldConfigMap.put("signFieldStyle", 1);
        normalSignFieldConfigMap.put("freeMode", false);
        normalSignFieldConfigMap.put("autoSign", false);
        Map<String, Object> signFieldPositionMap = new HashMap<>();
        signFieldPositionMap.put("positionX", positionX);
        signFieldPositionMap.put("positionY", positionY);
        signFieldPositionMap.put("positionPage", pageNums);

        signFieldsMap.put("normalSignFieldConfig", normalSignFieldConfigMap);
        normalSignFieldConfigMap.put("signFieldPosition", signFieldPositionMap);

        signFieldsList.add(signFieldsMap);
        signersMap.put("signFields", signFieldsList);

        String postAllUrl = host + "/v3/sign-flow/create-by-file";
        String postUrl = "/v3/sign-flow/create-by-file";
        JSONObject reqBodyObj = new JSONObject();
        reqBodyObj.put("docs", docsList);
        reqBodyObj.put("signFlowConfig", signFlowConfigMap);
        reqBodyObj.put("signers", signersList);
        System.out.println("reqBodyObj:" + reqBodyObj);
        System.out.println("paramsMap:" + paramsMap);
        try {
            Map<String, Object> map = getCreatePostSign(reqBodyObj, postUrl, postAllUrl);
            String signFlowId = (String) map.get("signFlowId");
            // 签署流程id 获取签署页面链接
            String signUrl = getSignUrl(signFlowId);
            map.put("signUrl", signUrl);
            map.put("signFlowId", signFlowId);
            return map;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

工具类代码比较多,就不全部写出来了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1555545.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

主干网络篇 | YOLOv8更换主干网络之EfficientNet

前言:Hello大家好,我是小哥谈。EfficientNet是一种高效的卷积神经网络架构,由Mingxing Tan和Quoc V. Le在2019年提出,其设计思想是在不增加计算复杂度的情况下提高模型的准确性。它引入了一个称为"复合系数"的概念,该系数用于同时缩放网络的深度、宽度和分辨率。…

C++类继承基础2——虚函数和纯虚函数

虚函数 如前所述&#xff0c;在C语言中&#xff0c;当我们使用基类的引用或指针调用一个虚成员函数时会执行动态绑定。 因为我们直到运行时才能知道到底调用了哪个版本的虚函数&#xff0c;所以所有虚函数都必须有定义。 通常情况下&#xff0c;如果我们不使用某个函数&…

C++:继承的介绍和深度解析

一、继承的概念和定义 1.什么是继承&#xff1f; 继承&#xff0c;顾名思义&#xff1a;就和现实生活中&#xff0c;孩子继承父母的东西有点类似。比如&#xff0c;你父亲的财产&#xff0c;你可以继承下来&#xff0c;你就可以使用父亲的钱。 官方一点的介绍&#xff1a; 继承…

代码随想录阅读笔记-二叉树【对称二叉树】

题目 给定一个二叉树&#xff0c;检查它是否是镜像对称的。 思路 首先想清楚&#xff0c;判断对称二叉树要比较的是哪两个节点&#xff0c;要比较的可不是左右节点&#xff01; 对于二叉树是否对称&#xff0c;要比较的是根节点的左子树与右子树是不是相互翻转的&#xff0…

2024 ccfcsp认证打卡 2021 12 01 序列查询

2021 12-1 序列查询 题解1题解2区别第一种算法&#xff1a;第二种算法&#xff1a; 题解1 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);// 输入n表示商品数目&#xff0c;N表示总数int n sc.n…

使用Vite安装TailwindCSS

一、认识TailwindCSS Tailwind CSS 是一个基于原子类的 CSS 框架&#xff0c;它提供了一种不同于传统 CSS 框架的方式来构建用户界面。下面是关于 Tailwind CSS 的优缺点以及它适合应用的情况&#xff1a; 优点&#xff1a; 灵活性&#xff1a; Tailwind CSS 提供了大量的原…

Portal Particle

Unity3D Portal Particle 2.2传送门粒子效果 链接&#xff1a;https://pan.baidu.com/s/1TCMXIif5d288lXHgixnDPw?pwd1234 下载&#xff1a;资源下载链接 效果图&#xff1a;

Java虚拟机(JVM)知识点总结

一. Java内存区域 1. JVM的内存区域划分&#xff0c;以及各部分的作用 可分为运行时数据区域和本地内存&#xff0c;按照线程私有和线程共享分类&#xff1a; 线程私有&#xff1a;程序计数器、虚拟机栈、本地方法栈。 线程共享&#xff1a;堆、方法区、直接内存。 JDK1.7…

V R虚拟现实元宇宙的前景|虚拟现实体验店加 盟合作|V R设备在线购买

VR&#xff08;虚拟现实&#xff09;技术作为一种新兴的技术&#xff0c;正在逐渐改变人们的生活和工作方式。随着技术的不断进步&#xff0c;人们对于元宇宙的概念也越来越感兴趣。元宇宙是一个虚拟世界&#xff0c;通过VR技术可以实现人们在其中进行各种活动和交互。 元宇宙的…

(C++17) std算法之执行策略 execution

文章目录 前言Code测试Code运行效果 msvc源码描述源码std::sequenced_policy seqstd::parallel_policy parstd::parallel_unsequenced_policy par_unseqstd::unsequenced_policy unseq END 前言 ref:算法库-执行策略 - cppreference.com 利用多核cpu加速算法在目前看来已经不是…

Springboot+MybatisPlus+EasyExcel实现文件导入数据

记录一下写Excel文件导入数据所经历的问题。 springboot提供的文件处理MultipartFile有关方法&#xff0c;我没有具体看文档&#xff0c;但目测比较复杂&#xff0c; 遂了解学习了一下别的文件上传方法&#xff0c;本文第1节记录的是springboot原始的导入文件方法写法&#xf…

论文阅读-《Lite Pose: Efficient Architecture Design for 2D Human Pose Estimation》

摘要 这篇论文主要研究了2D人体姿态估计的高效架构设计。姿态估计在以人为中心的视觉应用中发挥着关键作用&#xff0c;但由于基于HRNet的先进姿态估计模型计算成本高昂&#xff08;每帧超过150 GMACs&#xff09;&#xff0c;难以在资源受限的边缘设备上部署。因此&#xff0…

vue的创建、启动以及目录结构详解

vue的创建、启动以及目录结构详解目录 一. vue项目的创建 二. vue的目录结构 三. src的目录结构 四. vue项目的启动 4.1 方法1 4.2 方法2 一. vue项目的创建 创建一个工程化的Vue项目&#xff0c;执行命令&#xff1a;npm init vuelatest 注意&#xff1a;如果你在这个目…

【Python基础教程】3 . 算法的时间复杂度

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;python基础教程 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、…

[每周一更]-第91期:认识AMD的CPU

这一章来认识下AMD,说起AMD&#xff0c;从我上大学那时候&#xff0c;就选购的AMD 速龙系列&#xff0c;生活拮据的情况下&#xff0c;拉着室友去郑州电子城&#xff0c;在跟奸商老板的拉扯下&#xff0c;斥资2000购入一个无显卡 的台式机&#xff08;实在是资金有限&#xff0…

【Spring源码】WebSocket做推送动作的底层实例

一、前瞻 Ok&#xff0c;开始我们今天的对Spring的【模块阅读】。 那就挑Web里的WebSocket模块&#xff0c;先思考下本次阅读的阅读线索&#xff1a; WebSocket在Spring里起到什么作用这个模块采用了什么设计模式我们都知道WebSocket可以主动推送消息给用户&#xff0c;那做推…

小程序中使用less

在vscode中安装插件 找到左下角齿轮的设置&#xff0c;点击右边图标&#xff0c;进入“settings.json” 加上以下代码配置 "less.compile":{"outExt": ".wxss"}

在.Net6中用gdal实现第一个功能

目录 一、创建.NET6的控制台应用程序 二、加载Gdal插件 三、编写程序 一、创建.NET6的控制台应用程序 二、加载Gdal插件 Gdal的资源可以经过NuGet包引入。右键单击项目名称&#xff0c;然后选择 "Manage NuGet Packages"&#xff08;管理 NuGet 包&#xff09;。N…

揭秘情绪识别:如何让AI读懂你的心声?

最近我在研究大语言模型&#xff0c;想用它来给样本打分。 起初&#xff0c;我尝试让模型用1到5分来评分&#xff0c;但它总是极端地给出最低分或最高分&#xff0c;评分缺乏中间地带。 于是我换了个方法&#xff0c;不再用数字&#xff0c;而是用描述性的词语&#xff0c;比…

《让你的时间多一倍》逃离时间陷阱,你没有自己想的那么懒 - 三余书屋 3ysw.net

让你的时间多一倍 今天我们来阅读法比安奥利卡尔的作品《让你的时间多一倍》。或许你会心生疑虑&#xff0c;这本书是否又是一本沉闷的时间管理指南&#xff1f;但我要告诉你的是&#xff0c;尽管时间管理这个话题已经为大众所熟知&#xff0c;这本书却为我们揭示了一个全新的…