dy对接接口获取数据

news2025/6/18 0:24:24

1.需求方:

1.已知账户,获取该账户下每天的发视频数据,同时获取一个视频连续30天的点赞数,分享数,评论数。
2.需求方确定在标题中附带来源和作者相关信息,从标题中提取该部分信息,作为原创和作者绩效考核。

2.实现方:

1.在douyin开放平台中注册,并绑定该抖音账户,获取APPID和APP_SECRET。

3.实现过程:

3.1 授权步骤一 通过用户授权获取授权码code

在这里插入图片描述

该接口只适用于抖音获取授权临时票据(code)。

注意:

抖音的 OAuth API 以https://open.douyin.com/开头。 该 URL 不是用来请求的,
需要展示给用户用于扫码,在抖音 APP 支持端内唤醒的版本内打开的话会弹出客户端原生授权页面。 获取的 code
可以用来调用 oauth/access_token/ 换取用户 acccess_token。
若需要授权多个 scope 需要把多个 scope 使用英文 “,” 拼接,例如 scope1,scope2,scope3 。
使用本接口前提:

首先你需要去官网申请,使你的应用可以使用特定的 Scope,具体需要哪些 Scope,请查看各接口定义。 其次你需要在本 URL 的
scope 字段中填上用户需要授权给你的 Scope。 用户授权通过后,你才可以调用相应的接口。

       /**
     * 抖音授权
     *
     * @param request
     * @param response
     */
    @GetMapping("/videoConfig/douYin/getCode")
    @ResponseBody
    public R getDouYinCode(HttpServletRequest request, HttpServletResponse response) {
        VideoConfig dyInfo = videoConfigMapper.selectByType("dy");

        if (dyInfo == null) {
            logger.error("=============无抖音平台数据==============");
            throw new RuntimeException("无抖音平台数据");
        }
        // https://open.douyin.com/platform/oauth/connect/?client_key=xxxxxxx&response_type=code&scope=video.list&redirect_uri=https://baidu.com/login
        //需要用户开放的权限
        String scope = "trial.whitelist,video.list,renew_refresh_token";
        //回调地址 https://baidu.com/login 为临时回调地址的,正式上线要用线上的域名
        //VIDEO_AUTH_CALLBACK_URL 为网站的回调域名
        String redirect_uri = VIDEO_AUTH_CALLBACK_URL + "/douYin/authCallback";
        String requestUrl = "https://open.douyin.com/platform/oauth/connect/?client_key=" + dyInfo.getAppId()
                + "&response_type=code" + "&scope=" + scope + "&redirect_uri=" + redirect_uri;
        return R.ok().data("url", requestUrl);

    }
    }

3.2 授权步骤二 获取access_token

该接口用于获取用户授权第三方接口调用的凭证 access_token;该接口适用于抖音/头条授权。

注意:

抖音的 OAuth API 以https://open.douyin.com/开头。 头条的 OAuth API
以https://open.snssdk.com/开头。 西瓜的 OAuth API
以https://open-api.ixigua.com/开头。 access_token
为用户授权第三方接口调用的凭证,存储在客户端,可能会被窃取,泄漏后可能会发生用户隐私数据泄漏的风险,建议存储在服务端。 获取到
access_token 后授权临时票据 (code) 不要再授权刷新,否则会导致上一次获取的 code 过期。

上面/videoConfig/douYin/getCode请求将回调下面请求地址,并在request中返回code,然后请求获取token,并记录access_token,refresh_token ,expires_in,refresh_expires_in,open_id

 @GetMapping("/douYin/authCallback")
    @ResponseBody
    public String dyAuthCallback(HttpServletRequest request) throws ParseException {
        // 请求参数
        VideoConfig dyInfo = videoConfigMapper.selectByType("dy");
        //请求参数
        String code = request.getParameter("code");
//        String code = "xxxxxxxxxxxxxxxxxx";
        Map<String, String> map = new HashMap<>();
        map.put("client_key", dyInfo.getAppId());
        map.put("client_secret", dyInfo.getAppSecret());
        map.put("code", code);
        map.put("grant_type", "authorization_code");

        //请求地址
        String url = dyInfo.getGetTokenUrl();
        HttpRequest httpRequest = HttpRequest.get(url, map, Boolean.TRUE);
        String result = httpRequest.body();
        JSONObject jasonObject = JSONObject.parseObject(result);
        JSONObject data = (JSONObject) jasonObject.get("data");
        System.out.println("jasonObject = " + jasonObject);
        int errorCode = Integer.parseInt(data.get("error_code").toString());
        if (errorCode == 0) {
            //查询数据库里面的内容
            String open_id = data.get("open_id").toString();
            String access_token = data.get("access_token").toString();
            String refresh_token = data.get("refresh_token").toString();
            //记录token,将他的过期时间也记录下来,查询时过期,就去更新
            long expires_in = Long.parseLong(data.get("expires_in").toString());
            long refresh_expires_in = Long.parseLong(data.get("refresh_expires_in").toString());
            long currentTimeMillis = System.currentTimeMillis();
            long tokenExpiresInLong = currentTimeMillis + expires_in * 1000;
            long refreshExpiresInLong = currentTimeMillis + refresh_expires_in * 1000;
            String tokenExpiresIn = TimeUtil.transferLongToDate("yyyy-MM-dd HH:mm:ss", tokenExpiresInLong);
            String refreshExpiresIn = TimeUtil.transferLongToDate("yyyy-MM-dd HH:mm:ss", refreshExpiresInLong);
            dyInfo.setOpenId(open_id);
            dyInfo.setToken(access_token);
            dyInfo.setRefreshToken(refresh_token);
            dyInfo.setTokenExpiresIn(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(tokenExpiresIn));
            dyInfo.setRefreshExpiresIn(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(refreshExpiresIn));
            videoConfigMapper.updateByPrimaryKeySelective(dyInfo);
            logger.info("accessToken记录成功");
            return "授权成功,请关闭当前窗口!";
        }
        return "授权失败,请联系管理人员";
    }

3.3 授权步骤三 刷新access_token

 /**
     * 刷新 access_token 的有效期
     */
    private void refresh_token() throws ParseException {
        logger.info("=================抖音刷新 access_token 的有效期=================");
        VideoConfig dyInfo = videoConfigMapper.selectByType("dy");
        String client_key = dyInfo.getAppId();
        Map<String, String> map = new HashMap<>();
        map.put("client_key", client_key);
        map.put("grant_type", "refresh_token");
        map.put("refresh_token", dyInfo.getRefreshToken());
        //请求地址
        String url = dyInfo.getGetRefreshTokenUrl();
        HttpRequest httpRequest = HttpRequest.post(url, map, Boolean.TRUE).header("Content-Type", "multipart/form-data");
        String result = httpRequest.body();
        JSONObject jasonObject = JSONObject.parseObject(result);
        JSONObject data = (JSONObject) jasonObject.get("data");
        logger.info("=================抖音刷新刷新access_token 的有效期:{}=================", jasonObject);

        String errorCode = data.get("error_code").toString();
        if (errorCode.equals("10008") || errorCode.equals("2190008")) {
            //这表示access_token过期 需要重新获取refresh_token 后会获取一个新的 access_token 以及新的超时时间。
            //说明要刷新重新获取refresh_token
            logger.info("=================抖音刷新刷新重新获取refresh_token=================");
            renew_refresh_token();
        } else {
            //未过期,提取并记录刷新后的access_token
            if (errorCode.equals("0")) {
                logger.info("=================抖音 未过期,提取并记录刷新后的access_token=================");
                String open_id = data.get("open_id").toString();
                String access_token = data.get("access_token").toString();
                //记录token,将他的过期时间也记录下来,查询时过期,就去更新
                long expires_in = Long.parseLong(data.get("expires_in").toString());
                long refresh_expires_in = Long.parseLong(data.get("refresh_expires_in").toString());
                long currentTimeMillis = System.currentTimeMillis();

                long tokenExpiresInLong = currentTimeMillis + expires_in * 1000;
                long refreshExpiresInLong = currentTimeMillis + refresh_expires_in * 1000;

                String tokenExpiresIn = TimeUtil.transferLongToDate("yyyy-MM-dd HH:mm:ss", tokenExpiresInLong);
                String refreshExpiresIn = TimeUtil.transferLongToDate("yyyy-MM-dd HH:mm:ss", refreshExpiresInLong);
                dyInfo.setOpenId(open_id);
                dyInfo.setToken(access_token);
                dyInfo.setRefreshToken(dyInfo.getRefreshToken());
                dyInfo.setTokenExpiresIn(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(tokenExpiresIn));
                dyInfo.setRefreshExpiresIn(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(refreshExpiresIn));
                videoConfigMapper.updateByPrimaryKeySelective(dyInfo);
                logger.info("accessToken记录成功");

            }
        }
        // 提取并记录刷新后的access_token 获取成功后再执行一次获取视频列表数据
        // 如果access_token刷新后则进行renew_refresh_token操作
    }

3.4 授权步骤四 刷新refresh_token

 /**
     * 该接口用于刷新refresh_token的有效期
     */
    private void renew_refresh_token() throws ParseException {
        VideoConfig dyInfo = videoConfigMapper.selectByType("dy");
        String client_key = dyInfo.getAppId();
        Map<String, String> map = new HashMap<>();
        map.put("client_key", client_key);
        map.put("refresh_token", dyInfo.getRefreshToken());
        //请求地址
        String url = dyInfo.getGetRenewRefreshTokenUrl();
        HttpRequest httpRequest = HttpRequest.post(url, map, Boolean.TRUE).header("Content-Type", "multipart/form-data");
        String result = httpRequest.body();
        logger.info("=================刷新refresh_token的有效期,数据获取result:{}=================", result);

        JSONObject jasonObject = JSONObject.parseObject(result);
        JSONObject data = (JSONObject) jasonObject.get("data");
        logger.info("=================刷新refresh_token的有效期,数据获取jasonObject:{}=================", jasonObject);

        String errorCode = data.get("error_code").toString();
        //未过期,提取并记录刷新后的access_token
        if (errorCode.equals("0")) {
            //过期时间 30天
            long expires_in = Long.parseLong(data.get("expires_in").toString());
            String refresh_token = data.get("refresh_token").toString();

            long currentTimeMillis = System.currentTimeMillis();
            System.out.println("currentTimeMillis = " + currentTimeMillis);
            long refreshExpiresInLong = currentTimeMillis + expires_in * 1000;
            String refreshExpiresIn = TimeUtil.transferLongToDate("yyyy-MM-dd HH:mm:ss", refreshExpiresInLong);

            dyInfo.setRefreshToken(refresh_token);
            dyInfo.setRefreshExpiresIn(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(refreshExpiresIn));
            videoConfigMapper.updateByPrimaryKeySelective(dyInfo);
            logger.info("刷新refresh_token记录成功");
        }else{
			//TODO 给管理员发一个短信需要他认证。
		}
    }

3.5 获取视频列表

在这里插入图片描述

1.执行一个定时器任务,进行数据抓取,并记录数据库中。
2.抖音每次只支持十条数据返回,每次不能全部抓取,分批次读取数据,找到30天内的数据,30天内的数据是需要更新总点击数和总分享数等。
3.列表是有规律的,除指定的几个其余是按照当前时间逆序排序的。
作者可能有多个人,因此每个数据插入前要进行人员匹配哦

 /**
     * 获取视频列表并插入数据库    每天凌晨2点30分抓取
     */
    @Scheduled(cron = "${dy.task.cron}")
    public void getVideoList() throws ParseException {
        /**
         * open_id = 
         * access_token = 
         */
        VideoConfig dyInfo = videoConfigMapper.selectByType("dy");

        if (dyInfo == null) {
            throw new RuntimeException("无数据抖音平台数据");
        }
		//检查是否需要刷新access_token
        if (dyInfo.getTokenExpiresIn().getTime() < System.currentTimeMillis()) {
            //为空则说明需要去刷新 获取老版本然后更新
            refresh_token();
            //刷新后再次获取最新的access_token相关信息
            dyInfo = videoConfigMapper.selectByType("dy");
        }

        //获取列表
        String has_more = null;
        String cursor = "0";
        int count = 0;
        JSONArray jsonArray = new JSONArray();
        do {

            JSONObject list10 = getList10(dyInfo, cursor);
            if (list10 == null) return;
            has_more = list10.get("has_more").toString();
            cursor = list10.get("cursor").toString();
            JSONArray list = (JSONArray) list10.get("list");
            for (int i = 0; i < list.size(); i++) {
                JSONObject json = (JSONObject) list.get(i);
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                String date = json.get("create_time").toString();
                String createTime = sdf.format(new Date(Long.parseLong(String.valueOf(Long.parseLong(date) * 1000))));    //时间戳转化成时间

                boolean flag = TimeUtil.isPastNDays(createTime, 30);
                if (!flag) {
                    //不在该时间范围就移除数据.
                    list.remove(i);
                    System.out.println("移除时间为:" + TimeUtil.TimeStampToTime(date));
                    System.out.println("移除的标题为:" + json.get("title").toString());
                    //动态调整结束请求页数,当从第二页开始,如果有连续移除时,说明后面的页数据已经不是要取的了.
                    if (count >= 2) {
                        has_more = "false";
                    }
                }
            }
            jsonArray.addAll(list);

            //标记页数
            count++;

        } while (has_more.equals("true"));

        logger.info("本次翻页{}次,共采集到有效数据{}条", count, jsonArray.size());
        dataDeal(jsonArray);
        logger.info("=================抖音数据采集结束=================");
    }
 //数据处理过程
    private void dataDeal(JSONArray jsonArray) throws ParseException {
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            //视频创建时间戳
            String date = jsonObject.get("create_time").toString();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            String createTime = sdf.format(new Date(Long.parseLong(String.valueOf(Long.parseLong(date) * 1000))));    //时间戳转化成时间
            //网页链接
            String share_url = jsonObject.get("share_url").toString();
            //标题
            String title = jsonObject.get("title").toString();

            String layout = "";
            String authors = "";
            String editor = "";
            String editor2 = "";
            String editor3 = "";
            String editor4 = "";

            if (title.indexOf("来源:") > 0||title.indexOf("来源:") > 0) { //判断是否有这个
                String str1 = "";
                if(title.indexOf("来源:")>0){
                    str1 = title.substring(0, title.indexOf("来源:"));
                }else{
                    str1 = title.substring(0, title.indexOf("来源:"));
                }
                //获取作者相关信息,处理格式问题
                String contactInfo = title.substring(str1.length()).replace(" ", "").replace("\n", "");
                logger.info("=================抖音数据获取contactInfo:{}=================", contactInfo);
                Pattern from = Pattern.compile("来源:.+作者|编辑");
                Matcher fromM = from.matcher(contactInfo);

                while (fromM.find()) {
                    //获取作者信息然后去匹配字段
                    layout = contactInfo.substring(fromM.start(), fromM.end()).replace("来源:", "").replace("编辑", "");
                    if (layout.contains("xxxxx")) { //xxxx表示来来源后的一段文字
                        //自己的媒体有作者信息
                        // 以 编辑:开头,以 编辑: 结束的字符串
                        Pattern p = Pattern.compile("作者:.+编辑:");
                        Matcher m = p.matcher(contactInfo);
                        while (m.find()) {
                            //获取作者信息然后去匹配字段
                            authors = contactInfo.substring(m.start(), m.end()).replace("作者:", "").replace("编辑", "");
                            logger.info("作者:" + authors);
                        }
                    }
                }
                //获取编辑
                Pattern ed = Pattern.compile("编辑:.+责编");
                Matcher em = ed.matcher(contactInfo);
                while (em.find()) {
                    //获取作者信息然后去匹配字段
                    editor = contactInfo.substring(em.start(), em.end()).replace("编辑:", "").replace("责编", "");
                    logger.info("编辑:" + editor);
                }
                //获取责编
                Pattern ed2 = Pattern.compile("责编:.+编审");
                Matcher em2 = ed2.matcher(contactInfo);

                while (em2.find()) {
                    //获取作者信息然后去匹配字段
                    editor2 = contactInfo.substring(em2.start(), em2.end()).replace("责编:", "").replace("编审", "");
                    logger.info("责编:" + editor2);
                }
                //获取编审
                Pattern ed3 = Pattern.compile("编审:.+监制");
                Matcher em3 = ed3.matcher(contactInfo);

                while (em3.find()) {
                    //获取作者信息然后去匹配字段
                    editor3 = contactInfo.substring(em3.start(), em3.end()).replace("编审:", "").replace("监制", "");
                    logger.info("编审:" + editor3);
                }
                //获取监制
                Pattern ed4 = Pattern.compile("监制:.+");
                Matcher em4 = ed4.matcher(contactInfo);

                while (em4.find()) {
                    //获取监制信息然后去匹配字段
                    editor4 = contactInfo.substring(em4.start(), em4.end()).replace("监制:", "");
                    logger.info("监制:" + editor4);
                }
            }

            //统计数据
            JSONObject statistics = (JSONObject) jsonObject.get("statistics");            //分享数
            //播放数
            int play_count = (int) statistics.get("play_count");

            //点赞数
            int digg_count = (int) statistics.get("digg_count");

            //分享数
            int share_count = (int) statistics.get("share_count");

            //首先根据时间和标题看是否已经插入了
            title = title.replace("\n", "");
            Audit audi = auditMapper.selectByTitleAndTime(title, createTime);
            //如果已经入库则更新总点击和总分享量
            if (audi == null) {
                Audit audit = new Audit();
                audit.setCreateTime(createTime);
                audit.setMediaName("抖音");
                audit.setType("video");
                audit.setTitle(title);
                audit.setLayout(layout);
                audit.setLink(share_url);
                audit.setReadCount(play_count);
                audit.setClickTotal(digg_count);
                audit.setShareCount(share_count);
                audit.setReadTotal(play_count);
                audit.setClickTotal(digg_count);
                audit.setShareTotal(share_count);
                insertAudit(authors, editor, editor2, editor3, editor4, audit);

            } else {
                logger.info("================= 更新数据 =================");
                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
                String currentDate = df.format(new Date());
                int dayNum = TimeUtil.daysBetween(createTime, currentDate);

                //未审核并且时间再30天内
                if (audi.getIsAudit() < 2 && dayNum <= 30) {
                    audi.setReadTotal(audi.getReadTotal() + play_count);
                    audi.setClickTotal(audi.getClickTotal() + digg_count);
                    audi.setShareTotal(audi.getShareTotal() + share_count);
                    //更新
                    auditMapper.updateByPrimaryKeySelective(audi);
                    logger.info("更新一条数据成功,{},{}", audi.getTitle(), audi.getCreateTime());
                }


            }
        }
    }

    //获取数据
    private JSONObject getList10(VideoConfig dyInfo, String cursor) {
        //数据读取
        String open_id = dyInfo.getOpenId();
        String access_token = dyInfo.getToken();

        Map<String, String> map = new HashMap<>();
        map.put("open_id", open_id);
        map.put("cursor", cursor);
        map.put("count", "10");
        logger.info("=================抖音数据采集开始=================");
        //请求地址
        String url = dyInfo.getGetVideoListUrl();
        HttpRequest httpRequest = HttpRequest.get(url, map, Boolean.TRUE).header("access-token", access_token);
        String result = httpRequest.body();
        JSONObject jasonObject = JSONObject.parseObject(result);

        JSONObject data = (JSONObject) jasonObject.get("data");
        if (!data.get("error_code").toString().equals("0")) {
            logger.error("抖音数据获取失败:{}", jasonObject);
            return null;
        }
        JSONArray jsonArray = (JSONArray) data.get("list");
        logger.info("=================抖音数据获取,接口响应数据:{}=================", jasonObject);
        logger.info("数据条数为:{}", jsonArray.size());
        return data;
    }

	//开启一个事务,要么都成功插入数据库,要么都失败会滚
    @Transactional(rollbackFor = Exception.class)
    public void insertAudit(String authors, String editor, String editor2, String editor3, String editor4, Audit audit) {
        try {
            auditMapper.insertSelective(audit);
            logger.info("新加一条数据成功,{},{}", audit.getTitle(), audit.getCreateTime());
            //执行插入作者信息
            authorDoUtil.insertAuthorInfoPlus(audit.getAuditId(), authors, AuthorType.AUTHOR);
            //执行编辑作者信息
            authorDoUtil.insertAuthorInfoPlus(audit.getAuditId(), editor, AuthorType.EDITOR);
            //执行插入责编信息
            authorDoUtil.insertAuthorInfoPlus(audit.getAuditId(), editor2, AuthorType.EX_EDITOR);
            //执行插入编审信息
            authorDoUtil.insertAuthorInfoPlus(audit.getAuditId(), editor3, AuthorType.EDITORIAL);
            //执行插入监制信息
            authorDoUtil.insertAuthorInfoPlus(audit.getAuditId(), editor4, AuthorType.SUPERVISOR);

        } catch (Exception e) {
            logger.error("执行抖音数据采集失败,回滚成功!标题为:{},{},{}", audit.getTitle(), audit.getCreateTime(), e.getMessage());
            //手动回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(),使得事务生效,出现异常回滚。
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

        }
    }

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

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

相关文章

项目人力资源管理

相关概念 组织结构图:用图形表示项目汇报关系。最常用的有层次结构图、矩阵图、文本格式的角色描述等3种。 任务分配矩阵(或称责任分配矩阵)(RAM):用来表示需要完成的工作由哪个团队成员负责的矩阵,或需要完成的工作与哪个团队成员有关的矩阵。 一、规划人力资源管理(编…

【Mybatis源码分析】类型处理器(TypeHandler)及其注册

TypeHandler和TypeHandlerRegistryTypeHandlerTypeHandler 源码分析TypeHandler注册&#xff0c;TypeHandlerRegistry源码分析TypeHandler拓展案例总结TypeHandler 大伙都知道Mybatis是对JDBC的封装&#xff0c;那Mybatis是如何处理JDBC类型和Java类型之间的转换的呢&#xff…

图文在线翻译-文本翻译

随着国际交流不断增加&#xff0c;越来越多的企业需要将产品介绍、宣传文案等相关信息翻译成多种语言&#xff0c;以扩大海外市场。但传统的翻译过程通常比较繁琐&#xff0c;耗费时间和人力成本也相对较高。为此&#xff0c;我们推出了一款批量图文翻译软件&#xff0c;帮助企…

【设计模式之美 设计原则与思想:规范与重构】35 | 实战一(下):手把手带你将ID生成器代码从“能用”重构为“好用”

上一节课中&#xff0c;我们结合 ID 生成器代码讲解了如何发现代码质量问题。虽然 ID 生成器的需求非常简单&#xff0c;代码行数也不多&#xff0c;但看似非常简单的代码&#xff0c;实际上还是有很多优化的空间。综合评价一下的话&#xff0c;小王的代码也只能算是“能用”、…

Apache配置与应用和优化

--------构建虚拟 Web 主机--------虚拟Web主机指的是在同一台服务器中运行多个Web站点&#xff0c;其中每一个站点实际上并不独立占用整个服务器&#xff0c;因此被称为“虚拟”Web 主机。通过虚拟 Web 主机服务可以充分利用服务器的硬件资源&#xff0c;从而大大降低网站构建…

【数据结构与算法】一、数据结构的基本概念

文章目录一、数据结构的基本概念1.1 数据结构的研究内容1.2 数据类型和抽象数据类型1.3 算法和算法分析1.3.1 算法的时间复杂度1.3.2 算法时间效率的比较1.4 知识回顾一、数据结构的基本概念 1.1 数据结构的研究内容 1.2 数据类型和抽象数据类型 抽象数据类型&#xff08;ADT…

初识linux之线程基本概念

目录 一、进程地址空间和页表再理解 二、线程 1.线程的概念 2. 进程与线程 3. 线程的意义 4.线程的优点缺点 4.1 优点 4.2 缺点 4.linux中线程的优缺点 4.1 优点 4.2 缺点 5. linux中线程创建相关接口 5.1 线程创建 6. 通过代码查看进程与线程的关系 6.1 线程库…

【软件设计师11】面向对象设计

面向对象设计 1. 设计原则 单一职责原则&#xff1a;设计目的单一的类 开放-封闭原则&#xff1a;对外扩展开放&#xff0c;对修改关闭 里氏(Liskov)替换原则&#xff1a;子类可以替换父类 依赖倒置原则&#xff1a;要依赖与对象&#xff0c;而不是具体实现&#xff1b;针…

Windows安装配置Anaconda

要进行各类有意思大模型的运行&#xff0c;首选需要解决环境问题。本文介绍了windows 下安装anaconda、conda的配置、以及conda的常用命令。 一、下载安装 注1&#xff1a;不要求提前安装python。 注2&#xff1a;因为后续环境多了&#xff0c;会占用较大硬盘空间&#xff0…

siPOOLs(一款高效、特异性好的RNAi试剂,可有效消除脱靶效应)

对于靶向基因沉默&#xff0c;RNA干扰&#xff08;RNAi)具有易于操作、快速结果、高效率、广泛适用于各种细胞类型的多重优势。其具有瞬转效应和剂量依赖效应&#xff0c;这点与小分子非常相似。然而&#xff0c;目前的siRNA试剂的特异性和基因沉默效率不是很稳定&#xff0c;阻…

VSCODE配置OPENCV编译环境(windows)

参考文章&#xff1a; https://www.cnblogs.com/kensporger/archive/2020/02/19/12320622.html 1. 安装MinGW-64 和 CMake&#xff0c;这两个一定要把环境变量添加到电脑的环境变量中&#xff0c;并且打开cmake-gui&#xff0c;细节见上文链接 2.生成MakeFiles。 ①先从open…

二叉树的遍历(节点个数及层序遍历)

简易树的图形&#xff1a; #include <stdio.h> #include <stdlib.h>//重定义数据类型 typedef char DataType; //创建简易的二叉树结构体 typedef struct BTNode {struct BTNode* left;struct BTNode* right;DataType data; }BTNode; //前序&#xff08;根左右&am…

2023年股票代持行业研究报告

第一章 股票代持概述 1.1 基本概念 股票代持&#xff0c;或称委托持股&#xff0c;是指实际出资人与名义出资人达成以下约定&#xff1a;名义出资人作为名义股东&#xff0c;在股东名册等公司工商登记信息上出现&#xff0c;而实际上由实际出资人出资并享有投资权益。 股票代…

WPF MVVM模式构建项目

什么是MVVM模式&#xff1f; MVVM是Model-View-ViewModel的简写&#xff0c;Model就是模型&#xff0c;View就是视图&#xff0c;ViewModel就是View和Model之间解耦和传递消息的中间层。MVVM采用双向数据绑定&#xff0c;View中数据变化将自动反映到ViewModel上&#xff0c;反之…

【并发编程】异步编程CompletableFuture实战

文章目录1.CompletableFuture简介2.CompletableFuture核心API实战3.CompletableFuture嵌套案例实战4.合并两个CompletableFuture案例实战5.多个CompletableFuture任务组合调度实战1.CompletableFuture简介 在JDK8之前&#xff0c;我们使用的Java多线程变成&#xff0c;主要是 …

Golang GORM入门

一、GORM入门 1.1 什么是ORM&#xff1f; orm是一种术语而不是软件 orm英文全称object relational mapping&#xff0c;就是对象映射关系简单来说类似python这种面向对象的程序来说一切皆对象&#xff0c;但是我们使用的数据库却都是关系型的 为了保证一致的使用习惯&#xff…

NumPy 数组学习手册:1~5

原文&#xff1a;Learning NumPy Array 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 一、NumPy 入门 让我们开始吧。 我们将在不同的操作系统上安装 NumPy 和相关软件&#xff0c;并查看一些使用 NumPy 的简单代码。 正如“序言”所述&#xff0c;SciPy 与 NumPy 密…

开放式蓝牙耳机推荐,推荐几款具有代表性的骨传导耳机

骨传导耳机是一种骨传导技术和音频技术结合的产品&#xff0c;通过声音将振动从骨头传到听觉神经&#xff0c;从而达到听音的效果。相比于传统的入耳式耳机&#xff0c;骨传导耳机佩戴更舒适&#xff0c;不会对耳朵造成损伤。然而市面上有很多不同类型的骨传导耳机&#xff0c;…

机器学习和深度学习在气象中的应用(台风预报只能订正、风速预报订正、LSTM 方法预测 ENSO)

查看原文>>>Python人工智能在气象中的实践技术应用 目录 专题一、Python 和科学计算基础 专题二、机器学习和深度学习基础理论和实操 2.1 机器学习和深度学习基础理论 2.2 sklearn 和pytorch 库 专题三 、气象领域中的机器学习应用实例 3.1 GFS 数值模式的风速…

【玩转RT-Thread】RT-Thread网络框架:BSD网络接口SAL套接字抽象层

文章目录RT-Thread网络框架&#xff1a;BSD网络接口&SAL套接字抽象层基础知识1.TCP与UDP的区别2.TCP编程 服务端配置过程3.TCP编程 客户端配置过程4.UDP编程 客户端配置过程SAL套接字抽象层1.SAL组件主要功能特点&#xff1a;2.SAL网络框架3.工作原理4.多协议接入与接口函数…