diff --git a/pjcloud-app-server/pjcloud-app-server-api/src/main/java/com/bsd/pjcloud/app/api/constant/menu/AppMenuPlatformEnum.java b/pjcloud-app-server/pjcloud-app-server-api/src/main/java/com/bsd/pjcloud/app/api/constant/menu/AppMenuPlatformEnum.java index 23e6e50d5..f98c33e18 100644 --- a/pjcloud-app-server/pjcloud-app-server-api/src/main/java/com/bsd/pjcloud/app/api/constant/menu/AppMenuPlatformEnum.java +++ b/pjcloud-app-server/pjcloud-app-server-api/src/main/java/com/bsd/pjcloud/app/api/constant/menu/AppMenuPlatformEnum.java @@ -15,6 +15,7 @@ public enum AppMenuPlatformEnum { WECHAT("10", "微信小程序"), ZZD("30", "浙政钉"), + SHPJ("40", "诗画浦江"), ; private final String type; diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/dto/ZZDUserDTO.java b/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/dto/ZZDUserDTO.java index 489b08250..57b006f7e 100644 --- a/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/dto/ZZDUserDTO.java +++ b/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/dto/ZZDUserDTO.java @@ -4,7 +4,7 @@ import lombok.Data; /** * 第三方 SSO 在本服务内的统一用户视图(非开放平台原始报文体)。 - * 浙政钉:由 {@code dingtalk_app_user} 等适配层填入;诗画:由网关用户信息接口填入。 + * 浙政钉:由 {@code dingtalk_app_user} 等适配层填入;诗画:由本系统直连诗画开放接口填入。 * 后续业务(发 token、短信绑定快照等)只依赖本结构,与走网关还是走开放平台无关。 */ @Data diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/OAuth2ClientConfig.java b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/OAuth2ClientConfig.java index c45e61a9a..8bc0532a8 100644 --- a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/OAuth2ClientConfig.java +++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/OAuth2ClientConfig.java @@ -24,12 +24,7 @@ public class OAuth2ClientConfig { /** - * 诗画江南的token接口 - */ - private String shjnTokenUrl; - - /** - * 诗画江南 uabaseToken 换用户信息(网关 GET ?token=) + * 诗画江南 uabaseToken 换用户信息(诗画已直连,保留字段兼容旧配置) */ private String zzdShUserInfoUrl; 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 9bbf043cb..e82b45bf5 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 @@ -24,6 +24,8 @@ 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.shpj.ShpjOpenapiUser; +import com.bsd.pjcloud.thirdparty.shpj.ShpjUserClient; import com.bsd.pjcloud.thirdparty.vo.ZzdJsapiConfigVO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -34,7 +36,6 @@ import org.springframework.http.*; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.DigestUtils; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; @@ -42,7 +43,6 @@ import org.springframework.web.client.HttpStatusCodeException; import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.RestTemplate; -import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -85,6 +85,8 @@ public class ThirdPartyOAuth2Service { private final ZzdOpenPlatformExecutableClientHolder zzdExecutableClientHolder; + private final ShpjUserClient shpjUserClient; + public TokenDTO getAccessToken() { TokenDTO cachedToken = tokenCache.get("access_token"); @@ -736,102 +738,6 @@ public class ThirdPartyOAuth2Service { - /** - * 诗画江南的请求token - * @param accountId - * @param sessionId - * @return - */ - - public String getSHJNToken(String accountId,String sessionId){ - // 构建请求URL - String url = baseUrl + config.getShjnTokenUrl()+"?accountId=" + accountId + "&sessionId=" + sessionId; - - ResponseEntity> response = restTemplate.exchange( - url, HttpMethod.POST, null, new ParameterizedTypeReference>() {} - ); - return response.getBody().getData(); - - } - - - - - public String getUserDetails(String accountId, String sessionId, String connectNo, String appkey, String appsecret) { - String timestamp = System.currentTimeMillis() + ""; - String noncestr = timestamp; - - // 构造请求参数 - Map params = new HashMap<>(); - params.put("connectNo", connectNo); - params.put("sessionId", sessionId); - // 签名生成 - String signature = generateSignature(timestamp, appkey, appsecret, noncestr, params); - params.put("signature", signature); - params.put("timestamp", timestamp); - params.put("appkey", appkey); - params.put("noncestr", noncestr); - - // 构造 URL - String url = String.format("https://vcms.tmuyun.com/openapi/v2/%s/detail?%s", accountId, mapToUrl(params)); - - // 构造请求头 - HttpHeaders headers = new HttpHeaders(); - headers.set("Content-Type", "application/json"); - - // 构造请求实体 - HttpEntity entity = new HttpEntity<>(headers); - - // 发起请求 - ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class); - - // 返回响应体 - return response.getBody(); - } - - /** - * 生成签名 - */ - private String generateSignature(String timestamp, String appkey, String appsecret, String noncestr, Map params) { - // 移除不需要参与签名的字段 - params.remove("appkey"); - params.remove("timestamp"); - params.remove("noncestr"); - params.remove("signature"); - - // 参数名按照ASCII码表升序排序 - String[] keys = params.keySet().toArray(new String[0]); - Arrays.sort(keys); - - // 按照排序拼接参数值 - StringBuilder paramBuffer = new StringBuilder(); - for (String key : keys) { - String value = params.get(key); - if (StringUtils.hasLength(value) && !"0".equals(value)) { - paramBuffer.append("&&").append(value); - } - } - - // 拼接最终签名串 - String rawSignature = String.format("%s&&%s&&%s&&%s", timestamp, appkey, appsecret, noncestr) + paramBuffer.toString(); - return DigestUtils.md5DigestAsHex(rawSignature.getBytes(StandardCharsets.UTF_8)); - } - - /** - * 将Map转换为URL参数字符串 - */ - private String mapToUrl(Map map) { - StringBuilder sb = new StringBuilder(); - for (Map.Entry entry : map.entrySet()) { - sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&"); - } - if (sb.length() > 0) { - sb.deleteCharAt(sb.length() - 1); - } - return sb.toString(); - } - - /** * 政务钉钉 H5 JSAPI 鉴权:前端 {@code dd.authConfig({ ticket, jsApiList })}。 * 官方 SDK:{@code IntelligentGetClient /get_jsapi_token.json},请求体带应用 {@code accessToken},解析 {@code content.data.accessToken} 为 ticket。 @@ -891,17 +797,29 @@ public class ThirdPartyOAuth2Service { } /** - * 诗画 - * @param accountId - * @param sessionId - * @return + * 诗画浦江单点:直连诗画开放接口获取用户,不再经对方 /login/shpj 中转 */ public ZZDUserDTO shjnsso(String accountId, String sessionId) { - String shjnToken = getSHJNToken(accountId, sessionId); - if (StringUtils.isEmpty(shjnToken)){ - throw new RuntimeException("获取诗画江南token失败"); + ShpjOpenapiUser openapiUser = shpjUserClient.getUserInfo(accountId, sessionId); + if (openapiUser == null || openapiUser.getIsLogin() != 1) { + throw new ThirdPartyException("请先登录诗画客户端", "SHPJ_NOT_LOGIN"); } - ZZDUserDTO zzdUserDTO = getZZDUserInfo(shjnToken, UserTypeEnum.SH.getType()); - return zzdUserDTO; + if (!StringUtils.hasText(openapiUser.getMobile())) { + throw new ThirdPartyException("诗画用户手机号为空", "SHPJ_MOBILE_EMPTY"); + } + return mapShpjOpenapiUserToZzdDto(openapiUser); + } + + private static ZZDUserDTO mapShpjOpenapiUserToZzdDto(ShpjOpenapiUser user) { + ZZDUserDTO dto = new ZZDUserDTO(); + dto.setUsername(user.getNickName()); + dto.setNickName(user.getNickName()); + dto.setMobile(user.getMobile()); + ZZDUserDTO.Ext ext = dto.new Ext(); + ext.setNickNameCn(user.getNickName()); + ext.setMobile(user.getMobile()); + ext.setHeadImgUrl(user.getHeadImgUrl()); + dto.setExt(ext); + return dto; } } \ No newline at end of file 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 e063f162a..83d438eae 100644 --- a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/resources/application.yml +++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/resources/application.yml @@ -34,11 +34,23 @@ oauth2: dept-url: /prod-api/oauth2/apis/orguser/v1/list_sub_depts dept-users-url: /prod-api/oauth2/apis/orguser/v1/list_dept_users user_detail-url: /prod-api/oauth2/apis/orguser/v1/user_detail - # 诗画的获取token的接口 - shjn_token_url: /prod-api/login/shpj - # 诗画江南 uabaseToken 换用户信息(浙政钉单点已改走下方 zzd.open 开放平台,不再使用本 URL) + # 诗画江南 uabaseToken 换用户信息(已废弃:诗画改为本系统直连 vcms;浙政钉单点已改走下方 zzd.open) zzd_sh_user_info_url: /prod-api/oauth2/apis/sso/v1/fetch_user_by_uabase_token +# 诗画浦江开放接口(直连 https://vcms.tmuyun.com,不经对方 /login/shpj) +# 浙政钉无手机号时:首次短信核验 + 绑定表 third_party_zzd_phone_bind +pjcloud: + shpj: + app-key: ${SHPJ_APP_KEY:rQZzIvB7beaeZBf2IbQnemYv} + app-secret: ${SHPJ_APP_SECRET:4d248cd64bc5653fd2c1d6bb425b2817} + connect-no: ${SHPJ_CONNECT_NO:afb7e8fbf5f4eb9f} + base-url: ${SHPJ_BASE_URL:https://vcms.tmuyun.com} + http-timeout-seconds: 5 + thirdparty: + zzd-sms-bind: + enabled: true + bind-session-ttl-minutes: 15 + # 浙政钉:H5 authCode → gettoken + dingtalk_app_user(需 app-key / app-secret) zzd: open: @@ -199,10 +211,3 @@ hikvision: # base-url: https://api.mch.weixin.qq.com # ali-pay-config: # app-id: - -# 浙政钉无手机号时:首次短信核验 + 绑定表 third_party_zzd_phone_bind,与本系统用户手机号比对后再发 token(关闭则直接按手机号/员工编码查用户并发 token;新用户务必为 true 才会走短信页) -pjcloud: - thirdparty: - zzd-sms-bind: - enabled: true - bind-session-ttl-minutes: 15