Java腾讯会议api接口,创建会议、查询会议,取消会议调用实例,以及踩过的坑


最近公司需要做一个腾讯会议API的对接,经过查看官方文档和腾讯相关技术人员周旋~~以及不断踩坑,终于在我的不懈努力下,成功将API接口调通了

腾讯会议API接口文档:https://cloud.tencent.com/document/product/1095/42407

一、腾讯会议JWT鉴权

  • 腾讯会议API接口文档中详细写了使用API需要做的准备: 包括-创建应用,以及获取相应应用的SecretId,SecretKey,AppId等
  • 根据不同的应用创建方式,选择的鉴权方式也不同。
  • 腾讯会议API提供了两套鉴权方式,在这里我只介绍企业自建应用鉴权(JWT)

1、公共参数

公共参数是用于标识用户和接口鉴权目的的参数,如非必要,在每个接口单独的接口文档中不再对这些参数进行说明,但每次请求均需要携带这些参数,才能正常发起请求。
API 采用 TC3-HMAC-SHA256 签名方法,公共参数需要统一放到 HTTP Header 请求头部中。

参数名称类型必选描述
Content-TypeString内容类型,传入格式必须为 application/json。
X-TC-ActionString操作的接口名称。取值参考接口文档中输入参数公共参数 Action 的说明。例如云服务器的查询实例列表接口,取值为 DescribeInstances。
X-TC-RegionString地域参数,用来标识希望操作哪个地域的数据。接口接受的地域取值参考接口文档中输入参数公共参数 Region 的说明。注意:某些接口不需要传递该参数,接口文档中会对此特别说明,此时即使传递该参数也不会生效。
X-TC-KeyString此参数参与签名计算。腾讯云 API 接入,申请的安全凭证密钥对中的 SecretId,其 Secretkey 用于签名。企业管理员可以登录 腾讯会议官网,单击右上角用户中心,在左侧菜单栏中的企业管理 > 高级 > restApi中进行查看。
X-TC-TimestampString此参数参与签名计算。当前 UNIX 时间戳,可记录发起 API 请求的时间。例如1529223702,单位为秒。注意:如果与服务器时间相差超过5分钟,会引起签名过期错误。
X-TC-NonceString此参数参与签名计算。随机正整数。
X-TC-VersionString应用 App 的版本号,建议设置,以便灰度和查找问题。
X-TC-SignatureString放置由下面的签名方法产生的签名。
X-TC-TokenString临时证书所用的 Token ,需要结合临时密钥一起使用。临时密钥和 Token 需要到访问管理服务调用接口获取。长期密钥不需要 Token。
AppIdString腾讯会议分配给三方开发应用的 App ID。企业管理员可以登录 腾讯会议官网,单击右上角用户中心,在左侧菜单栏中的企业管理 > 高级 > restApi中进行查看。
SdkIdString用户子账号或开发的应用 ID,企业管理员可以登录 腾讯会议官网,单击右上角用户中心,在左侧菜单栏中的企业管理 > 高级 > restApi中进行查看(如存在 SdkId 则必须填写,早期申请 API 且未分配 SdkId 的客户可不填写)。
X-TC-RegisteredString启用账户通讯录,传入值必须为1,创建的会议可出现在用户的会议列表中。 启用账户通讯录说明: 1. 通过 SSO 接入腾讯会议账号体系。 2. 通过调用接口创建企业用户。 3. 通过企业管理后台添加或批量导入企业用户。

2、签名算法

可参考腾讯会议API-企业自建应用鉴权(JWT)

https://cloud.tencent.com/document/product/1095/42413

import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

/**
   * 生成签名,开发版本oracle jdk 1.8.0_221
   *
   * @param secretId        邮件下发的secret_id
   * @param secretKey       邮件下发的secret_key
   * @param httpMethod      http请求方法 GET/POST/PUT等
   * @param headerNonce     X-TC-Nonce请求头,随机数
   * @param headerTimestamp X-TC-Timestamp请求头,当前时间的秒级时间戳
   * @param requestUri      请求uri,eg:/v1/meetings
   * @param requestBody     请求体,没有的设为空串
   * @return 签名,需要设置在请求头X-TC-Signature中
   * @throws NoSuchAlgorithmException e
   * @throws InvalidKeyException      e
   */
  static String sign(String secretId, String secretKey, String httpMethod, String headerNonce,
      String headerTimestamp, String requestUri, String requestBody)
      throws NoSuchAlgorithmException, InvalidKeyException {

    String tobeSig =
        httpMethod + "\nX-TC-Key=" + secretId + "&X-TC-Nonce=" + headerNonce + "&X-TC-Timestamp="
            + headerTimestamp + "\n" + requestUri + "\n" + requestBody;
    Mac mac = Mac.getInstance(HMAC_ALGORITHM);
    SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8),
        mac.getAlgorithm());
    mac.init(secretKeySpec);
    byte[] hash = mac.doFinal(tobeSig.getBytes(StandardCharsets.UTF_8));
    String hexHash = bytesToHex(hash);
    return new String(Base64.getEncoder().encode(hexHash.getBytes(StandardCharsets.UTF_8)));
  }

  private static String HMAC_ALGORITHM = "HmacSHA256";

  private static char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c',
      'd', 'e', 'f'};

  static String bytesToHex(byte[] bytes) {

    char[] buf = new char[bytes.length * 2];
    int index = 0;
    for (byte b : bytes) {
      buf[index++] = HEX_CHAR[b >>> 4 & 0xf];
      buf[index++] = HEX_CHAR[b & 0xf];
    }

    return new String(buf);
  }

有了签名算法,我们可以生成公共请求头了

二、封装获取公共请求头方法

JWT鉴权方式需要在每次请求时加入公共请求头参数,以及通过签名算法生成的签名也需携带入请求头,借此,我封装了生成请求头的方法。

其中secretIdsecretKeyappIdsdkId是创建企业自建应用生成的,参考腾讯会议API文档-创建企业自建应用

需要注意的地方:

1、Timestamp参数是Unix时间戳,单位是秒

2、GET方法请求体需传""

/**
   * 获取公共请求头
   *
   * @param httpMethod  请求方式:POST|GET
   * @param requestUri  请求uri
   * @param requestBody 请求体 GET方法请求体需传""
   * @return 拼接好的请求头
   */
  public static Map<String, String> getHeader(String httpMethod, String requestUri,
      String requestBody) {
    HashMap<String, String> header = new HashMap<>(8);
//      请求随机数
    String headerNonce = String.valueOf(new Random().nextInt(999999));
    //  当前时间的UNIX时间戳
    String headerTimestamp = String.valueOf(System.currentTimeMillis() / 1000);
    String signature = null;
    try {
      signature = sign(secretId, secretKey, httpMethod, headerNonce, headerTimestamp, requestUri,
          requestBody);
    } catch (Exception e) {
      log.error("签名生成异常", e);
    }

    header.put("Content-Type", "application/json");
    header.put("X-TC-Key", secretId);
    header.put("X-TC-Timestamp", headerTimestamp);
    header.put("X-TC-Nonce", headerNonce);
    header.put("AppId", appId);
    header.put("X-TC-Version", "1.0");
    header.put("X-TC-Signature", signature);
    header.put("SdkId", sdkId);
    header.put("X-TC-Registered", "1");

    return header;
  }

三、封装HTTP请求方法

为了方便调用API,我封装了GET和POST方法

1、GET方法

/**
   * 发起get请求
   * @param uri 	请求uri 生成签名使用
   * @param address 请求路径
   * @return 		请求结果的JsonStr
   */
  public static String sendGet(String address,String uri) {
    //get请求,请求体为""
    Map<String, String> header = getHeader("GET",uri,"");
    String result = "";
    String logInfo = "";
    GetMethod getMethod = null;
    try {
      // 创建httpClient实例对象
      HttpClient httpClient = new HttpClient();
      // 设置httpClient连接主机服务器超时时间:15000毫秒
      httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(15000);
      // 创建GET请求方法实例对象
      getMethod = new GetMethod(address);
      // 设置post请求超时时间
      getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 60000);
      if (header != null) {
        for (Map.Entry<String, String> entry : header.entrySet()) {
          getMethod.addRequestHeader(entry.getKey(), entry.getValue());
        }
      }
      logInfo = "HTTP调用接口:" + address;
      httpClient.executeMethod(getMethod);
      result = getMethod.getResponseBodyAsString();
    } catch (Exception e) {
      log.info("HTTP调用接口出错:" + logInfo + e, e);
    } finally {
      if (null != getMethod) {
        getMethod.releaseConnection();
      }
    }
    return result;
  }

2、POST方法

 /**
   * 腾讯会议发送post请求  携带生产签名和公共请求头参数
   *
   * @param address     请求地址
   * @param uri         请求uri生产签名使用
   * @param requestBody 请求参数
   * @return 			请求响应结果
   */
  public static String sendPost(String address, String uri, String requestBody) {

    //生成公共请求头参数和签名
    HashMap<String, String> headerMap = getHeaderTest("POST", uri, requestBody);
    CloseableHttpClient httpClient = HttpClients.createDefault();
    HttpPost httpPost = new HttpPost(address);
    String jsonStr = "";
    try {
      for (Entry<String, String> header : headerMap.entrySet()) {
        httpPost.setHeader(header.getKey(), header.getValue());
      }

      httpPost.setEntity(new StringEntity(requestBody));
      CloseableHttpResponse httpResponse = null;
      httpResponse = httpClient.execute(httpPost);
      HttpEntity httpEntity = httpResponse.getEntity();
      if (httpEntity != null) {
        jsonStr = EntityUtils.toString(httpEntity, "UTF-8");
      }
      log.info("腾讯会议httpPost请求相应信息{}", jsonStr);
    } catch (IOException e) {
      log.info("腾讯会议httpPost发送异常", e);
    } finally {
      httpPost.releaseConnection();
      try {
        httpClient.close();
      } catch (IOException e) {
        log.error("httpClient关闭异常", e);
      }
    }
    return jsonStr;
  }

有了公共请求头,我们就可以调用API了

四、调用REST APIs

REST APIs:https://cloud.tencent.com/document/product/1095/42414

1、创建会议

需要注意的是会议主题 subject,直接传入中文,会报API签名验证失败,这个是一个坑点,当初搞了我好久,一直没查到原因。

解决办法是取中文的Unicode

/**
   * 创建预约腾讯会议
   *
   * @param subject   主题
   * @param startTime 开始时间戳
   * @param duration  持续时间(单位分钟)
   * @param userId    企业注册时的用户名
   * @return 创建结果
   */
  public static Map<String, Object> creatMeeting(String subject, Long startTime,
      Integer duration, String userId) {
//    Unix时间戳,单位为秒
    startTime = startTime / 1000;
    String endTime = String.valueOf(startTime + (duration * 60));
    HashMap<String, Object> resultMap = new HashMap<>(8);
//    请求体,get方法请求体需传""
    String resultBody = "{" +
        //会议结束时间
        "\"end_time\": \"" + endTime + "\"" + "," +
        //会议开始时间戳(单位秒)。
        "\"start_time\": \"" + startTime + "\"," +
        //用户的终端设备类型 1:PC
        "\"instanceid\": " + "1" + "," +
        // 会议类型:0:预约会议 1:快速会议
        "\"type\": " + "0" + "," +
        //腾讯会议用户唯一标识 不能为1--9内的数字
        "\"userid\": \"" + userId + "\"," +
        //会议主题
        "\"subject\": \"" + getUnicode(subject) + "\"" +
        "}";

//    创建会议uri
    String uri = "/v1/meetings";
    String address = MEETING_DOMAIN_URL + uri;

    try {
      String jsonStr = requestPost(address, uri, resultBody);
      JSONObject jsonObject = JSONObject.parseObject(jsonStr);
      if (null == jsonObject.getJSONObject("error_info")) {
        JSONObject meetingInfo = jsonObject.getJSONArray("meeting_info_list").getJSONObject(0);
        resultMap.put("success", "true");
        resultMap.put("message", "创建预约会议成功");
        resultMap.put("meetingId", meetingInfo.getString("meeting_id"));
        resultMap.put("meetingCode", meetingInfo.getString("meeting_code"));
        resultMap.put("joinUrl", meetingInfo.getString("join_url"));
      } else {
        resultMap.put("success", "false");
        resultMap.put("message", "创建预约会议失败");
        resultMap.put("errorInfo", jsonObject.getJSONObject("error_info"));
      }
    } catch (Exception e) {
      resultMap.put("success", "false");
      resultMap.put("message", e.getMessage());
      log.error("创建预约会议发生异常", e);
    }
    return resultMap;
  }

  /**
   * 防止api请求中传入中文导致的报错问题
   * @param s 中文字符串
   * @return Unicode码
   */
public static String getUnicode(String s) {
    try {
      StringBuffer out = new StringBuffer("");
      byte[] bytes = s.getBytes("unicode");
      for (int i = 0; i < bytes.length - 1; i += 2) {
        out.append("\\u");
        String str = Integer.toHexString(bytes[i + 1] & 0xff);
        for (int j = str.length(); j < 2; j++) {
          out.append("0");
        }
        String str1 = Integer.toHexString(bytes[i] & 0xff);
        out.append(str1);
        out.append(str);
      }
      return out.toString();
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
      return null;
    }
  }

2、查询会议

  /**
   * 根据meetingId查询腾讯会议
   *
   * @param meetingId 创建腾讯会议生成的meetingId
   * @param userId    创建人的id
   * @return 查询结果
   */
  public static Map<String, Object> queryMeetings(String meetingId, String userId) {
    HashMap<String, Object> resultMap = new HashMap<>(8);
    try {
      String uri = "/v1/meetings/" + meetingId + "?userid=" + userId + "&instanceid=1";

//    https://api.meeting.qq.com/v1/meetings/{meetingId}?userid={userid}&instanceid={instanceid}
//      String address = url + "/" + meetingId;
      String address = MEETING_DOMAIN_URL + uri;


      String jsonStr = sendGet(address, uri);
      JSONObject jsonObject = JSONObject.parseObject(jsonStr);

      resultMap.put("message", jsonObject.getString("error_info"));
    } catch (Exception e) {
      log.error("获取会议信息异常", e);
      resultMap.put("success", "false");
      resultMap.put("message", e.getMessage());
    }
    return resultMap;
  }

3、取消会议

/**
   * 取消预约的会议
   *
   * @param userId    预定人id
   * @param meetingId 会议id
   * @return 取消结果
   */
  public static Map<String, Object> cancelMeeting(String userId, String meetingId) {

    HashMap<String, Object> resultMap = new HashMap<>(8);
//    https://api.meeting.qq.com/v1/meetings/{meetingId}/cancel
    String address;
//    创建会议uri
    String uri = "/v1/meetings/" + meetingId + "/cancel";
    address = MEETING_DOMAIN_URL + uri;
    try {
//    请求体,get方法请求体需传""
      String resultBody = "{\n"
          + "     \"meetingId\" : \"" + meetingId + "\",\n"
          + "     \"userid\" : \"" + userId + "\",\n"
          + "     \"instanceid\" : 1,\n"
          + "     \"reason_code\" : 1\n"
          + "}";

      String jsonStr = requestPost(address, uri, resultBody);
      JSONObject jsonObject = JSONObject.parseObject(jsonStr);
      if (null == jsonObject.getJSONObject("error_info")) {
        resultMap.put("message", "取消会议成功");
        resultMap.put("success", "true");
      } else {
        resultMap.put("message", "取消会议失败");
        resultMap.put("errorInfo", jsonObject.getJSONObject("error_info"));
        resultMap.put("success", "false");

      }
    } catch (Exception e) {
      resultMap.put("success", "false");
      resultMap.put("message", e.getMessage());
      log.error("取消会议异常:", e);
    }
    return resultMap;
  }

到此我的文章就结束了,希望可以帮助到大家,少走一些弯路。

大家还有一些不懂的地方,可以到腾讯云提交工单(https://console.cloud.tencent.com/workorder/category),找腾讯售后寻求技术支持,客服还是很负责任的。

首先,需要在腾讯云官网上申请 API 密钥,具体步骤如下: 1. 登录腾讯云控制台,进入 API 密钥管理页面。 2. 点击“新建密钥”按钮,生成 API 密钥。 3. 将密钥保存好,以便在 PHP 代码中调用。 接下来,可以使用 PHP CURL 函数调用腾讯云 API 接口,具体步骤如下: 1. 构造请求 URL,包括接口地址、请求参数、签名等信息; 2. 使用 CURL 函数发送请求,获取服务器响应; 3. 解析服务器响应,提取需要的信息。 下面是一个简单的 PHP 代码示例,演示如何调用腾讯云 API 接口: ``` <?php // 定义 API 地址和请求参数 $url = "https://api.qcloud.com/v2/index.php"; $params = array( "Action" => "DescribeInstances", "Nonce" => rand(), "Region" => "ap-guangzhou", "SecretId" => "your_secret_id", "SignatureMethod" => "HmacSHA256", "Timestamp" => time(), ); // 计算签名 ksort($params); $srcStr = "GET" . $url . "?" . http_build_query($params); $signStr = base64_encode(hash_hmac("sha256", $srcStr, "your_secret_key", true)); $params["Signature"] = $signStr; // 发送请求 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url . "?" . http_build_query($params)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); // 解析响应 $result = json_decode($response, true); print_r($result); ?> ``` 在上面的代码中,需要替换以下参数: - your_secret_id:替换为自己的 API 密钥 ID; - your_secret_key:替换为自己的 API 密钥 Key。 此外,还需要根据接口文档,调整请求参数和响应解析方式。
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值