|
@@ -0,0 +1,123 @@
|
|
|
|
|
+package com.nb.web.service.bus.YiXinQian;
|
|
|
|
|
+
|
|
|
|
|
+import cn.hutool.cache.Cache;
|
|
|
|
|
+import cn.hutool.cache.CacheUtil;
|
|
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
|
|
+import cn.hutool.http.HttpRequest;
|
|
|
|
|
+import cn.hutool.http.HttpResponse;
|
|
|
|
|
+import cn.hutool.http.HttpUtil;
|
|
|
|
|
+import cn.hutool.json.JSONUtil;
|
|
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+
|
|
|
|
|
+import java.util.HashMap;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 医信签核心服务(实现4.1.1获取Token、4.2.3查询用户信息)
|
|
|
|
|
+ */
|
|
|
|
|
+@Service
|
|
|
|
|
+@RequiredArgsConstructor // 构造器注入配置类
|
|
|
|
|
+public class YixinqianService {
|
|
|
|
|
+ // 注入医信签配置
|
|
|
|
|
+ private final YixinqianConfig yixinqianConfig;
|
|
|
|
|
+
|
|
|
|
|
+ // 本地缓存:存储AccessToken(key=固定值,value=accessToken),有效期=配置的tokenExpire
|
|
|
|
|
+ private final Cache<String, String> accessTokenCache = CacheUtil.newTimedCache(yixinqianConfig.getTokenExpire());
|
|
|
|
|
+ private static final String CACHE_KEY_ACCESS_TOKEN = "YIXINQIAN_ACCESS_TOKEN";
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 1. 获取AccessToken(4.1.1接口)
|
|
|
|
|
+ * 逻辑:优先从缓存取,缓存不存在/过期则调用接口获取,并更新缓存
|
|
|
|
|
+ * @return AccessToken
|
|
|
|
|
+ */
|
|
|
|
|
+ public String getAccessToken() {
|
|
|
|
|
+ // 1. 从缓存获取AccessToken
|
|
|
|
|
+ String accessToken = accessTokenCache.get(CACHE_KEY_ACCESS_TOKEN);
|
|
|
|
|
+ if (accessToken != null && !StrUtil.isBlank(accessToken)) {
|
|
|
|
|
+ return accessToken;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 缓存无有效Token,调用接口获取
|
|
|
|
|
+ String tokenUrl = yixinqianConfig.getBaseUrl() + "/api/v1.0/getAccessToken";
|
|
|
|
|
+ // 构造请求参数(appid、appkey)
|
|
|
|
|
+ Map<String, Object> tokenParam = new HashMap<>(2);
|
|
|
|
|
+ tokenParam.put("appid", yixinqianConfig.getAppId());
|
|
|
|
|
+ tokenParam.put("appkey", yixinqianConfig.getAppKey());
|
|
|
|
|
+
|
|
|
|
|
+ // 发送POST请求(JSON格式)
|
|
|
|
|
+ String tokenRespStr = HttpUtil.post(tokenUrl, JSONUtil.toJsonStr(tokenParam));
|
|
|
|
|
+ // 解析返回结果
|
|
|
|
|
+ RespResult<AccessTokenData> tokenResp = JSONUtil.toBean(
|
|
|
|
|
+ tokenRespStr,
|
|
|
|
|
+ new cn.hutool.core.lang.TypeReference<RespResult<AccessTokenData>>() {},
|
|
|
|
|
+ false
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // 校验请求结果
|
|
|
|
|
+ if (!tokenResp.isSuccess()) {
|
|
|
|
|
+ throw new RuntimeException("获取AccessToken失败:" + tokenResp.getMessage() + "(状态码:" + tokenResp.getStatus() + ")");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 提取AccessToken并存入缓存
|
|
|
|
|
+ AccessTokenData tokenData = tokenResp.getData();
|
|
|
|
|
+ if (tokenData == null || tokenData.getAccessToken() == null || StrUtil.isBlank(tokenData.getAccessToken())) {
|
|
|
|
|
+ throw new RuntimeException("获取AccessToken失败:返回Token为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ accessToken = tokenData.getAccessToken();
|
|
|
|
|
+ accessTokenCache.put(CACHE_KEY_ACCESS_TOKEN, accessToken); // 存入缓存(自动过期)
|
|
|
|
|
+
|
|
|
|
|
+ return accessToken;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 2. 查询用户信息(4.2.3接口)
|
|
|
|
|
+ * 逻辑:先获取AccessToken,再调用查询接口,返回用户信息
|
|
|
|
|
+ * @param queryParam 查询参数(三选一)
|
|
|
|
|
+ * @return 用户信息
|
|
|
|
|
+ */
|
|
|
|
|
+ public UserInfoData queryUserInfo(UserInfoQueryParam queryParam) {
|
|
|
|
|
+ // 1. 参数校验(三选一)
|
|
|
|
|
+ queryParam.validate();
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 获取有效的AccessToken
|
|
|
|
|
+ String accessToken = getAccessToken();
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 构造查询用户信息的URL(带accessToken参数)
|
|
|
|
|
+ String userInfoUrl = yixinqianConfig.getBaseUrl() + "/doctor/api/v1.0/user/info?accessToken=" + accessToken;
|
|
|
|
|
+
|
|
|
|
|
+ // 4. 发送POST请求(JSON格式,设置请求头)
|
|
|
|
|
+ HttpResponse userInfoResp = HttpRequest.post(userInfoUrl)
|
|
|
|
|
+ .header("Content-Type", "application/json") // 传参格式:JSON
|
|
|
|
|
+ .header("Accept", "application/json") // 返回格式:JSON
|
|
|
|
|
+ .body(JSONUtil.toJsonStr(queryParam)) // 请求体(JSON字符串)
|
|
|
|
|
+ .execute();
|
|
|
|
|
+
|
|
|
|
|
+ // 5. 解析返回结果
|
|
|
|
|
+ String userInfoRespStr = userInfoResp.body();
|
|
|
|
|
+ RespResult<UserInfoData> userInfoResult = JSONUtil.toBean(
|
|
|
|
|
+ userInfoRespStr,
|
|
|
|
|
+ new cn.hutool.core.lang.TypeReference<RespResult<UserInfoData>>() {},
|
|
|
|
|
+ false
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // 6. 校验请求结果
|
|
|
|
|
+ if (!userInfoResult.isSuccess()) {
|
|
|
|
|
+ throw new RuntimeException("查询用户信息失败:" + userInfoResult.getMessage() + "(状态码:" + userInfoResult.getStatus() + ")");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 7. 返回用户信息(data字段)
|
|
|
|
|
+ UserInfoData userInfoData = userInfoResult.getData();
|
|
|
|
|
+ if (userInfoData == null) {
|
|
|
|
|
+ throw new RuntimeException("查询用户信息失败:返回用户数据为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ return userInfoData;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 手动刷新AccessToken(可选,用于测试)
|
|
|
|
|
+ */
|
|
|
|
|
+ public void refreshAccessToken() {
|
|
|
|
|
+ accessTokenCache.remove(CACHE_KEY_ACCESS_TOKEN);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|