springboot集成钉钉,发送钉钉日报

news2025/2/5 0:53:25

目录

1.说明

2.示例

3.总结


1.说明

学习地图 - 钉钉开放平台

在钉钉开放文档中可以查看有关日志相关的api,主要用到以下几个api:

        ①获取模板详情

        ②获取用户发送日志的概要信息

        ③获取日志接收人员列表

        ④创建日志

发送日志时需要根据模板规定日志的格式,所以先获取要发送日志的模板信息,然后获取用户在最近一段时间内发送的日志的概要信息,并根据最新一次的日志信息获取日志的接收人员信息,然后调用创建日志的api,设置日志内容,及接收人员。

2.示例

依赖

        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>alibaba-dingtalk-service-sdk</artifactId>
            <version>2.0.0</version>
        </dependency>

钉钉工具类

package com.kingagroot.info.common.tools.common;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.nacos.api.config.annotation.NacosValue;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.*;
import com.dingtalk.api.response.*;
import com.kingagroot.info.common.contants.CommonContants;
import com.kingagroot.info.common.tools.thirdparty.DingDingTool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;

/**
 * @Author linaibo
 * @Date 2024/1/6 16:18
 * @Version 1.0
 */
@Component
public class DingTool {

    private static LogTool logTool;

    @Autowired
    public void setLogTool(LogTool logTool) {
        DingTool.logTool = logTool;
    }

    // 权限用户名
    private static String accessKey;
    // 权限密码
    private static String secret;
    // agent_id
    private static Long agentId;
    // tokenUrl
    private static String tokenUrl;
    // 发送消息url
    private static String sendMsgUrl;
    // 系统url
    private static String sysUrl;
    // 模板名称
    private static String dingTemplateName;
    // 模板url
    private static String dingTemplateUrl;
    // 创建日报url
    private static String dingCreateUrl;
    // 日志信息url
    private static String simpleListUrl;
    // 接收人信息url
    private static String receiverUrl;


    @NacosValue(value = "${dingding.appkey}", autoRefreshed = true)
    public void setAccessKey(String accessKey) {
        DingTool.accessKey = accessKey;
    }

    @NacosValue(value = "${dingding.appsecret}", autoRefreshed = true)
    public void setSecret(String secret) {
        DingTool.secret = secret;
    }

    @NacosValue(value = "${dingding.agentId}", autoRefreshed = true)
    public void setAgentId(Long agentId) {
        DingTool.agentId = agentId;
    }

    @NacosValue(value = "${dingding.gettoken}", autoRefreshed = true)
    public void setTokenUrl(String tokenUrl) {
        DingTool.tokenUrl = tokenUrl;
    }

    @NacosValue(value = "${dingding.sendMsg}", autoRefreshed = true)
    public void setSendMsgUrl(String sendMsgUrl) {
        DingTool.sendMsgUrl = sendMsgUrl;
    }

    @NacosValue(value = "${sys.url}", autoRefreshed = true)
    public void setSysUrl(String sysUrl) {
        DingTool.sysUrl = sysUrl;
    }

    @NacosValue(value = "${dingding.templateName}", autoRefreshed = true)
    public void setDingTemplateName(String dingTemplateName) {
        DingTool.dingTemplateName = dingTemplateName;
    }

    @NacosValue(value = "${dingding.templateUrl}", autoRefreshed = true)
    public void setDingTemplateUrl(String dingTemplateUrl) {
        DingTool.dingTemplateUrl = dingTemplateUrl;
    }

    @NacosValue(value = "${dingding.createUrl}", autoRefreshed = true)
    public void setDingCreateUrl(String dingCreateUrl) {
        DingTool.dingCreateUrl = dingCreateUrl;
    }

    @NacosValue(value = "${dingding.simpleListUrl}", autoRefreshed = true)
    public void setSimpleListUrl(String simpleListUrl) {
        DingTool.simpleListUrl = simpleListUrl;
    }

    @NacosValue(value = "${dingding.receiverUrl}", autoRefreshed = true)
    public void setReceiverUrl(String receiverUrl) {
        DingTool.receiverUrl = receiverUrl;
    }

    /**
     * 获取钉钉token
     *
     * @return
     */
    public static String getDingToken() {
        DingTalkClient client = new DefaultDingTalkClient(tokenUrl);
        OapiGettokenRequest request = new OapiGettokenRequest();
        request.setAppkey(accessKey);
        request.setAppsecret(secret);
        request.setHttpMethod("GET");
        try {
            OapiGettokenResponse response = client.execute(request);
            if (response.isSuccess()) {
                // 调用成功返回token信息
                return response.getAccessToken();
            }
            // 调用接口异常,输出异常信息,发送钉钉通知
            processErrMsg("getDingToken", "获取钉钉token失败", JSON.toJSONString(response));
        } catch (Exception e) {
            // 调用接口异常,输出异常信息
            processErrMsg("getDingToken", "获取钉钉token失败", JSON.toJSONString(e));
        }
        return null;
    }

    public static void processErrMsg(String method, String errorMsg, String responseMsg, String... dingId) {
        StringBuilder errMsg = new StringBuilder();
        errMsg.append(errorMsg).append(",响应信息:").append(JSON.toJSONString(responseMsg));
        if (dingId.length != CommonContants.NUM_0) {
            errMsg.append(",钉钉id:").append(dingId[0]);
        }
        logTool.saveExceptionLog("", "DingTool", method, errMsg.toString());
        DingDingTool.sendDingMsg(errMsg.toString());
    }

    /**
     * 发送钉钉通知
     *
     * @param token
     * @param pwd
     * @param userCode
     */
    public static boolean sendMsg(String token, String pwd, String userCode) {
        DingTalkClient client = new DefaultDingTalkClient(sendMsgUrl);
        OapiMessageCorpconversationAsyncsendV2Request request = new OapiMessageCorpconversationAsyncsendV2Request();
        request.setAgentId(agentId);
        request.setUseridList(userCode);
        request.setToAllUser(false);

        OapiMessageCorpconversationAsyncsendV2Request.Msg msg = new OapiMessageCorpconversationAsyncsendV2Request.Msg();
        msg.setMsgtype("text");
        msg.setText(new OapiMessageCorpconversationAsyncsendV2Request.Text());
        StringBuilder content = new StringBuilder();
        content.append("系统地址: ");
        content.append(sysUrl);
        content.append("  ");
        content.append("密码: ");
        content.append(pwd);
        msg.getText().setContent(content.toString());
        request.setMsg(msg);

        try {
            OapiMessageCorpconversationAsyncsendV2Response result = client.execute(request, token);
            if (result.isSuccess()) {
                return true;
            }
            // 调用接口异常,输出异常信息
            processErrMsg("sendMsg", "发送钉钉消息(密码)失败", JSON.toJSONString(result));
        } catch (Exception e) {
            // 调用接口异常,输出异常信息
            processErrMsg("sendMsg", "发送钉钉消息(密码)失败", JSON.toJSONString(e));
        }
        return false;
    }

    /**
     * 发送钉钉通知
     *
     * @param token
     * @param message
     * @param dingId
     */
    public static void sendMessage(String token, String message, String dingId) {
        DingTalkClient client = new DefaultDingTalkClient(sendMsgUrl);
        OapiMessageCorpconversationAsyncsendV2Request request = new OapiMessageCorpconversationAsyncsendV2Request();
        request.setAgentId(agentId);
        // 设置接收者列表
        request.setUseridList(dingId);
        // 是否发送给公司全员
        request.setToAllUser(false);
        // 设置发送的消息类型及消息内容
        OapiMessageCorpconversationAsyncsendV2Request.Msg msg = new OapiMessageCorpconversationAsyncsendV2Request.Msg();
        msg.setMsgtype(CommonContants.TEXT);
        msg.setText(new OapiMessageCorpconversationAsyncsendV2Request.Text());
        msg.getText().setContent(message);
        request.setMsg(msg);
        try {
            OapiMessageCorpconversationAsyncsendV2Response result = client.execute(request, token);
            if (result.isSuccess()) {
                return;
            }
            // 调用接口异常,输出异常信息
            processErrMsg("sendMessage", "发送钉钉消息失败", JSON.toJSONString(result));
        } catch (Exception e) {
            // 调用接口异常,输出异常信息
            processErrMsg("sendMessage", "发送钉钉消息失败", JSON.toJSONString(e));
        }
    }


    /**
     * 查询钉钉模板信息
     *
     * @param token
     * @param dingId
     */
    public static OapiReportTemplateGetbynameResponse.ReportTemplateResponseVo getTemplate(String token, String dingId) {
        DingTalkClient client = new DefaultDingTalkClient(dingTemplateUrl);
        OapiReportTemplateGetbynameRequest req = new OapiReportTemplateGetbynameRequest();
        req.setUserid(dingId);
        req.setTemplateName(dingTemplateName);
        try {
            OapiReportTemplateGetbynameResponse rsp = client.execute(req, token);
            if (rsp.isSuccess()) {
                // 日志内容的校验
                if (CollUtil.isEmpty(rsp.getResult().getFields()) ||
                        !Objects.equals(rsp.getResult().getFields().get(0).getType(), CommonContants.LONG_1)) {
                    processErrMsg("getTemplate", "模板内容已经修改", JSON.toJSONString(rsp.getResult()), dingId);
                    sendMessage(token, "模板内容已经修改,系统无法进行日志推送,请自行发送。" + DateUtil.formatDateTime(new Date()), dingId);
                    return null;
                }
                return rsp.getResult();
            }
            // 调用接口异常,输出异常信息,发送钉钉通知
            processErrMsg("getTemplate", "获取作物育种信息模板失败", JSON.toJSONString(rsp), dingId);
            sendMessage(token, "获取作物育种信息模板失败,系统无法进行日志推送,请自行发送。" + DateUtil.formatDateTime(new Date()), dingId);
        } catch (Exception e) {
            // 调用接口异常,输出异常信息,发送钉钉通知
            processErrMsg("getTemplate", "获取作物育种信息模板失败", JSON.toJSONString(e), dingId);
            sendMessage(token, "获取作物育种信息模板失败,系统无法进行日志推送,请自行发送。" + DateUtil.formatDateTime(new Date()), dingId);
        }
        return null;
    }


    /**
     * 获取用户在某个时间段的日志信息
     *
     * @param dingId
     * @param token
     * @return
     */
    public static OapiReportSimplelistResponse.ReportOapiVo getSimpleReport(String token, String dingId) {
        DingTalkClient client = new DefaultDingTalkClient(simpleListUrl);
        OapiReportSimplelistRequest req = new OapiReportSimplelistRequest();
        long endTime = System.currentTimeMillis();
        long startTime = Instant.now().minus(CommonContants.LONG_21, ChronoUnit.DAYS).toEpochMilli();
        req.setStartTime(startTime);
        req.setEndTime(endTime);
        req.setTemplateName(dingTemplateName);
        req.setUserid(dingId);
        req.setCursor(CommonContants.LONG_0);
        req.setSize(CommonContants.LONG_20);
        try {
            OapiReportSimplelistResponse rsp = client.execute(req, token);
            if (rsp.isSuccess()) {
                List<OapiReportSimplelistResponse.ReportOapiVo> dataList = rsp.getResult().getDataList();
                if (CollUtil.isEmpty(dataList)) {
                    DingDingTool.sendDingMsg("获取最近的日志信息为空,钉钉id:" + dingId);
                    sendMessage(token, "获取最近的日志信息为空,系统无法进行日志推送,请自行发送。" + DateUtil.formatDateTime(new Date()), dingId);
                    return null;
                } else {
                    // 获取最新一次的日报信息
                    Optional<OapiReportSimplelistResponse.ReportOapiVo> lastReport = dataList.stream()
                            .max(Comparator.comparingLong(OapiReportSimplelistResponse.ReportOapiVo::getCreateTime));
                    return lastReport.get();
                }
            }
            processErrMsg("getSimpleReport", "获取最近的日志信息失败", JSON.toJSONString(rsp), dingId);
            sendMessage(token, "获取最近的日志信息失败,系统无法进行日志推送,请自行发送。" + DateUtil.formatDateTime(new Date()), dingId);
        } catch (Exception e) {
            processErrMsg("getSimpleReport", "获取最近的日志信息失败", JSON.toJSONString(e), dingId);
            sendMessage(token, "获取最近的日志信息失败,系统无法进行日志推送,请自行发送。" + DateUtil.formatDateTime(new Date()), dingId);
        }
        return null;
    }

    public static List<String> getReceiver(OapiReportSimplelistResponse.ReportOapiVo report, String token) {
        DingTalkClient client = new DefaultDingTalkClient(receiverUrl);
        OapiReportReceiverListRequest req = new OapiReportReceiverListRequest();
        req.setReportId(report.getReportId());
        try {
            OapiReportReceiverListResponse rsp = client.execute(req, token);
            if (rsp.isSuccess()) {
                List<String> useridList = rsp.getResult().getUseridList();
                if (CollUtil.isEmpty(useridList)) {
                    DingDingTool.sendDingMsg("查询的日志接收人信息为空,请求信息:" + JSON.toJSONString(report));
                    sendMessage(token, "查询的日志接收人信息为空,系统无法进行日志推送,请自行发送。" + DateUtil.formatDateTime(new Date()), report.getCreatorId());
                    return null;
                }
                return useridList;
            }
            processErrMsg("getReceiver", "查询日志的接收人信息失败", JSON.toJSONString(rsp), report.getCreatorId());
            sendMessage(token, "查询日志的接收人信息失败,系统无法进行日志推送,请自行发送。" + DateUtil.formatDateTime(new Date()), report.getCreatorId());
        } catch (Exception e) {
            processErrMsg("getReceiver", "查询日志的接收人信息失败", JSON.toJSONString(e), report.getCreatorId());
            sendMessage(token, "查询日志的接收人信息失败,系统无法进行日志推送,请自行发送。" + DateUtil.formatDateTime(new Date()), report.getCreatorId());
        }
        return null;
    }

    /**
     * 发送钉钉日报
     *
     * @param createDataParam
     * @param token
     */
    public static void createReport(OapiReportCreateRequest.OapiCreateReportParam createDataParam, String token) {
        DingTalkClient client = new DefaultDingTalkClient(dingCreateUrl);
        OapiReportCreateRequest req = new OapiReportCreateRequest();
        req.setCreateReportParam(createDataParam);
        try {
            OapiReportCreateResponse rsp = client.execute(req, token);
            if (!rsp.isSuccess()) {
                // 调用接口异常,输出异常信息
                processErrMsg("createReport", "发送日报失败", JSON.toJSONString(rsp), JSON.toJSONString(createDataParam));
                sendMessage(token, "系统发送日志失败,请自行发送。" + DateUtil.formatDateTime(new Date()), createDataParam.getUserid());
            }
        } catch (Exception e) {
            // 调用接口异常,输出异常信息
            processErrMsg("createReport", "发送日报失败", JSON.toJSONString(e), JSON.toJSONString(createDataParam));
            sendMessage(token, "系统发送日志失败,请自行发送。" + DateUtil.formatDateTime(new Date()), createDataParam.getUserid());
        }
    }
}

发送钉钉日报

    /**
     * 发送钉钉日报
     *
     * @param farmWorkMap
     */
    private void processDingTalkReports(Map<String, List<FarmInfoDto>> farmWorkMap) {
        // 钉钉id为空的用户列表,此类用户没有加入钉钉组织
        StringBuilder errUserId = new StringBuilder();
        // 循环农事信息列表,进行如下处理
        for (Map.Entry<String, List<FarmInfoDto>> farm : farmWorkMap.entrySet()) {
            // 用户及钉钉id
            String userId = farm.getKey();
            String dingId = farm.getValue().get(0).getDingId();
            // 钉钉id为空时,无法进行发送处理
            if (StrUtil.isBlank(dingId)) {
                errUserId.append(userId).append(",");
                continue;
            }
            // 获取钉钉的token信息
            String dingToken = getDingToken();
            if (StrUtil.isBlank(dingToken)) {
                continue;
            }
            // 获取模板信息
            OapiReportTemplateGetbynameResponse.ReportTemplateResponseVo template =
                    DingTool.getTemplate(dingToken, dingId);
            if (ObjectUtil.isNull(template)) {
                continue;
            }
            // 获取接收人信息
            OapiReportSimplelistResponse.ReportOapiVo simpleReport = DingTool.getSimpleReport(dingToken, dingId);
            List<String> receiver = new ArrayList<>();
            if (ObjectUtil.isNotNull(simpleReport)) {
                receiver = DingTool.getReceiver(simpleReport, dingToken);
            }
            if (CollUtil.isNotEmpty(receiver)) {
                // 构建发送日报的请求信息
                OapiReportCreateRequest.OapiCreateReportParam createDataParam = getCreateDataParam(template, farm, receiver);
                // 发送钉钉日报
                DingTool.createReport(createDataParam, dingToken);
            }
        }
        // 如果有问题的用户列表不为空,则将有问题的用户列表推送至钉钉群中
        if (StrUtil.isNotEmpty(errUserId)) {
            DingDingTool.sendDingMsg("以下用户不存在钉钉id,用户:" + errUserId);
        }
    }
    // 获取钉钉token
    public String getDingToken() {
        String dingDingToken = RedisTool.getString(redisEntr.getJedis(), CommonContants.DINGDING_TOKEN);
        if (StrUtil.isEmpty(dingDingToken)) {
            // 获取token
            dingDingToken = DingTool.getDingToken();
            // 存储token
            RedisTool.setString(redisEntr.getJedis(), CommonContants.DINGDING_TOKEN, dingDingToken, 5400);
        }
        return dingDingToken;
    }
 /**
     * 获取创建日报的请求信息
     *
     * @param template
     * @param farmMap
     */
    public OapiReportCreateRequest.OapiCreateReportParam getCreateDataParam(OapiReportTemplateGetbynameResponse.ReportTemplateResponseVo template,
                                                                            Map.Entry<String, List<FarmInfoDto>> farmMap,
                                                                            List<String> receivers) {
        // 获取模板内容信息
        List<OapiReportTemplateGetbynameResponse.Fields> fields = template.getFields();
        // 构建创建日报请求结构
        OapiReportCreateRequest.OapiCreateReportParam createReportParam = new OapiReportCreateRequest.OapiCreateReportParam();
        List<OapiReportCreateRequest.OapiReportContentVo> list = new ArrayList<>();
        OapiReportCreateRequest.OapiReportContentVo obj = new OapiReportCreateRequest.OapiReportContentVo();
        list.add(obj);
        // 添加日报内容,只添加模板的第一项
        OapiReportTemplateGetbynameResponse.Fields field = fields.get(0);
        obj.setSort(field.getSort());
        obj.setType(field.getType());
        obj.setContentType(CommonContants.MARKDOWN);
        StringBuilder farmInfo = new StringBuilder();
        for (int i = 0; i < farmMap.getValue().size(); i++) {
            FarmInfoDto farm = farmMap.getValue().get(i);
            farmInfo.append(i + 1).append(". ").append(farm.getBsName()).append(" ").append(farm.getFarmName()).append(" ");
            if (farm.getWorkCount().compareTo(BigDecimal.ZERO) > CommonContants.NUM_0) {
                farmInfo.append(farm.getWorkCount().stripTrailingZeros().toPlainString()).append(farm.getWorkValue()).append(" ");
            }
            if (StrUtil.isNotBlank(farm.getParticipants())) {
                farmInfo.append("参与人:").append(farm.getParticipants());
            }
            farmInfo.append("\n");
        }
        obj.setContent(farmInfo.toString());
        obj.setKey(field.getFieldName());
        createReportParam.setContents(list);
        // 设置汇报人信息
        createReportParam.setToUserids(receivers);
        // 设置模板id
        createReportParam.setTemplateId(template.getId());
        // 是否发送单聊消息
        createReportParam.setToChat(false);
        // 日志来源
        createReportParam.setDdFrom(CommonContants.TJNS);
        // 创建日志的用户id
        createReportParam.setUserid(template.getUserid());

        return createReportParam;
    }

3.总结

①我使用的是企业内部创建应用的方式,创建应用后可以拿到应用的凭证信息。

②要调用日志相关的接口,需要开通日志接口的权限信息,如下:

③测试时,可以创建一个企业账号,然后创建应用,并开通日志相关接口的权限,拉入相关人员,设置日志模板,并设置人员的上下级关系进行测试。

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

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

相关文章

优选算法的灵动之章:双指针专题(一)

个人主页&#xff1a;手握风云 专栏&#xff1a;算法 目录 一、双指针算法思想 二、算法题精讲 2.1. 查找总价格为目标值的两个商品 2.2. 盛最多水的容器 ​编辑 2.3. 移动零 2.4. 有效的三角形个数 一、双指针算法思想 双指针算法主要用于处理数组、链表等线性数据结构…

PyQt4学习笔记1】使用QWidget创建窗口

目录 一、创建一个简单的 QWidget 窗口 二、设置窗口属性 1. 设置窗口标题 2. 设置背景颜色 3. 设置窗口大小和位置 4. 设置窗口模式 5. 关闭窗口 6. QWidget 及其子控件的样式 三、添加控件到 QWidget 1. 添加按钮 2. 添加标签 3. 添加文本框 4. 控件布局管理 四、自定义样式 …

pycharm 中的 Mark Directory As 的作用是什么?

文章目录 Mark Directory As 的作用PYTHONPATH 是什么PYTHONPATH 作用注意事项 Mark Directory As 的作用 可以查看官网&#xff1a;https://www.jetbrains.com/help/pycharm/project-structure-dialog.html#-9p9rve_3 我们这里以 Mark Directory As Sources 为例进行介绍。 这…

【C++】string类(上):string类的常用接口介绍

文章目录 前言一、C中设计string类的意义二、string类的常用接口说明1. string类对象的常见构造2. string类对象的容量操作2.1 size、capacity 和 empty的使用2.2 clear的使用2.3 reserve的使用2.4 resize的使用 3. string类对象的访问及遍历操作3.1 下标[ ] 和 at3.2 迭代器it…

从理论到实践:Linux 进程替换与 exec 系列函数

个人主页&#xff1a;chian-ocean 文章专栏-Linux 前言&#xff1a; 在Linux中&#xff0c;进程替换&#xff08;Process Substitution&#xff09;是一个非常强大的特性&#xff0c;它允许将一个进程的输出直接当作一个文件来处理。这种技术通常用于Shell脚本和命令行操作中…

3 卷积神经网络CNN

1 Image Classification (Neuron Version) – 1.1 Observation 1 1.2 Observation 2 如果不同的receptive field需要相同功能的neuron&#xff0c;可以使这些neuron共享参数 1.3 Benefit of Convolutional Layer 2 Image Classification (Filter Version) 不用担心filter大小…

详解Linux系统的终端(Terminal)以及分类(各种tty开头的设备文件)

目录 终端(Terminal)的概念和作用终端(Terminal)在Linux中被视为设备,每个终端有自己的设备文件tty三个字母的来源(tty名字的来源)如何查看当前终端的设备文件常见终端的分类1-串口终端02-虚拟控制台终端&#xff08;Virtual Console&#xff09;03-伪终端&#xff08;Pseudo T…

强化学习数学原理(五)——随机近似与随机

一、Motivating example 首先有个random variable(随机变量)X&#xff0c;我们的目标就是求出他的expectation E(x)&#xff0c;我们有一些iid的采样&#xff0c;xi&#xff0c;从1到n&#xff0c;求出均值 但是如果有很多数据&#xff0c;我需要等很久&#xff0c;把所有数据都…

线性数据结构:单向链表

放弃眼高手低&#xff0c;你真正投入学习&#xff0c;会因为找到一个新方法产生成就感&#xff0c;学习不仅是片面的记单词、学高数......只要是提升自己的过程&#xff0c;探索到了未知&#xff0c;就是学习。 考虑到可能有小白在合并代码时出现各种细节问题&#xff0c;本文…

线程互斥同步

前言&#xff1a; 简单回顾一下上文所学&#xff0c;上文我们最重要核心的工作就是介绍了我们线程自己的LWP和tid究竟是个什么&#xff0c;总结一句话&#xff0c;就是tid是用户视角下所认为的概念&#xff0c;因为在Linux系统中&#xff0c;从来没有线程这一说法&#xff0c;…

《苍穹外卖》项目学习记录-Day11订单统计

根据起始时间和结束时间&#xff0c;先把begin放入集合中用while循环当begin不等于end的时候&#xff0c;让begin加一天&#xff0c;这样就把这个区间内的时间放到List集合。 查询每天的订单总数也就是查询的时间段是大于当天的开始时间&#xff08;0点0分0秒&#xff09;小于…

SAP HCM 回溯分析

最近总有人问回溯问题&#xff0c;今天把12年总结的笔记在这共享下&#xff1a; 12年开这个图的时候总是不明白是什么原理&#xff0c;教程看N次&#xff0c;网上资料找一大堆&#xff0c;就是不明白原理&#xff0c;后来为搞明白逻辑&#xff0c;按照教材的数据一样做&#xf…

Med-R2:基于循证医学的检索推理框架:提升大语言模型医疗问答能力的新方法

Med-R2 : Crafting Trustworthy LLM Physicians through Retrieval and Reasoning of Evidence-Based Medicine Med-R2框架Why - 这个研究要解决什么现实问题What - 核心发现或论点是什么How - 1. 前人研究的局限性How - 2. 你的创新方法/视角How - 3. 关键数据支持How - 4. 可…

bypass hcaptcha、hcaptcha逆向

可以过steam&#xff0c;已支持并发&#xff0c;欢迎询问&#xff01; 有事危&#xff0c;ProfessorLuoMing

python-UnitTest框架笔记

UnitTest框架的基本使用方法 UnitTest框架介绍 框架&#xff1a;framework&#xff0c;为了解决一类事情的功能集合 UnitTest框架&#xff1a;是python自带的单元测试框架 自带的&#xff0c;可以直接使用&#xff0c;不需要格外安装 测试人员用来做自动化测试&#xff0c;作…

掌握API和控制点(从Java到JNI接口)_35 JNI开发与NDK 03

3、 如何载入 .so档案 VM的角色 由于Android的应用层级类别都是以Java撰写的&#xff0c;这些Java类别转译为Dex型式的Bytecode之后&#xff0c;必须仰赖Dalvik虚拟机器(VM: Virtual Machine)来执行之。 VM在Android平台里&#xff0c;扮演很重要的角色。此外&#xff0c;在执…

CDDIS从2025年2月开始数据迁移

CDDIS 将从 2025 年 2 月开始将我们的网站从 cddis.nasa.gov 迁移到 earthdata.nasa.gov&#xff0c;并于 2025 年 6 月结束。 期间可能对GAMIT联网数据下载造成影响。

VSCode设置内容字体大小

1、打开VSCode软件&#xff0c;点击左下角的“图标”&#xff0c;选择“Setting”。 在命令面板中的Font Size处选择适合自己的字体大小。 2、对比Font Size值为14与20下的字体大小。

嵌入式学习---蜂鸣器篇

1. 蜂鸣器分类 蜂鸣器是一种电子发声器件&#xff0c;采用直流电压供电&#xff0c;能够发出声音。广泛应用于计算机、打印机、报警器、电子玩具等电子产品中作为发声部件。一般仅从外形不易分辨蜂鸣器的种类。但是有些蜂鸣器使用广泛&#xff0c;见得多了就很容易分辨。例如常…

【优先算法】专题——前缀和

目录 一、【模版】前缀和 参考代码&#xff1a; 二、【模版】 二维前缀和 参考代码&#xff1a; 三、寻找数组的中心下标 参考代码&#xff1a; 四、除自身以外数组的乘积 参考代码&#xff1a; 五、和为K的子数组 参考代码&#xff1a; 六、和可被K整除的子数组 参…