diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/pom.xml b/pjcloud-thirdparty/pjcloud-thirdparty-biz/pom.xml
index cb431bbcf..81f09a7fa 100644
--- a/pjcloud-thirdparty/pjcloud-thirdparty-biz/pom.xml
+++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/pom.xml
@@ -155,6 +155,19 @@
5.7.0
compile
+
+
+ com.alibaba.xxpt
+ zwdd-sdk-java
+ 1.2.0
+ system
+ ${project.basedir}/src/lib/zwdd-sdk-java-1.2.0.jar
+
+
+ joda-time
+ joda-time
+ 2.10
+
@@ -190,6 +203,7 @@
CLASSIC
+ true
diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/lib/zwdd-sdk-java-1.2.0.jar b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/lib/zwdd-sdk-java-1.2.0.jar
new file mode 100644
index 000000000..497940666
Binary files /dev/null and b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/lib/zwdd-sdk-java-1.2.0.jar differ
diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/ZzdOpenPlatformExecutableClientHolder.java b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/ZzdOpenPlatformExecutableClientHolder.java
new file mode 100644
index 000000000..dbe3a8726
--- /dev/null
+++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/ZzdOpenPlatformExecutableClientHolder.java
@@ -0,0 +1,41 @@
+package com.bsd.pjcloud.thirdparty.config;
+
+import com.alibaba.xxpt.gateway.shared.client.http.ExecutableClient;
+import com.bsd.pjcloud.thirdparty.exception.ThirdPartyException;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+/**
+ * 政务钉钉开放平台 {@link ExecutableClient} 单例初始化(与官方 SDK 文档一致,全局只 init 一次)。
+ */
+@Component
+@RequiredArgsConstructor
+public class ZzdOpenPlatformExecutableClientHolder {
+
+ private final ZzdOpenPlatformProperties zzdOpenPlatform;
+
+ private final Object initLock = new Object();
+
+ private volatile boolean initialized;
+
+ public ExecutableClient client() {
+ if (!zzdOpenPlatform.isEnabled() || !zzdOpenPlatform.isAppCredentialConfigured()) {
+ throw new ThirdPartyException("浙政钉开放平台未启用或未配置 appKey/appSecret", "ZZD_OPEN_SDK_CFG");
+ }
+ if (!initialized) {
+ synchronized (initLock) {
+ if (!initialized) {
+ ExecutableClient c = ExecutableClient.getInstance();
+ c.setDomainName(zzdOpenPlatform.getDomain());
+ c.setProtocal(zzdOpenPlatform.getProtocol());
+ c.setAccessKey(zzdOpenPlatform.getAppKey());
+ c.setSecretKey(zzdOpenPlatform.getAppSecret());
+ c.setValidateAfterInactivity(60_000);
+ c.init();
+ initialized = true;
+ }
+ }
+ }
+ return ExecutableClient.getInstance();
+ }
+}
diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/ZzdOpenPlatformProperties.java b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/ZzdOpenPlatformProperties.java
index 459b0901f..530eb1914 100644
--- a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/ZzdOpenPlatformProperties.java
+++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/ZzdOpenPlatformProperties.java
@@ -32,6 +32,11 @@ public class ZzdOpenPlatformProperties {
private String appSecret = "";
+ /**
+ * H5 微应用 JSAPI 鉴权用 AgentId(管理后台应用详情,非 appKey)
+ */
+ private String jsapiAgentId = "";
+
public boolean isAppCredentialConfigured() {
return StringUtils.hasText(appKey) && StringUtils.hasText(appSecret);
}
diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/controller/ThirdPartyOAth2Cotroller.java b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/controller/ThirdPartyOAth2Cotroller.java
index db7de5420..d6cc79d4b 100644
--- a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/controller/ThirdPartyOAth2Cotroller.java
+++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/controller/ThirdPartyOAth2Cotroller.java
@@ -17,6 +17,8 @@ import com.bsd.pjcloud.common.log.annotation.SysLog;
import com.bsd.pjcloud.common.security.annotation.Inner;
import com.bsd.pjcloud.thirdparty.dto.*;
import com.bsd.pjcloud.thirdparty.service.SmsService;
+import com.bsd.pjcloud.thirdparty.exception.ThirdPartyException;
+import com.bsd.pjcloud.thirdparty.vo.ZzdJsapiConfigVO;
import com.bsd.pjcloud.thirdparty.service.ThirdPartyOAuth2Service;
import com.bsd.pjcloud.thirdparty.service.ThirdPartyUserService;
import com.bsd.pjcloud.thirdparty.utils.SmsCodeUtil;
@@ -87,6 +89,17 @@ public class ThirdPartyOAth2Cotroller {
* @param token 浙政钉前端获取的token
* @return
*/
+ @GetMapping("/zzdJsapiConfig")
+ @Operation(summary = "政务钉钉H5 JSAPI鉴权(dd.authConfig 所需 ticket)")
+ @Inner(value = false)
+ public R zzdJsapiConfig() {
+ try {
+ return R.ok(thirdPartyOAuth2Service.buildZzdJsapiConfigForH5());
+ } catch (ThirdPartyException e) {
+ return R.failed(e.getMessage());
+ }
+ }
+
@PostMapping("/zzdsso")
@Operation(summary = "浙政钉单点登录")
@SysLog("浙政钉单点登录")
diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/ThirdPartyOAuth2Service.java b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/ThirdPartyOAuth2Service.java
index 7e40f6d55..853a4992f 100644
--- a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/ThirdPartyOAuth2Service.java
+++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/ThirdPartyOAuth2Service.java
@@ -4,7 +4,19 @@ import com.alibaba.fastjson.JSON;
import com.bsd.pjcloud.admin.api.entity.SysUser;
import com.bsd.pjcloud.common.core.util.R;
import com.alibaba.fastjson.JSONObject;
+import com.alibaba.xxpt.gateway.shared.api.request.OapiGetJsapiTokenJsonRequest;
+import com.alibaba.xxpt.gateway.shared.api.request.OapiGettokenJsonRequest;
+import com.alibaba.xxpt.gateway.shared.api.request.OapiRpcOauth2DingtalkAppUserJsonRequest;
+import com.alibaba.xxpt.gateway.shared.api.response.OapiGetJsapiTokenJsonResponse;
+import com.alibaba.xxpt.gateway.shared.api.response.OapiGettokenJsonResponse;
+import com.alibaba.xxpt.gateway.shared.api.response.OapiRpcOauth2DingtalkAppUserJsonResponse;
+import com.alibaba.xxpt.gateway.shared.client.http.ExecutableClient;
+import com.alibaba.xxpt.gateway.shared.client.http.IntelligentGetClient;
+import com.alibaba.xxpt.gateway.shared.client.http.IntelligentPostClient;
+import com.alibaba.xxpt.gateway.shared.client.http.api.OapiResponse;
+import com.alibaba.xxpt.gateway.shared.client.http.api.OapiSpResultContent;
import com.bsd.pjcloud.thirdparty.config.OAuth2ClientConfig;
+import com.bsd.pjcloud.thirdparty.config.ZzdOpenPlatformExecutableClientHolder;
import com.bsd.pjcloud.thirdparty.config.ZzdOpenPlatformProperties;
import com.bsd.pjcloud.thirdparty.constant.UserTypeEnum;
import com.bsd.pjcloud.thirdparty.dto.*;
@@ -12,6 +24,7 @@ import com.bsd.pjcloud.thirdparty.entity.ThirdPartyDeptEntity;
import com.bsd.pjcloud.thirdparty.entity.ThirdPartyLogEntity;
import com.bsd.pjcloud.thirdparty.entity.ThirdPartyUserEntity;
import com.bsd.pjcloud.thirdparty.exception.ThirdPartyException;
+import com.bsd.pjcloud.thirdparty.vo.ZzdJsapiConfigVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
@@ -48,6 +61,12 @@ public class ThirdPartyOAuth2Service {
private volatile String zzdOpenCachedAppAccessToken;
private volatile long zzdOpenCachedAppAccessTokenExpireMs;
+ private final Object zzdJsapiTicketLock = new Object();
+
+ private volatile String zzdOpenCachedJsapiTicket;
+
+ private volatile long zzdOpenCachedJsapiTicketExpireMs;
+
@Autowired
private OAuth2ClientConfig config;
private final String baseUrl = "http://192.168.93.121:8880";
@@ -65,6 +84,8 @@ public class ThirdPartyOAuth2Service {
private final ZzdOpenPlatformProperties zzdOpenPlatform;
+ private final ZzdOpenPlatformExecutableClientHolder zzdExecutableClientHolder;
+
public TokenDTO getAccessToken() {
TokenDTO cachedToken = tokenCache.get("access_token");
@@ -390,12 +411,8 @@ public class ThirdPartyOAuth2Service {
return getZZDUserInfo(authCode, UserTypeEnum.ZZD.getType(), true);
}
- private String zzdOpenBaseUrl() {
- return zzdOpenPlatform.getProtocol() + "://" + zzdOpenPlatform.getDomain();
- }
-
/**
- * 开放平台文档:POST /gettoken.json,表单 appkey、appsecret;返回 content.data.accessToken
+ * 开放平台:官方 SDK {@code IntelligentGetClient /gettoken.json},解析 content.data 中 accessToken
*/
private String getZzdOpenAppAccessToken() {
long now = System.currentTimeMillis();
@@ -407,97 +424,111 @@ public class ThirdPartyOAuth2Service {
if (zzdOpenCachedAppAccessToken != null && now < zzdOpenCachedAppAccessTokenExpireMs - 60_000L) {
return zzdOpenCachedAppAccessToken;
}
- String url = zzdOpenBaseUrl() + "/gettoken.json";
- HttpHeaders headers = new HttpHeaders();
- headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
- MultiValueMap body = new LinkedMultiValueMap<>();
- body.add("appkey", zzdOpenPlatform.getAppKey());
- body.add("appsecret", zzdOpenPlatform.getAppSecret());
- HttpEntity> entity = new HttpEntity<>(body, headers);
- ResponseEntity response;
try {
- response = restTemplate.postForEntity(url, entity, String.class);
- } catch (HttpStatusCodeException e) {
- log.error("浙政钉 gettoken HTTP异常 状态:{} 响应:{}", e.getRawStatusCode(), e.getResponseBodyAsString());
- throw new ThirdPartyException("浙政钉获取应用 access_token 失败", "ZZD_OPEN_TOKEN_HTTP");
- } catch (ResourceAccessException e) {
- log.error("浙政钉 gettoken 网络异常", e);
- throw new ThirdPartyException("浙政钉开放平台暂不可用", "ZZD_OPEN_TOKEN_NET");
- }
- String respBody = response.getBody();
- JSONObject data = parseZzdOpenContentData(respBody, url);
- String accessToken = data.getString("accessToken");
- if (!StringUtils.hasText(accessToken)) {
- throw new ThirdPartyException("浙政钉 gettoken 未返回 accessToken", "ZZD_OPEN_TOKEN_BAD");
- }
- int expiresIn = 7100;
- try {
- Object ex = data.get("expiresIn");
- if (ex != null) {
- expiresIn = Integer.parseInt(ex.toString());
+ ExecutableClient ec = zzdExecutableClientHolder.client();
+ IntelligentGetClient getClient = ec.newIntelligentGetClient("/gettoken.json");
+ OapiGettokenJsonRequest req = new OapiGettokenJsonRequest();
+ req.setAppkey(zzdOpenPlatform.getAppKey());
+ req.setAppsecret(zzdOpenPlatform.getAppSecret());
+ OapiGettokenJsonResponse resp = getClient.get(req);
+ zzdAssertSdkTopSuccess(resp, "gettoken");
+ JSONObject data = zzdSdkSpResultData(resp.getContent(), "gettoken");
+ String accessToken = data.getString("accessToken");
+ if (!StringUtils.hasText(accessToken)) {
+ throw new ThirdPartyException("浙政钉 gettoken 未返回 accessToken", "ZZD_OPEN_TOKEN_BAD");
}
- } catch (NumberFormatException ignored) {
+ int expiresIn = 7100;
+ try {
+ Object ex = data.get("expiresIn");
+ if (ex != null) {
+ expiresIn = Integer.parseInt(ex.toString());
+ }
+ } catch (NumberFormatException ignored) {
+ }
+ zzdOpenCachedAppAccessToken = accessToken;
+ zzdOpenCachedAppAccessTokenExpireMs = System.currentTimeMillis() + expiresIn * 1000L;
+ return accessToken;
+ } catch (ThirdPartyException e) {
+ throw e;
+ } catch (Exception e) {
+ log.error("浙政钉 gettoken SDK 调用异常", e);
+ throw new ThirdPartyException("浙政钉获取应用 access_token 失败", "ZZD_OPEN_TOKEN_SDK");
}
- zzdOpenCachedAppAccessToken = accessToken;
- zzdOpenCachedAppAccessTokenExpireMs = System.currentTimeMillis() + expiresIn * 1000L;
- return accessToken;
}
}
/**
- * POST /rpc/oauth2/dingtalk_app_user.json,表单 access_token、auth_code
+ * 官方 SDK {@code IntelligentPostClient /rpc/oauth2/dingtalk_app_user.json}
*/
private ZZDUserDTO getZzdUserByOpenPlatformAuthCode(String authCode) {
- String appToken = getZzdOpenAppAccessToken();
- String url = zzdOpenBaseUrl() + "/rpc/oauth2/dingtalk_app_user.json";
- HttpHeaders headers = new HttpHeaders();
- headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
- MultiValueMap body = new LinkedMultiValueMap<>();
- body.add("access_token", appToken);
- body.add("auth_code", authCode);
- HttpEntity> entity = new HttpEntity<>(body, headers);
- ResponseEntity response;
try {
- response = restTemplate.postForEntity(url, entity, String.class);
- } catch (HttpStatusCodeException e) {
- log.error("浙政钉 dingtalk_app_user HTTP异常 状态:{} 响应:{}", e.getRawStatusCode(), e.getResponseBodyAsString());
- throw new ThirdPartyException("浙政钉获取用户失败", "ZZD_OPEN_USER_HTTP");
- } catch (ResourceAccessException e) {
- log.error("浙政钉 dingtalk_app_user 网络异常", e);
- throw new ThirdPartyException("浙政钉开放平台暂不可用", "ZZD_OPEN_USER_NET");
+ String appToken = getZzdOpenAppAccessToken();
+ ExecutableClient ec = zzdExecutableClientHolder.client();
+ IntelligentPostClient postClient = ec.newIntelligentPostClient("/rpc/oauth2/dingtalk_app_user.json");
+ OapiRpcOauth2DingtalkAppUserJsonRequest req = new OapiRpcOauth2DingtalkAppUserJsonRequest();
+ req.setAccess_token(appToken);
+ req.setAuth_code(authCode);
+ OapiRpcOauth2DingtalkAppUserJsonResponse resp = postClient.post(req);
+ zzdAssertSdkTopSuccess(resp, "dingtalk_app_user");
+ JSONObject data = zzdSdkDingtalkAppUserContentData(resp.getContent());
+ ZZDUserDTO dto = mapOpenPlatformUserToZzdDto(data);
+ if (StringUtils.hasText(zzdOpenPlatform.getTenantId()) && dto.getExt() != null
+ && StringUtils.hasText(dto.getExt().getRealmId())
+ && !zzdOpenPlatform.getTenantId().equals(dto.getExt().getRealmId())) {
+ log.warn("浙政钉用户 realmId={} 与配置 zzd.open.tenant-id={} 不一致", dto.getExt().getRealmId(), zzdOpenPlatform.getTenantId());
+ }
+ return dto;
+ } catch (ThirdPartyException e) {
+ throw e;
+ } catch (Exception e) {
+ log.error("浙政钉 dingtalk_app_user SDK 调用异常", e);
+ throw new ThirdPartyException("浙政钉获取用户失败", "ZZD_OPEN_USER_SDK");
}
- JSONObject data = parseZzdOpenContentData(response.getBody(), url);
- ZZDUserDTO dto = mapOpenPlatformUserToZzdDto(data);
- if (StringUtils.hasText(zzdOpenPlatform.getTenantId()) && dto.getExt() != null
- && StringUtils.hasText(dto.getExt().getRealmId())
- && !zzdOpenPlatform.getTenantId().equals(dto.getExt().getRealmId())) {
- log.warn("浙政钉用户 realmId={} 与配置 zzd.open.tenant-id={} 不一致", dto.getExt().getRealmId(), zzdOpenPlatform.getTenantId());
- }
- return dto;
}
- private static JSONObject parseZzdOpenContentData(String body, String requestUrl) {
- if (!StringUtils.hasText(body)) {
- throw new ThirdPartyException("浙政钉接口空响应: " + requestUrl, "ZZD_OPEN_EMPTY");
+ private static void zzdAssertSdkTopSuccess(OapiResponse resp, String apiLabel) {
+ if (resp == null || !Boolean.TRUE.equals(resp.getSuccess())) {
+ String msg = resp != null && StringUtils.hasText(resp.getMessage()) ? resp.getMessage() : "success!=true";
+ throw new ThirdPartyException("浙政钉 " + apiLabel + " 网关失败: " + msg, "ZZD_OPEN_FAIL");
}
- JSONObject root = JSON.parseObject(body);
- if (root == null || !root.getBooleanValue("success")) {
- throw new ThirdPartyException("浙政钉接口失败: " + body, "ZZD_OPEN_FAIL");
- }
- JSONObject content = root.getJSONObject("content");
+ }
+
+ private static JSONObject zzdSdkSpResultData(OapiSpResultContent content, String apiLabel) {
if (content == null) {
- throw new ThirdPartyException("浙政钉响应缺少 content: " + body, "ZZD_OPEN_BAD");
+ throw new ThirdPartyException("浙政钉 " + apiLabel + " 响应无 content", "ZZD_OPEN_BAD");
}
- if (!content.getBooleanValue("success")) {
- throw new ThirdPartyException("浙政钉 content 失败: " + body, "ZZD_OPEN_FAIL");
+ if (content.getSuccess() != null && !content.getSuccess()) {
+ throw new ThirdPartyException("浙政钉 " + apiLabel + " 业务失败: " + content.getResponseCode() + " " + content.getResponseMessage(), "ZZD_OPEN_FAIL");
}
- JSONObject data = content.getJSONObject("data");
+ String dataStr = content.getData();
+ if (!StringUtils.hasText(dataStr)) {
+ throw new ThirdPartyException("浙政钉 " + apiLabel + " 响应无 data", "ZZD_OPEN_BAD");
+ }
+ JSONObject data = JSON.parseObject(dataStr);
if (data == null) {
- throw new ThirdPartyException("浙政钉响应缺少 data: " + body, "ZZD_OPEN_BAD");
+ throw new ThirdPartyException("浙政钉 " + apiLabel + " data 非 JSON", "ZZD_OPEN_BAD");
}
return data;
}
+ private static JSONObject zzdSdkDingtalkAppUserContentData(String contentStr) {
+ if (!StringUtils.hasText(contentStr)) {
+ throw new ThirdPartyException("浙政钉 dingtalk_app_user 响应无 content body", "ZZD_OPEN_BAD");
+ }
+ JSONObject content = JSON.parseObject(contentStr);
+ if (content == null) {
+ throw new ThirdPartyException("浙政钉 dingtalk_app_user content 非 JSON", "ZZD_OPEN_BAD");
+ }
+ if (content.containsKey("success") && !content.getBooleanValue("success")) {
+ throw new ThirdPartyException("浙政钉 dingtalk_app_user 业务失败: " + content.getString("responseCode") + " " + content.getString("responseMessage"), "ZZD_OPEN_FAIL");
+ }
+ JSONObject data = content.getJSONObject("data");
+ if (data != null) {
+ return data;
+ }
+ return content;
+ }
+
private static ZZDUserDTO mapOpenPlatformUserToZzdDto(JSONObject data) {
ZZDUserDTO dto = new ZZDUserDTO();
String lastName = data.getString("lastName");
@@ -728,6 +759,64 @@ public class ThirdPartyOAuth2Service {
}
+ /**
+ * 政务钉钉 H5 JSAPI 鉴权:前端 {@code dd.authConfig({ ticket, jsApiList })}。
+ * 官方 SDK:{@code IntelligentGetClient /get_jsapi_token.json},请求体带应用 {@code accessToken},解析 {@code content.data.accessToken} 为 ticket。
+ */
+ public ZzdJsapiConfigVO buildZzdJsapiConfigForH5() {
+ if (!zzdOpenPlatform.isEnabled() || !zzdOpenPlatform.isAppCredentialConfigured()) {
+ throw new ThirdPartyException("浙政钉开放平台未启用或未配置 appKey/appSecret", "ZZD_JSAPI_CFG");
+ }
+ String ticket = getZzdOpenJsapiTokenTicket();
+ ZzdJsapiConfigVO vo = new ZzdJsapiConfigVO();
+ vo.setTicket(ticket);
+ vo.setJsApiList(Arrays.asList("getAuthCode", "runtime.permission.requestAuthCode"));
+ return vo;
+ }
+
+ private String getZzdOpenJsapiTokenTicket() {
+ long now = System.currentTimeMillis();
+ if (StringUtils.hasText(zzdOpenCachedJsapiTicket) && now < zzdOpenCachedJsapiTicketExpireMs - 120_000L) {
+ return zzdOpenCachedJsapiTicket;
+ }
+ synchronized (zzdJsapiTicketLock) {
+ now = System.currentTimeMillis();
+ if (StringUtils.hasText(zzdOpenCachedJsapiTicket) && now < zzdOpenCachedJsapiTicketExpireMs - 120_000L) {
+ return zzdOpenCachedJsapiTicket;
+ }
+ String appToken = getZzdOpenAppAccessToken();
+ try {
+ ExecutableClient ec = zzdExecutableClientHolder.client();
+ IntelligentGetClient getClient = ec.newIntelligentGetClient("/get_jsapi_token.json");
+ OapiGetJsapiTokenJsonRequest req = new OapiGetJsapiTokenJsonRequest();
+ req.setAccessToken(appToken);
+ OapiGetJsapiTokenJsonResponse resp = getClient.get(req);
+ zzdAssertSdkTopSuccess(resp, "get_jsapi_token");
+ JSONObject data = zzdSdkSpResultData(resp.getContent(), "get_jsapi_token");
+ String ticket = data.getString("accessToken");
+ if (!StringUtils.hasText(ticket)) {
+ throw new ThirdPartyException("浙政钉 get_jsapi_token 响应无 data.accessToken", "ZZD_JSAPI_TK_BAD");
+ }
+ int ttl = 7100;
+ try {
+ Object ex = data.get("expiresIn");
+ if (ex != null) {
+ ttl = Integer.parseInt(ex.toString());
+ }
+ } catch (NumberFormatException ignored) {
+ }
+ zzdOpenCachedJsapiTicket = ticket;
+ zzdOpenCachedJsapiTicketExpireMs = System.currentTimeMillis() + ttl * 1000L;
+ return ticket;
+ } catch (ThirdPartyException e) {
+ throw e;
+ } catch (Exception e) {
+ log.error("浙政钉 get_jsapi_token SDK 调用异常", e);
+ throw new ThirdPartyException("浙政钉获取 JSAPI ticket 失败", "ZZD_JSAPI_TK_SDK");
+ }
+ }
+ }
+
/**
* 诗画
* @param accountId
diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/vo/ZzdJsapiConfigVO.java b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/vo/ZzdJsapiConfigVO.java
new file mode 100644
index 000000000..1301b4030
--- /dev/null
+++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/vo/ZzdJsapiConfigVO.java
@@ -0,0 +1,17 @@
+package com.bsd.pjcloud.thirdparty.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 政务钉钉 / 专有钉钉 H5:{@code dd.authConfig} 参数。
+ * ticket 来自开放平台 {@code POST /get_jsapi_token.json} 返回的 {@code content.data.accessToken}(文档称鉴权 ticket)。
+ */
+@Data
+public class ZzdJsapiConfigVO {
+
+ private String ticket;
+
+ private List jsApiList;
+}
diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/resources/application.yml b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/resources/application.yml
index f0b36d3ac..390f36294 100644
--- a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/resources/application.yml
+++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/resources/application.yml
@@ -52,6 +52,8 @@ zzd:
tenant-id: "196729"
app-key: ${ZZD_OPEN_APP_KEY:pjx_zhhqyth-wt2VrVa0olcqEBBJJb}
app-secret: ${ZZD_OPEN_APP_SECRET:rT5zbpHO0L7raRHJtvlIs26N8hn53JDv1K1SNa7X}
+ # H5 使用 dd.authConfig 时仅需 ak/sk;jsapi-agent-id 为旧版 dd.config 方案保留,可不配
+ # jsapi-agent-id:
# 正元智慧
# https://gwyc.zj.gov.cn/API/%E7%9C%81%E5%B9%B3%E5%8F%B0API%E5%AF%B9%E6%8E%A5%E6%8E%A5%E5%8F%A3.html