| 
					
				 | 
			
			
				@@ -0,0 +1,196 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+package com.fs.erp.service.impl; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import cn.hutool.core.bean.BeanUtil; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.alibaba.fastjson.JSON; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.alibaba.fastjson.TypeReference; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.fs.erp.dto.GetInitTokenRequestDTO; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.fs.erp.dto.GetInitTokenResponseDTO; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.fs.erp.dto.RefreshTokenRequestDTO; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.fs.erp.http.JstErpHttpService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.fs.erp.utils.SignUtil; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.apache.http.protocol.HttpService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.slf4j.Logger; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.slf4j.LoggerFactory; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.beans.factory.annotation.Autowired; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.beans.factory.annotation.Value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.data.redis.core.StringRedisTemplate; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.scheduling.annotation.Scheduled; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.stereotype.Service; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import javax.annotation.PostConstruct; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.concurrent.TimeUnit; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@Service 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+public class JstTokenService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static final Logger log = LoggerFactory.getLogger(JstTokenService.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static final String JST_TOKEN_KEY = "jst:access_token"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static final String JST_REFRESH_TOKEN_KEY = "jst:refresh_token"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static final String JST_TOKEN_EXPIRE_KEY = "jst:token_expire"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Value("${jst.app_key}") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private String appKey; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Value("${jst.app_secret}") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private String appSecret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Value("${jst.authorization_code}") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private String authorizationCode; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Autowired 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private JstErpHttpService httpService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Autowired 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private StringRedisTemplate redisTemplate; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 获取访问令牌,如果缓存中存在有效令牌则直接返回,否则重新获取 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @return 有效的访问令牌 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public String getAccessToken() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        String accessToken = redisTemplate.opsForValue().get(JST_TOKEN_KEY); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (accessToken != null && !accessToken.isEmpty()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            log.debug("从缓存中获取到有效的access_token"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return accessToken; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        log.info("缓存中没有有效的access_token,重新获取"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return refreshAccessToken(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 强制刷新访问令牌 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @return 新的访问令牌 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public String refreshAccessToken() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 检查是否有刷新令牌 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        String refreshToken = redisTemplate.opsForValue().get(JST_REFRESH_TOKEN_KEY); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (refreshToken != null && !refreshToken.isEmpty()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            log.info("使用refresh_token刷新令牌"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return refreshTokenWithApi(refreshToken); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } catch (Exception e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                log.error("使用refresh_token刷新失败,尝试重新初始化", e); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 如果没有刷新令牌或刷新失败,则重新初始化 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return initToken(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 初始化令牌 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @return 新的访问令牌 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private String initToken() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        GetInitTokenRequestDTO requestDTO = new GetInitTokenRequestDTO(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        requestDTO.setApp_key(appKey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        requestDTO.setGrant_type("authorization_code"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        requestDTO.setCharset("utf-8"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        requestDTO.setCode(authorizationCode); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        requestDTO.setTimestamp(String.valueOf(System.currentTimeMillis() / 1000)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 计算签名 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        requestDTO.setSign(SignUtil.getSignNew(appSecret, requestDTO)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        log.info("初始化token请求参数: {}", JSON.toJSONString(requestDTO)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        GetInitTokenResponseDTO response = httpService.getInitToken(requestDTO); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (response == null || response.getAccess_token() == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new RuntimeException("初始化token失败: " + (response != null ? JSON.toJSONString(response) : "响应为空")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 缓存token信息 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        storeTokenInfo(response); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        log.info("初始化token成功,token将在{}秒后过期", response.getExpires_in()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return response.getAccess_token(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 使用刷新令牌API刷新令牌 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param refreshToken 刷新令牌 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @return 新的访问令牌 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private String refreshTokenWithApi(String refreshToken) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        RefreshTokenRequestDTO requestDTO = new RefreshTokenRequestDTO(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        requestDTO.setApp_key(appKey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        requestDTO.setGrant_type("refresh_token"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        requestDTO.setCharset("utf-8"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        requestDTO.setRefresh_token(refreshToken); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        requestDTO.setTimestamp(String.valueOf(System.currentTimeMillis() / 1000)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 计算签名 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        requestDTO.setSign(SignUtil.getSignNew(appSecret, requestDTO)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        log.info("刷新token请求参数: {}", JSON.toJSONString(requestDTO)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        GetInitTokenResponseDTO response = httpService.refreshToken(requestDTO); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (response == null || response.getAccess_token() == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new RuntimeException("刷新token失败: " + (response != null ? JSON.toJSONString(response) : "响应为空")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 缓存token信息 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        storeTokenInfo(response); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        log.info("刷新token成功,token将在{}秒后过期", response.getExpires_in()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return response.getAccess_token(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 将令牌信息存储到Redis 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param tokenInfo 令牌信息 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void storeTokenInfo(GetInitTokenResponseDTO tokenInfo) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 默认30天过期时间,转换为秒 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        long expiresIn = tokenInfo.getExpires_in() != null ? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          Long.parseLong(String.valueOf(tokenInfo.getExpires_in())) : 30 * 24 * 60 * 60; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 存储令牌,设置比官方少一天的过期时间,以确保安全 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        redisTemplate.opsForValue().set(JST_TOKEN_KEY, tokenInfo.getAccess_token(), expiresIn - 86400, TimeUnit.SECONDS); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 存储刷新令牌,有效期与访问令牌相同 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (tokenInfo.getRefresh_token() != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            redisTemplate.opsForValue().set(JST_REFRESH_TOKEN_KEY, tokenInfo.getRefresh_token(), expiresIn, TimeUnit.SECONDS); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 存储过期时间戳 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        long expireAt = System.currentTimeMillis() + (expiresIn * 1000); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        redisTemplate.opsForValue().set(JST_TOKEN_EXPIRE_KEY, String.valueOf(expireAt), expiresIn, TimeUnit.SECONDS); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 定时任务,每20天刷新一次令牌 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 根据文档说明,刷新token必须在有效期小于15天时进行,所以这里设置20天刷新一次 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Scheduled(fixedRate = 20 * 24 * 60 * 60 * 1000) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public void scheduleTokenRefresh() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        log.info("定时任务:刷新聚水潭API令牌"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            refreshAccessToken(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } catch (Exception e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            log.error("定时刷新令牌失败", e); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 启动时初始化令牌 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @PostConstruct 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public void init() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // 检查Redis中是否已有有效token 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            String accessToken = redisTemplate.opsForValue().get(JST_TOKEN_KEY); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (accessToken == null || accessToken.isEmpty()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                log.info("系统启动,初始化聚水潭API令牌"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                initToken(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                log.info("系统启动,发现缓存中已有有效的聚水潭API令牌"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } catch (Exception e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            log.error("初始化聚水潭API令牌失败", e); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 |