!84 czh-20260717-修复-测试接口补充linkTitle参数
* czh-20260717-修复-测试接口补充linkTitle参数 * czh-20260717-新增-sendMessage增加linkTitle参数 * czh-20260717-新增-sendMessage增加data参数支持 * czh-20260717-优化-去掉bizId和createDatetime字段 * czh-20260717-修复system-start缺少jeecg-module-im依赖 * czh-20260717-修复测试接口SSRF和日志泄漏问题 * czh-20260717-新增即时通消息推送模块及测试接口
This commit is contained in:
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>jeecg-boot-module</artifactId>
|
||||||
|
<groupId>org.jeecgframework.boot</groupId>
|
||||||
|
<version>${jeecgProjectVersion}</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>jeecg-module-im</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jeecgframework.boot</groupId>
|
||||||
|
<artifactId>jeecg-boot-base-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
+74
@@ -0,0 +1,74 @@
|
|||||||
|
package org.jeecg.modules.ksim.client;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jeecg.common.util.RestUtil;
|
||||||
|
import org.jeecg.modules.ksim.config.KsimProperties;
|
||||||
|
import org.jeecg.modules.ksim.dto.KsimApiResult;
|
||||||
|
import org.jeecg.modules.ksim.dto.KsimMsgSendDTO;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class KsimApiClient {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private KsimProperties ksimProperties;
|
||||||
|
|
||||||
|
private HttpHeaders jsonHeaders() {
|
||||||
|
return RestUtil.getHeaderApplicationJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONObject createToken(String appId, String appSecret, int timeoutSeconds) {
|
||||||
|
JSONObject body = new JSONObject();
|
||||||
|
body.put("appId", appId);
|
||||||
|
body.put("appSecret", appSecret);
|
||||||
|
body.put("tokenTimeoutSeconds", timeoutSeconds);
|
||||||
|
|
||||||
|
String url = ksimProperties.getHost() + "/api-open/v1/token/create";
|
||||||
|
ResponseEntity<JSONObject> resp = RestUtil.request(url, HttpMethod.POST, jsonHeaders(), null, body, JSONObject.class);
|
||||||
|
|
||||||
|
log.info("【即时通推送-Token创建】请求完成, appId: {}, 获取Token: {}",
|
||||||
|
appId, resp.getBody() != null && resp.getBody().getJSONObject("dataBody") != null ? "成功" : "失败");
|
||||||
|
return resp.getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONObject refreshToken(String oldToken, int timeoutSeconds) {
|
||||||
|
JSONObject body = new JSONObject();
|
||||||
|
body.put("tokenTimeoutSeconds", timeoutSeconds);
|
||||||
|
|
||||||
|
HttpHeaders headers = jsonHeaders();
|
||||||
|
headers.add("imOpenApiToken", oldToken);
|
||||||
|
|
||||||
|
String url = ksimProperties.getHost() + "/api-open/v1/token/refresh";
|
||||||
|
ResponseEntity<JSONObject> resp = RestUtil.request(url, HttpMethod.POST, headers, null, body, JSONObject.class);
|
||||||
|
|
||||||
|
log.info("【即时通推送-Token刷新】请求完成, 获取Token: {}",
|
||||||
|
resp.getBody() != null && resp.getBody().getJSONObject("dataBody") != null ? "成功" : "失败");
|
||||||
|
return resp.getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
public KsimApiResult<JSONObject> sendMsg(String token, KsimMsgSendDTO dto) {
|
||||||
|
HttpHeaders headers = jsonHeaders();
|
||||||
|
headers.add("imOpenApiToken", token);
|
||||||
|
|
||||||
|
String bodyJson = JSON.toJSONString(dto);
|
||||||
|
String url = ksimProperties.getHost() + "/api-open/v1/msg/send";
|
||||||
|
|
||||||
|
ResponseEntity<JSONObject> resp = RestUtil.request(url, HttpMethod.POST, headers, null,
|
||||||
|
JSON.parseObject(bodyJson), JSONObject.class);
|
||||||
|
|
||||||
|
JSONObject respBody = resp.getBody();
|
||||||
|
int code = respBody != null ? respBody.getIntValue("code") : -1;
|
||||||
|
log.info("【即时通推送-消息发送】请求完成, toUser: {}, title: {}, code: {}",
|
||||||
|
dto.getToUserValue(), dto.getTitle(), code);
|
||||||
|
|
||||||
|
return respBody != null ? respBody.toJavaObject(KsimApiResult.class) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
package org.jeecg.modules.ksim.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "ksim")
|
||||||
|
public class KsimProperties {
|
||||||
|
|
||||||
|
/** 即时通服务地址,如 http://10.2.0.54:19010 */
|
||||||
|
private String host = "http://10.2.0.54:19010";
|
||||||
|
|
||||||
|
/** 应用ID,在 application-{env}.yml 中配置 */
|
||||||
|
private String appId;
|
||||||
|
|
||||||
|
/** 应用密钥,在 application-{env}.yml 中配置 */
|
||||||
|
private String appSecret;
|
||||||
|
|
||||||
|
/** Token 有效期(秒),默认 7200(2小时) */
|
||||||
|
private int tokenTimeoutSeconds = 7200;
|
||||||
|
|
||||||
|
public String getHost() { return host; }
|
||||||
|
public void setHost(String host) { this.host = host; }
|
||||||
|
public String getAppId() { return appId; }
|
||||||
|
public void setAppId(String appId) { this.appId = appId; }
|
||||||
|
public String getAppSecret() { return appSecret; }
|
||||||
|
public void setAppSecret(String appSecret) { this.appSecret = appSecret; }
|
||||||
|
public int getTokenTimeoutSeconds() { return tokenTimeoutSeconds; }
|
||||||
|
public void setTokenTimeoutSeconds(int tokenTimeoutSeconds) { this.tokenTimeoutSeconds = tokenTimeoutSeconds; }
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package org.jeecg.modules.ksim.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableScheduling
|
||||||
|
public class KsimScheduleConfig {
|
||||||
|
}
|
||||||
+127
@@ -0,0 +1,127 @@
|
|||||||
|
package org.jeecg.modules.ksim.controller;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jeecg.common.api.vo.Result;
|
||||||
|
import org.jeecg.common.util.RestUtil;
|
||||||
|
import org.jeecg.modules.ksim.dto.KsimMsgSendDTO;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@Profile("dev")
|
||||||
|
@RequestMapping("/ksim/test")
|
||||||
|
public class KsimTestController {
|
||||||
|
|
||||||
|
@PostMapping("/send")
|
||||||
|
public Result<Map<String, Object>> send(@RequestBody Map<String, Object> params) {
|
||||||
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
|
Map<String, Object> requestInfo = new LinkedHashMap<>();
|
||||||
|
result.put("request", requestInfo);
|
||||||
|
|
||||||
|
try {
|
||||||
|
String host = (String) params.getOrDefault("host", "");
|
||||||
|
String appId = (String) params.getOrDefault("appId", "");
|
||||||
|
String appSecret = (String) params.getOrDefault("appSecret", "");
|
||||||
|
String toUserKeyType = (String) params.getOrDefault("toUserKeyType", "");
|
||||||
|
String toUserValue = (String) params.getOrDefault("toUserValue", "");
|
||||||
|
String title = (String) params.getOrDefault("title", "");
|
||||||
|
String content = (String) params.getOrDefault("content", "");
|
||||||
|
String linkUrl = (String) params.getOrDefault("linkUrl", "");
|
||||||
|
String linkTitle = (String) params.getOrDefault("linkTitle", "");
|
||||||
|
Object data = params.get("data");
|
||||||
|
|
||||||
|
// --- Step 1: 创建 Token ---
|
||||||
|
JSONObject tokenBody = new JSONObject();
|
||||||
|
tokenBody.put("appId", appId);
|
||||||
|
tokenBody.put("appSecret", appSecret);
|
||||||
|
tokenBody.put("tokenTimeoutSeconds", 7200);
|
||||||
|
|
||||||
|
String tokenUrl = host + "/api-open/v1/token/create";
|
||||||
|
requestInfo.put("tokenRequestUrl", tokenUrl);
|
||||||
|
requestInfo.put("tokenRequestBody", tokenBody);
|
||||||
|
|
||||||
|
ResponseEntity<JSONObject> tokenResp = RestUtil.request(
|
||||||
|
tokenUrl, HttpMethod.POST, jsonHeaders(), null, tokenBody, JSONObject.class);
|
||||||
|
|
||||||
|
JSONObject tokenRespBody = tokenResp.getBody();
|
||||||
|
result.put("tokenResponse", tokenRespBody);
|
||||||
|
log.info("【即时通测试-Token创建】完成, 成功: {}",
|
||||||
|
tokenRespBody != null && tokenRespBody.getJSONObject("dataBody") != null);
|
||||||
|
|
||||||
|
if (tokenRespBody == null || tokenRespBody.getJSONObject("dataBody") == null) {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("errorMessage", "Token 创建失败:响应为空");
|
||||||
|
return Result.OK(result);
|
||||||
|
}
|
||||||
|
String token = tokenRespBody.getJSONObject("dataBody").getString("token");
|
||||||
|
|
||||||
|
// --- Step 2: 发送消息 ---
|
||||||
|
KsimMsgSendDTO dto = new KsimMsgSendDTO();
|
||||||
|
dto.setToUserKeyType(toUserKeyType);
|
||||||
|
dto.setToUserValue(toUserValue);
|
||||||
|
dto.setTitle(title);
|
||||||
|
dto.setContent(content);
|
||||||
|
if (data != null) {
|
||||||
|
dto.setData(data);
|
||||||
|
}
|
||||||
|
if (linkTitle != null && !linkTitle.isEmpty()) {
|
||||||
|
dto.setLinkTitle(linkTitle);
|
||||||
|
}
|
||||||
|
if (linkUrl != null && !linkUrl.isEmpty()) {
|
||||||
|
dto.setLinkUrl(linkUrl);
|
||||||
|
dto.setLinkOpenMode("Web");
|
||||||
|
}
|
||||||
|
|
||||||
|
String msgUrl = host + "/api-open/v1/msg/send";
|
||||||
|
requestInfo.put("msgRequestUrl", msgUrl);
|
||||||
|
requestInfo.put("msgRequestBody", dto);
|
||||||
|
|
||||||
|
HttpHeaders msgHeaders = jsonHeaders();
|
||||||
|
msgHeaders.add("imOpenApiToken", token);
|
||||||
|
|
||||||
|
ResponseEntity<JSONObject> msgResp = RestUtil.request(
|
||||||
|
msgUrl, HttpMethod.POST, msgHeaders, null,
|
||||||
|
JSON.parseObject(JSON.toJSONString(dto)), JSONObject.class);
|
||||||
|
|
||||||
|
JSONObject msgRespBody = msgResp.getBody();
|
||||||
|
result.put("msgResponse", msgRespBody);
|
||||||
|
log.info("【即时通测试-消息发送】完成, code: {}",
|
||||||
|
msgRespBody != null ? msgRespBody.getIntValue("code") : -1);
|
||||||
|
|
||||||
|
if (msgRespBody != null) {
|
||||||
|
int code = msgRespBody.getIntValue("code");
|
||||||
|
result.put("success", code == 0 || code == 200);
|
||||||
|
if (msgRespBody.getJSONObject("dataBody") != null) {
|
||||||
|
result.put("messageId", msgRespBody.getJSONObject("dataBody").getString("messageId"));
|
||||||
|
}
|
||||||
|
result.put("responseCode", code);
|
||||||
|
} else {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("errorMessage", "消息发送失败:响应为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("【即时通测试-发送】异常, error: {}", e.getMessage(), e);
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("errorMessage", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.OK(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpHeaders jsonHeaders() {
|
||||||
|
return RestUtil.getHeaderApplicationJson();
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
package org.jeecg.modules.ksim.dto;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.annotation.JSONField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 即时通 API 通用响应体
|
||||||
|
*/
|
||||||
|
public class KsimApiResult<T> {
|
||||||
|
|
||||||
|
private int code;
|
||||||
|
private String message;
|
||||||
|
private String timestamp;
|
||||||
|
private String requestId;
|
||||||
|
|
||||||
|
@JSONField(name = "dataBody")
|
||||||
|
private T dataBody;
|
||||||
|
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return code == 0 || code == 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() { return code; }
|
||||||
|
public void setCode(int code) { this.code = code; }
|
||||||
|
public String getMessage() { return message; }
|
||||||
|
public void setMessage(String message) { this.message = message; }
|
||||||
|
public String getTimestamp() { return timestamp; }
|
||||||
|
public void setTimestamp(String timestamp) { this.timestamp = timestamp; }
|
||||||
|
public String getRequestId() { return requestId; }
|
||||||
|
public void setRequestId(String requestId) { this.requestId = requestId; }
|
||||||
|
public T getDataBody() { return dataBody; }
|
||||||
|
public void setDataBody(T dataBody) { this.dataBody = dataBody; }
|
||||||
|
}
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
package org.jeecg.modules.ksim.dto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对应即时通文档 3.4.3 ReqMsgSendDTO
|
||||||
|
*/
|
||||||
|
public class KsimMsgSendDTO {
|
||||||
|
|
||||||
|
private String title;
|
||||||
|
private String content;
|
||||||
|
private String toUserKeyType;
|
||||||
|
private String toUserValue;
|
||||||
|
private Object data;
|
||||||
|
private String dataFormat;
|
||||||
|
private String linkOpenMode;
|
||||||
|
private String linkTitle;
|
||||||
|
private String linkUrl;
|
||||||
|
|
||||||
|
public String getTitle() { return title; }
|
||||||
|
public void setTitle(String title) { this.title = title; }
|
||||||
|
public String getContent() { return content; }
|
||||||
|
public void setContent(String content) { this.content = content; }
|
||||||
|
public String getToUserKeyType() { return toUserKeyType; }
|
||||||
|
public void setToUserKeyType(String toUserKeyType) { this.toUserKeyType = toUserKeyType; }
|
||||||
|
public String getToUserValue() { return toUserValue; }
|
||||||
|
public void setToUserValue(String toUserValue) { this.toUserValue = toUserValue; }
|
||||||
|
public Object getData() { return data; }
|
||||||
|
public void setData(Object data) { this.data = data; }
|
||||||
|
public String getDataFormat() { return dataFormat; }
|
||||||
|
public void setDataFormat(String dataFormat) { this.dataFormat = dataFormat; }
|
||||||
|
public String getLinkOpenMode() { return linkOpenMode; }
|
||||||
|
public void setLinkOpenMode(String linkOpenMode) { this.linkOpenMode = linkOpenMode; }
|
||||||
|
public String getLinkTitle() { return linkTitle; }
|
||||||
|
public void setLinkTitle(String linkTitle) { this.linkTitle = linkTitle; }
|
||||||
|
public String getLinkUrl() { return linkUrl; }
|
||||||
|
public void setLinkUrl(String linkUrl) { this.linkUrl = linkUrl; }
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
package org.jeecg.modules.ksim;
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
package org.jeecg.modules.ksim.service;
|
||||||
|
|
||||||
|
import org.jeecg.common.api.vo.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 即时通消息推送服务(对外唯一入口)
|
||||||
|
*/
|
||||||
|
public interface IKsimMessageService {
|
||||||
|
|
||||||
|
Result<String> sendMessage(String toUserKeyType, String toUserValue,
|
||||||
|
String title, String content,
|
||||||
|
String linkUrl, String linkTitle,
|
||||||
|
Object data);
|
||||||
|
}
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
package org.jeecg.modules.ksim.service.impl;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jeecg.common.api.vo.Result;
|
||||||
|
import org.jeecg.modules.ksim.client.KsimApiClient;
|
||||||
|
import org.jeecg.modules.ksim.dto.KsimApiResult;
|
||||||
|
import org.jeecg.modules.ksim.dto.KsimMsgSendDTO;
|
||||||
|
import org.jeecg.modules.ksim.service.IKsimMessageService;
|
||||||
|
import org.jeecg.modules.ksim.token.KsimTokenManager;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class KsimMessageServiceImpl implements IKsimMessageService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private KsimTokenManager tokenManager;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private KsimApiClient apiClient;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<String> sendMessage(String toUserKeyType, String toUserValue,
|
||||||
|
String title, String content,
|
||||||
|
String linkUrl, String linkTitle,
|
||||||
|
Object data) {
|
||||||
|
String token = tokenManager.getToken();
|
||||||
|
|
||||||
|
KsimMsgSendDTO dto = new KsimMsgSendDTO();
|
||||||
|
dto.setToUserKeyType(toUserKeyType);
|
||||||
|
dto.setToUserValue(toUserValue);
|
||||||
|
dto.setTitle(title);
|
||||||
|
dto.setContent(content);
|
||||||
|
dto.setData(data);
|
||||||
|
if (linkTitle != null && !linkTitle.isEmpty()) {
|
||||||
|
dto.setLinkTitle(linkTitle);
|
||||||
|
}
|
||||||
|
if (linkUrl != null && !linkUrl.isEmpty()) {
|
||||||
|
dto.setLinkUrl(linkUrl);
|
||||||
|
dto.setLinkOpenMode("Web");
|
||||||
|
}
|
||||||
|
|
||||||
|
KsimApiResult<JSONObject> apiResult = apiClient.sendMsg(token, dto);
|
||||||
|
if (apiResult == null) {
|
||||||
|
log.error("【即时通推送-消息发送】响应为空, toUser: {}, title: {}", toUserValue, title);
|
||||||
|
return Result.error("即时通响应为空");
|
||||||
|
}
|
||||||
|
if (!apiResult.isSuccess()) {
|
||||||
|
log.error("【即时通推送-消息发送】发送失败, code: {}, message: {}, toUser: {}, title: {}",
|
||||||
|
apiResult.getCode(), apiResult.getMessage(), toUserValue, title);
|
||||||
|
return Result.error(apiResult.getCode(), "即时通发送失败: " + apiResult.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
String messageId = apiResult.getDataBody() != null
|
||||||
|
? apiResult.getDataBody().getString("messageId") : "";
|
||||||
|
log.info("【即时通推送-消息发送】成功, messageId: {}, toUser: {}, title: {}", messageId, toUserValue, title);
|
||||||
|
return Result.OK(messageId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+80
@@ -0,0 +1,80 @@
|
|||||||
|
package org.jeecg.modules.ksim.token;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jeecg.common.exception.JeecgBootException;
|
||||||
|
import org.jeecg.modules.ksim.client.KsimApiClient;
|
||||||
|
import org.jeecg.modules.ksim.config.KsimProperties;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class KsimTokenManager {
|
||||||
|
|
||||||
|
private static final String TOKEN_KEY = "ksim:token";
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private KsimProperties ksimProperties;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private KsimApiClient ksimApiClient;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
|
public String getToken() {
|
||||||
|
Object cached = redisTemplate.opsForValue().get(TOKEN_KEY);
|
||||||
|
if (cached != null) {
|
||||||
|
return cached.toString();
|
||||||
|
}
|
||||||
|
return createAndCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Scheduled(cron = "0 0 * * * ?")
|
||||||
|
public void refreshTask() {
|
||||||
|
log.info("【即时通推送-Token定时刷新】开始");
|
||||||
|
try {
|
||||||
|
Object old = redisTemplate.opsForValue().get(TOKEN_KEY);
|
||||||
|
JSONObject result;
|
||||||
|
int timeout = ksimProperties.getTokenTimeoutSeconds();
|
||||||
|
if (old != null) {
|
||||||
|
try {
|
||||||
|
result = ksimApiClient.refreshToken(old.toString(), timeout);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("【即时通推送-Token定时刷新】刷新失败,回退到创建, error: {}", e.getMessage());
|
||||||
|
result = ksimApiClient.createToken(ksimProperties.getAppId(), ksimProperties.getAppSecret(), timeout);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = ksimApiClient.createToken(ksimProperties.getAppId(), ksimProperties.getAppSecret(), timeout);
|
||||||
|
}
|
||||||
|
cacheToken(result, timeout);
|
||||||
|
log.info("【即时通推送-Token定时刷新】完成");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("【即时通推送-Token定时刷新】失败, error: {}", e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createAndCache() {
|
||||||
|
log.info("【即时通推送-Token懒加载】创建新Token");
|
||||||
|
JSONObject result = ksimApiClient.createToken(
|
||||||
|
ksimProperties.getAppId(), ksimProperties.getAppSecret(), ksimProperties.getTokenTimeoutSeconds());
|
||||||
|
return cacheToken(result, ksimProperties.getTokenTimeoutSeconds());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String cacheToken(JSONObject result, int timeoutSeconds) {
|
||||||
|
if (result == null || result.getJSONObject("dataBody") == null) {
|
||||||
|
throw new JeecgBootException("【即时通推送】Token创建/刷新返回为空");
|
||||||
|
}
|
||||||
|
JSONObject dataBody = result.getJSONObject("dataBody");
|
||||||
|
String token = dataBody.getString("token");
|
||||||
|
int ttl = Math.max(timeoutSeconds - 60, 60);
|
||||||
|
redisTemplate.opsForValue().set(TOKEN_KEY, token, ttl, TimeUnit.SECONDS);
|
||||||
|
log.info("【即时通推送-Token缓存】已写入Redis, ttl: {}s", ttl);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
<module>jeecg-boot-module-airag</module>
|
<module>jeecg-boot-module-airag</module>
|
||||||
<module>jeecg-module-flow</module>
|
<module>jeecg-module-flow</module>
|
||||||
<module>jeecg-module-tools</module>
|
<module>jeecg-module-tools</module>
|
||||||
|
<module>jeecg-module-im</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,12 @@
|
|||||||
<groupId>org.jeecgframework.boot</groupId>
|
<groupId>org.jeecgframework.boot</groupId>
|
||||||
<artifactId>jeecg-module-supervision</artifactId>
|
<artifactId>jeecg-module-supervision</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- 即时通消息推送模块 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jeecgframework.boot</groupId>
|
||||||
|
<artifactId>jeecg-module-im</artifactId>
|
||||||
|
<version>${jeecgProjectVersion}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jeecgframework.boot</groupId>
|
<groupId>org.jeecgframework.boot</groupId>
|
||||||
<artifactId>jeecg-module-flow</artifactId>
|
<artifactId>jeecg-module-flow</artifactId>
|
||||||
|
|||||||
Reference in New Issue
Block a user