Skip to content

Commit 0efc029

Browse files
committed
refactor:重构token,为对接后续阿里普通音色与coze
1 parent ea58221 commit 0efc029

File tree

5 files changed

+440
-0
lines changed

5 files changed

+440
-0
lines changed

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,12 @@
239239
<artifactId>dashscope-sdk-java</artifactId>
240240
<version>2.20.2</version>
241241
</dependency>
242+
<!-- 阿里云Token -->
243+
<dependency>
244+
<groupId>com.aliyun</groupId>
245+
<artifactId>aliyun-java-sdk-core</artifactId>
246+
<version>3.7.1</version>
247+
</dependency>
242248
<!-- 讯飞 -->
243249
<dependency>
244250
<groupId>cn.xfyun</groupId>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.xiaozhi.dialogue.token;
2+
3+
public interface TokenService {
4+
/**
5+
* 获取Token (统一入口)
6+
*/
7+
String getToken();
8+
9+
/**
10+
* 获取服务提供商名称
11+
*/
12+
String getProviderName();
13+
14+
/**
15+
* 手动刷新Token
16+
*/
17+
String refreshToken();
18+
19+
/**
20+
* 检查Token是否有效
21+
*/
22+
boolean isTokenValid();
23+
24+
/**
25+
* 清理指定配置的Token缓存
26+
*/
27+
void clearTokenCache();
28+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.xiaozhi.dialogue.token.entity;
2+
3+
import java.time.LocalDateTime;
4+
5+
public class TokenCache {
6+
private String token;
7+
private LocalDateTime expireTime;
8+
private LocalDateTime lastUsedTime;
9+
private LocalDateTime createTime;
10+
11+
public TokenCache(String token, LocalDateTime expireTime) {
12+
this.token = token;
13+
this.expireTime = expireTime;
14+
this.createTime = LocalDateTime.now();
15+
this.lastUsedTime = LocalDateTime.now();
16+
}
17+
18+
// 更新最后使用时间
19+
public void updateLastUsedTime() {
20+
this.lastUsedTime = LocalDateTime.now();
21+
}
22+
23+
// 检查是否需要刷新(剩余1小时)
24+
public boolean needsRefresh() {
25+
return expireTime != null &&
26+
LocalDateTime.now().plusHours(1).isAfter(expireTime);
27+
}
28+
29+
// 检查是否过期
30+
public boolean isExpired() {
31+
return expireTime != null &&
32+
LocalDateTime.now().isAfter(expireTime);
33+
}
34+
35+
// 检查是否需要清除缓存(超过24小时未使用)
36+
public boolean needsCacheCleanup() {
37+
return LocalDateTime.now().minusHours(24).isAfter(lastUsedTime);
38+
}
39+
40+
// Getters and Setters
41+
public String getToken() { return token; }
42+
public void setToken(String token) { this.token = token; }
43+
44+
public LocalDateTime getExpireTime() { return expireTime; }
45+
public void setExpireTime(LocalDateTime expireTime) { this.expireTime = expireTime; }
46+
47+
public LocalDateTime getLastUsedTime() { return lastUsedTime; }
48+
public void setLastUsedTime(LocalDateTime lastUsedTime) { this.lastUsedTime = lastUsedTime; }
49+
50+
public LocalDateTime getCreateTime() { return createTime; }
51+
public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }
52+
}
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package com.xiaozhi.dialogue.token.factory;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
import org.springframework.stereotype.Component;
6+
7+
import com.xiaozhi.dialogue.token.TokenService;
8+
import com.xiaozhi.dialogue.token.providers.AliyunTokenService;
9+
import com.xiaozhi.entity.SysConfig;
10+
11+
import jakarta.annotation.PostConstruct;
12+
import jakarta.annotation.PreDestroy;
13+
import java.util.Map;
14+
import java.util.concurrent.ConcurrentHashMap;
15+
import java.util.concurrent.Executors;
16+
import java.util.concurrent.ScheduledExecutorService;
17+
import java.util.concurrent.TimeUnit;
18+
19+
@Component
20+
public class TokenServiceFactory {
21+
22+
private static final Logger logger = LoggerFactory.getLogger(TokenServiceFactory.class);
23+
24+
// 缓存已初始化的服务:键为"provider:configId"格式
25+
private final Map<String, TokenService> serviceCache = new ConcurrentHashMap<>();
26+
27+
// 使用虚拟线程的定时任务执行器
28+
private ScheduledExecutorService scheduler;
29+
30+
@PostConstruct
31+
public void init() {
32+
// 使用虚拟线程工厂创建定时任务执行器
33+
scheduler = Executors.newScheduledThreadPool(2, Thread.ofVirtual()
34+
.name("token-cleanup-scheduler-", 0)
35+
.factory());
36+
37+
// 启动定时清理任务,每小时执行一次
38+
scheduler.scheduleAtFixedRate(this::cleanupUnusedTokens, 1, 1, TimeUnit.HOURS);
39+
}
40+
41+
@PreDestroy
42+
public void destroy() {
43+
if (scheduler != null && !scheduler.isShutdown()) {
44+
scheduler.shutdown();
45+
try {
46+
if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
47+
scheduler.shutdownNow();
48+
}
49+
} catch (InterruptedException e) {
50+
scheduler.shutdownNow();
51+
Thread.currentThread().interrupt();
52+
}
53+
}
54+
}
55+
56+
// 创建缓存键,包含provider、configId
57+
private String createCacheKey(SysConfig config, String provider) {
58+
Integer configId = config != null && config.getConfigId() != null ?
59+
config.getConfigId() : -1;
60+
return provider + ":" + configId;
61+
}
62+
63+
/**
64+
* 根据配置获取Token服务
65+
*/
66+
public TokenService getTokenService(SysConfig config) {
67+
var provider = config.getProvider();
68+
var cacheKey = createCacheKey(config, provider);
69+
70+
// 检查是否已有该配置的服务实例
71+
TokenService service = serviceCache.get(cacheKey);
72+
if (service != null) {
73+
return service;
74+
}
75+
76+
// 创建新的服务实例
77+
service = createTokenService(config);
78+
serviceCache.put(cacheKey, service);
79+
80+
return service;
81+
}
82+
83+
/**
84+
* 根据配置创建Token服务
85+
*/
86+
private TokenService createTokenService(SysConfig config) {
87+
return switch (config.getProvider()) {
88+
case "aliyun" -> new AliyunTokenService(config);
89+
default -> new AliyunTokenService(config);
90+
};
91+
}
92+
93+
/**
94+
* 移除指定配置的缓存
95+
*/
96+
public void removeCache(SysConfig config) {
97+
String provider = config.getProvider();
98+
Integer configId = config.getConfigId();
99+
String cacheKey = provider + ":" + configId;
100+
101+
TokenService service = serviceCache.remove(cacheKey);
102+
if (service != null) {
103+
service.clearTokenCache();
104+
}
105+
}
106+
107+
/**
108+
* 清理需要清除缓存的Token(超过24小时未使用)
109+
*/
110+
private void cleanupUnusedTokens() {
111+
112+
// 使用虚拟线程并行处理清理任务
113+
serviceCache.entrySet().parallelStream().forEach(entry -> {
114+
Thread.startVirtualThread(() -> {
115+
try {
116+
TokenService service = entry.getValue();
117+
118+
// 检查是否为阿里云服务且需要清除缓存
119+
if (service instanceof AliyunTokenService aliyunService) {
120+
if (aliyunService.needsCacheCleanup()) {
121+
service.clearTokenCache();
122+
serviceCache.remove(entry.getKey());
123+
}
124+
}
125+
} catch (Exception e) {
126+
logger.error("清理Token缓存时发生错误: {}", e.getMessage(), e);
127+
}
128+
});
129+
});
130+
}
131+
132+
/**
133+
* 异步刷新所有即将过期的Token
134+
*/
135+
public void refreshExpiringTokensAsync() {
136+
137+
serviceCache.values().parallelStream().forEach(service -> {
138+
Thread.startVirtualThread(() -> {
139+
try {
140+
if (service instanceof AliyunTokenService aliyunService) {
141+
// 检查是否需要刷新
142+
String token = aliyunService.getToken(); // 内部会处理刷新逻辑
143+
}
144+
} catch (Exception e) {
145+
logger.error("刷新Token时发生错误: {}", e.getMessage(), e);
146+
}
147+
});
148+
});
149+
}
150+
151+
/**
152+
* 获取当前缓存的服务数量
153+
*/
154+
public int getCacheSize() {
155+
return serviceCache.size();
156+
}
157+
158+
/**
159+
* 清理所有缓存
160+
*/
161+
public void clearAllCache() {
162+
// 使用虚拟线程并行清理
163+
serviceCache.values().parallelStream().forEach(service -> {
164+
Thread.startVirtualThread(() -> {
165+
try {
166+
service.clearTokenCache();
167+
} catch (Exception e) {
168+
logger.error("清理Token缓存时发生错误: {}", e.getMessage(), e);
169+
}
170+
});
171+
});
172+
serviceCache.clear();
173+
}
174+
}

0 commit comments

Comments
 (0)