|
|
@@ -0,0 +1,179 @@
|
|
|
+package com.malk.shunfeng.service.impl;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.malk.shunfeng.server.ZoomConf;
|
|
|
+import com.malk.shunfeng.server.ZoomR;
|
|
|
+import com.malk.shunfeng.service.ZoomClient;
|
|
|
+import com.malk.utils.UtilHttp;
|
|
|
+import com.malk.utils.UtilMap;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.util.Base64;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Zoom会议客户端实现
|
|
|
+ * - prd 使用Server-to-Server OAuth认证方式
|
|
|
+ * - fixme Token有效期为1小时,生产环境建议缓存Token
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class ZoomClientImpl implements ZoomClient {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ZoomConf zoomConf;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 缓存Token及过期时间
|
|
|
+ */
|
|
|
+ private String cachedToken;
|
|
|
+ private long tokenExpireTime;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取OAuth Access Token
|
|
|
+ * - fixme 使用Server-to-Server OAuth (Account Credentials) 方式
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public String getAccessToken() {
|
|
|
+ // ppExt Token缓存,避免频繁请求
|
|
|
+ if (cachedToken != null && System.currentTimeMillis() < tokenExpireTime) {
|
|
|
+ return cachedToken;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建Basic Auth: Base64(client_id:client_secret)
|
|
|
+ String credentials = zoomConf.getClientId() + ":" + zoomConf.getClientSecret();
|
|
|
+ String basicAuth = Base64.getEncoder().encodeToString(credentials.getBytes(StandardCharsets.UTF_8));
|
|
|
+
|
|
|
+ Map<String, String> header = new HashMap<>();
|
|
|
+ header.put("Authorization", "Basic " + basicAuth);
|
|
|
+ header.put("Content-Type", "application/x-www-form-urlencoded");
|
|
|
+
|
|
|
+ // Server-to-Server OAuth 请求参数 (form-urlencoded方式)
|
|
|
+ Map<String, Object> formParams = new HashMap<>();
|
|
|
+ formParams.put("grant_type", "account_credentials");
|
|
|
+ formParams.put("account_id", zoomConf.getAccountId());
|
|
|
+
|
|
|
+ // fixme 使用form方式提交,body传空Map避免方法重载歧义
|
|
|
+ String rsp = UtilHttp.doPost(zoomConf.getOauthUrl(), header, null, (Map) null, formParams);
|
|
|
+ JSONObject json = JSON.parseObject(rsp);
|
|
|
+
|
|
|
+ log.debug("Zoom OAuth响应: {}", rsp);
|
|
|
+
|
|
|
+ cachedToken = json.getString("access_token");
|
|
|
+ // fixme Token有效期通常为3600秒,提前5分钟刷新
|
|
|
+ int expiresIn = json.getIntValue("expires_in");
|
|
|
+ tokenExpireTime = System.currentTimeMillis() + (expiresIn - 300) * 1000L;
|
|
|
+
|
|
|
+ return cachedToken;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建请求头
|
|
|
+ */
|
|
|
+ private Map<String, String> buildHeader() {
|
|
|
+ Map<String, String> header = new HashMap<>();
|
|
|
+ header.put("Authorization", "Bearer " + getAccessToken());
|
|
|
+ header.put("Content-Type", "application/json");
|
|
|
+ return header;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建Zoom会议
|
|
|
+ * - prd POST /v2/users/{userId}/meetings
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public Map<String, Object> createMeeting(String userId, String topic, Integer type, String startTime,
|
|
|
+ Integer duration, String timezone, String password, Map<String, Object> settings) {
|
|
|
+ String url = zoomConf.getBaseUrl() + "/users/" + userId + "/meetings";
|
|
|
+
|
|
|
+ Map<String, Object> body = new HashMap<>();
|
|
|
+ body.put("topic", topic);
|
|
|
+ body.put("type", type);
|
|
|
+ body.put("start_time", startTime);
|
|
|
+ body.put("duration", duration);
|
|
|
+ body.put("timezone", timezone);
|
|
|
+ if (password != null) {
|
|
|
+ body.put("password", password);
|
|
|
+ }
|
|
|
+ if (settings != null) {
|
|
|
+ body.put("settings", settings);
|
|
|
+ }
|
|
|
+
|
|
|
+ ZoomR r = (ZoomR) UtilHttp.doPost(url, buildHeader(), null, body, ZoomR.class);
|
|
|
+ log.info("创建Zoom会议, topic={}, response={}", topic, JSON.toJSONString(r));
|
|
|
+
|
|
|
+ return UtilMap.map(
|
|
|
+ "id", r.getId(),
|
|
|
+ "topic", r.getTopic(),
|
|
|
+ "join_url", r.getJoin_url(),
|
|
|
+ "start_url", r.getStart_url(),
|
|
|
+ "password", r.getPassword(),
|
|
|
+ "start_time", r.getStart_time(),
|
|
|
+ "duration", r.getDuration()
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建Zoom会议 (使用默认用户)
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public Map<String, Object> createMeeting(String topic, Integer type, String startTime,
|
|
|
+ Integer duration, String timezone, String password, Map<String, Object> settings) {
|
|
|
+ return createMeeting(zoomConf.getDefaultUserId(), topic, type, startTime, duration, timezone, password, settings);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新Zoom会议
|
|
|
+ * - prd PATCH /v2/meetings/{meetingId}
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public Map<String, Object> updateMeeting(Long meetingId, Map<String, Object> params) {
|
|
|
+ String url = zoomConf.getBaseUrl() + "/meetings/" + meetingId;
|
|
|
+
|
|
|
+ String rsp = UtilHttp.doPatch(url, buildHeader(), null, params);
|
|
|
+ log.info("更新Zoom会议, meetingId={}, response={}", meetingId, rsp);
|
|
|
+
|
|
|
+ // fixme 更新成功返回204 No Content,返回查询结果
|
|
|
+ return getMeeting(meetingId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 删除Zoom会议
|
|
|
+ * - prd DELETE /v2/meetings/{meetingId}
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void deleteMeeting(Long meetingId) {
|
|
|
+ String url = zoomConf.getBaseUrl() + "/meetings/" + meetingId;
|
|
|
+
|
|
|
+ String rsp = UtilHttp.doDelete(url, buildHeader(), null, (Map) null);
|
|
|
+ log.info("删除Zoom会议, meetingId={}, response={}", meetingId, rsp);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询Zoom会议详情
|
|
|
+ * - prd GET /v2/meetings/{meetingId}
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public Map<String, Object> getMeeting(Long meetingId) {
|
|
|
+ String url = zoomConf.getBaseUrl() + "/meetings/" + meetingId;
|
|
|
+
|
|
|
+ ZoomR r = (ZoomR) UtilHttp.doGet(url, buildHeader(), null, ZoomR.class);
|
|
|
+ log.info("查询Zoom会议, meetingId={}, response={}", meetingId, JSON.toJSONString(r));
|
|
|
+
|
|
|
+ return UtilMap.map(
|
|
|
+ "id", r.getId(),
|
|
|
+ "topic", r.getTopic(),
|
|
|
+ "join_url", r.getJoin_url(),
|
|
|
+ "start_url", r.getStart_url(),
|
|
|
+ "password", r.getPassword(),
|
|
|
+ "start_time", r.getStart_time(),
|
|
|
+ "duration", r.getDuration(),
|
|
|
+ "timezone", r.getTimezone()
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|