@jolibox/implement 1.2.4 → 1.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.rush/temp/package-deps_build.json +24 -18
- package/dist/common/cache/__tests__/request-cache-service.test.d.ts +1 -0
- package/dist/common/cache/request-cache-service.d.ts +111 -0
- package/dist/common/report/base-tracker.d.ts +2 -1
- package/dist/common/rewards/cached-fetch-reward.d.ts +46 -0
- package/dist/common/rewards/cached-reward-service.d.ts +24 -0
- package/dist/common/rewards/fetch-reward.d.ts +2 -3
- package/dist/common/rewards/index.d.ts +2 -0
- package/dist/common/rewards/registers/utils/coins/jolicoin/cached-fetch-balance.d.ts +34 -0
- package/dist/common/rewards/registers/utils/coins/jolicoin/fetch-balance.d.ts +2 -1
- package/dist/common/rewards/registers/utils/coins/joligem/cached-fetch-gem-balance.d.ts +34 -0
- package/dist/common/rewards/registers/utils/coins/joligem/fetch-gem-balance.d.ts +2 -1
- package/dist/index.js +9 -9
- package/dist/index.native.js +33 -33
- package/dist/native/payment/payment-service.d.ts +36 -30
- package/implement.build.log +2 -2
- package/package.json +5 -5
- package/src/common/cache/__tests__/request-cache-service.test.ts +686 -0
- package/src/common/cache/request-cache-service.ts +393 -0
- package/src/common/report/base-tracker.ts +2 -2
- package/src/common/rewards/cached-fetch-reward.ts +241 -0
- package/src/common/rewards/cached-reward-service.ts +255 -0
- package/src/common/rewards/fetch-reward.ts +17 -93
- package/src/common/rewards/index.ts +3 -0
- package/src/common/rewards/registers/utils/coins/commands/use-payment.ts +8 -0
- package/src/common/rewards/registers/utils/coins/jolicoin/cached-fetch-balance.ts +177 -0
- package/src/common/rewards/registers/utils/coins/jolicoin/fetch-balance.ts +13 -1
- package/src/common/rewards/registers/utils/coins/jolicoin/jolicoin-handler.ts +2 -0
- package/src/common/rewards/registers/utils/coins/joligem/cached-fetch-gem-balance.ts +181 -0
- package/src/common/rewards/registers/utils/coins/joligem/fetch-gem-balance.ts +13 -1
- package/src/common/rewards/registers/utils/coins/joligem/gem-handler.ts +2 -0
- package/src/h5/api/ads.ts +5 -0
- package/src/h5/api/storage.ts +2 -2
- package/src/h5/http/index.ts +2 -2
- package/src/h5/report/event-tracker.ts +2 -2
- package/src/native/api/ads.ts +7 -1
- package/src/native/api/payment.ts +4 -4
- package/src/native/payment/__tests__/payment-service-simple.test.ts +97 -31
- package/src/native/payment/payment-service.ts +224 -210
- package/src/native/rewards/ui/payment-modal.ts +14 -7
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { RequestCacheService, RequestAdapter } from '../cache/request-cache-service';
|
|
2
|
+
import { IHttpClient } from '../http';
|
|
3
|
+
import { StandardResponse, GlobalConfig } from '@jolibox/types';
|
|
4
|
+
import { IJolicoinRewardOption, IUnlockOption, IJoliCoin, IGem } from './type';
|
|
5
|
+
import { IUseModalFrequencyConfig } from './reward-emitter';
|
|
6
|
+
|
|
7
|
+
// 定义请求参数类型
|
|
8
|
+
interface RewardRequest {
|
|
9
|
+
endpoint: string;
|
|
10
|
+
body?: Record<string, unknown>;
|
|
11
|
+
method?: 'GET' | 'POST';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface UnlockOptionsCacheData {
|
|
15
|
+
unlockOptions: IUnlockOption[];
|
|
16
|
+
joliCoin: IJoliCoin;
|
|
17
|
+
joliGem: IGem;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface UnlockOptionsRealTimeData {
|
|
21
|
+
timestamp?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// 定义全局配置的缓存数据
|
|
25
|
+
interface GlobalConfigCacheData {
|
|
26
|
+
joliCoinUseAndCharge: IUseModalFrequencyConfig['joliCoinUseAndCharge'];
|
|
27
|
+
loginGuide: IUseModalFrequencyConfig['loginGuide'];
|
|
28
|
+
// 其他全局配置字段
|
|
29
|
+
[key: string]: unknown;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 奖励接口适配器
|
|
33
|
+
class RewardRequestAdapter
|
|
34
|
+
implements
|
|
35
|
+
RequestAdapter<RewardRequest, IJolicoinRewardOption, UnlockOptionsCacheData, UnlockOptionsRealTimeData>
|
|
36
|
+
{
|
|
37
|
+
constructor(private httpClient: IHttpClient) {}
|
|
38
|
+
|
|
39
|
+
async execute(endpoint: string, options?: RewardRequest): Promise<IJolicoinRewardOption> {
|
|
40
|
+
const method = options?.method || 'POST';
|
|
41
|
+
const body = options?.body || {};
|
|
42
|
+
|
|
43
|
+
if (method === 'GET') {
|
|
44
|
+
return await this.httpClient.get<IJolicoinRewardOption>(endpoint, body);
|
|
45
|
+
} else {
|
|
46
|
+
return await this.httpClient.post<IJolicoinRewardOption>(endpoint, body);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
extractCacheableData(response: IJolicoinRewardOption): UnlockOptionsCacheData {
|
|
51
|
+
return {
|
|
52
|
+
unlockOptions: response.data?.unlockOptions || [],
|
|
53
|
+
joliCoin: response.extra.joliCoin,
|
|
54
|
+
joliGem: response.extra.joliGem || { balance: 0, enableAutoDeduct: false }
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
extractRealTimeData?: undefined;
|
|
59
|
+
|
|
60
|
+
mergeData(cached: UnlockOptionsCacheData, realTime: UnlockOptionsRealTimeData): IJolicoinRewardOption {
|
|
61
|
+
return {
|
|
62
|
+
code: 'SUCCESS',
|
|
63
|
+
message: 'success',
|
|
64
|
+
data: {
|
|
65
|
+
unlockOptions: cached.unlockOptions
|
|
66
|
+
},
|
|
67
|
+
extra: {
|
|
68
|
+
joliCoin: cached.joliCoin,
|
|
69
|
+
joliGem: cached.joliGem
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
processCachedData(cached: UnlockOptionsCacheData): IJolicoinRewardOption {
|
|
75
|
+
return {
|
|
76
|
+
code: 'SUCCESS',
|
|
77
|
+
message: 'success from cache',
|
|
78
|
+
data: {
|
|
79
|
+
unlockOptions: cached.unlockOptions
|
|
80
|
+
},
|
|
81
|
+
extra: {
|
|
82
|
+
joliCoin: cached.joliCoin,
|
|
83
|
+
joliGem: cached.joliGem
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
class GlobalConfigRequestAdapter
|
|
90
|
+
implements
|
|
91
|
+
RequestAdapter<
|
|
92
|
+
RewardRequest,
|
|
93
|
+
StandardResponse<GlobalConfig & IUseModalFrequencyConfig>,
|
|
94
|
+
GlobalConfigCacheData,
|
|
95
|
+
undefined
|
|
96
|
+
>
|
|
97
|
+
{
|
|
98
|
+
constructor(private httpClient: IHttpClient) {}
|
|
99
|
+
|
|
100
|
+
async execute(
|
|
101
|
+
endpoint: string,
|
|
102
|
+
options?: RewardRequest
|
|
103
|
+
): Promise<StandardResponse<GlobalConfig & IUseModalFrequencyConfig>> {
|
|
104
|
+
const method = options?.method || 'GET';
|
|
105
|
+
const body = options?.body || {};
|
|
106
|
+
|
|
107
|
+
if (method === 'GET') {
|
|
108
|
+
return await this.httpClient.get<StandardResponse<GlobalConfig & IUseModalFrequencyConfig>>(
|
|
109
|
+
endpoint,
|
|
110
|
+
body
|
|
111
|
+
);
|
|
112
|
+
} else {
|
|
113
|
+
return await this.httpClient.post<StandardResponse<GlobalConfig & IUseModalFrequencyConfig>>(
|
|
114
|
+
endpoint,
|
|
115
|
+
body
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
extractCacheableData(
|
|
121
|
+
response: StandardResponse<GlobalConfig & IUseModalFrequencyConfig>
|
|
122
|
+
): GlobalConfigCacheData {
|
|
123
|
+
const data = response.data;
|
|
124
|
+
return {
|
|
125
|
+
joliCoinUseAndCharge: data?.joliCoinUseAndCharge,
|
|
126
|
+
loginGuide: data?.loginGuide,
|
|
127
|
+
...data // 包含其他所有配置字段
|
|
128
|
+
} as GlobalConfigCacheData;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
extractRealTimeData?: undefined;
|
|
132
|
+
mergeData?: undefined;
|
|
133
|
+
|
|
134
|
+
processCachedData(
|
|
135
|
+
cached: GlobalConfigCacheData
|
|
136
|
+
): StandardResponse<GlobalConfig & IUseModalFrequencyConfig> {
|
|
137
|
+
return {
|
|
138
|
+
code: 'SUCCESS',
|
|
139
|
+
message: 'success from cache',
|
|
140
|
+
data: cached as GlobalConfig & IUseModalFrequencyConfig
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export class CachedRewardService {
|
|
146
|
+
private unlockOptionsService: RequestCacheService<
|
|
147
|
+
RewardRequest,
|
|
148
|
+
IJolicoinRewardOption,
|
|
149
|
+
UnlockOptionsCacheData,
|
|
150
|
+
UnlockOptionsRealTimeData
|
|
151
|
+
>;
|
|
152
|
+
private globalConfigService: RequestCacheService<
|
|
153
|
+
RewardRequest,
|
|
154
|
+
StandardResponse<GlobalConfig & IUseModalFrequencyConfig>,
|
|
155
|
+
GlobalConfigCacheData,
|
|
156
|
+
undefined
|
|
157
|
+
>;
|
|
158
|
+
|
|
159
|
+
constructor(httpClient: IHttpClient) {
|
|
160
|
+
// 创建 unlock-options 服务,缓存 unlockOptions,实时获取余额
|
|
161
|
+
this.unlockOptionsService = new (RequestCacheService as new (...args: unknown[]) => RequestCacheService<
|
|
162
|
+
RewardRequest,
|
|
163
|
+
IJolicoinRewardOption,
|
|
164
|
+
UnlockOptionsCacheData,
|
|
165
|
+
UnlockOptionsRealTimeData
|
|
166
|
+
>)(new RewardRequestAdapter(httpClient), {
|
|
167
|
+
duration: 30 * 60 * 1000, // 30分钟缓存
|
|
168
|
+
timeout: 1000, // 1s timeout for request
|
|
169
|
+
keyGenerator: (endpoint: string) => endpoint,
|
|
170
|
+
// unlockOptions 结构一致性检查
|
|
171
|
+
consistencyChecker: (cached: UnlockOptionsCacheData, serverData: UnlockOptionsCacheData) => {
|
|
172
|
+
if (cached.unlockOptions.length !== serverData.unlockOptions.length) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
return cached.unlockOptions.every((cachedOption: IUnlockOption, index: number) => {
|
|
176
|
+
const serverOption = serverData.unlockOptions[index];
|
|
177
|
+
return (
|
|
178
|
+
cachedOption.type === serverOption.type &&
|
|
179
|
+
cachedOption.joliGemChoice?.joliGemQuantity === serverOption.joliGemChoice?.joliGemQuantity
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
this.globalConfigService = new (RequestCacheService as new (...args: unknown[]) => RequestCacheService<
|
|
186
|
+
RewardRequest,
|
|
187
|
+
StandardResponse<GlobalConfig & IUseModalFrequencyConfig>,
|
|
188
|
+
GlobalConfigCacheData,
|
|
189
|
+
undefined
|
|
190
|
+
>)(new GlobalConfigRequestAdapter(httpClient), {
|
|
191
|
+
duration: 60 * 60 * 1000, // 60分钟缓存
|
|
192
|
+
timeout: 1000, // 1s timeout for request
|
|
193
|
+
keyGenerator: (endpoint: string) => endpoint
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async getUnlockOptions(body: Record<string, unknown> = {}): Promise<IJolicoinRewardOption> {
|
|
198
|
+
return await this.unlockOptionsService.request('/api/games/unlock-options', {
|
|
199
|
+
endpoint: '/api/games/unlock-options',
|
|
200
|
+
body,
|
|
201
|
+
method: 'POST'
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async getGlobalConfig(): Promise<StandardResponse<GlobalConfig & IUseModalFrequencyConfig>> {
|
|
206
|
+
return await this.globalConfigService.request('/api/fe-configs/web-common/global-config', {
|
|
207
|
+
endpoint: '/api/fe-configs/web-common/global-config',
|
|
208
|
+
method: 'GET'
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
async refreshUnlockOptions(body: Record<string, unknown> = {}): Promise<IJolicoinRewardOption> {
|
|
212
|
+
return await this.unlockOptionsService.forceRequest('/api/games/unlock-options', {
|
|
213
|
+
endpoint: '/api/games/unlock-options',
|
|
214
|
+
body,
|
|
215
|
+
method: 'POST'
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async refreshGlobalConfig(): Promise<StandardResponse<GlobalConfig & IUseModalFrequencyConfig>> {
|
|
220
|
+
return await this.globalConfigService.forceRequest('/api/fe-configs/web-common/global-config', {
|
|
221
|
+
endpoint: '/api/fe-configs/web-common/global-config',
|
|
222
|
+
method: 'GET'
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async warmupCache(): Promise<void> {
|
|
227
|
+
await Promise.all([
|
|
228
|
+
this.unlockOptionsService.warmupCache('/api/games/unlock-options', {
|
|
229
|
+
endpoint: '/api/games/unlock-options',
|
|
230
|
+
method: 'POST'
|
|
231
|
+
}),
|
|
232
|
+
this.globalConfigService.warmupCache('/api/fe-configs/web-common/global-config', {
|
|
233
|
+
endpoint: '/api/fe-configs/web-common/global-config',
|
|
234
|
+
method: 'GET'
|
|
235
|
+
})
|
|
236
|
+
]);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
clearAllCache(): void {
|
|
240
|
+
this.unlockOptionsService.clearCache();
|
|
241
|
+
this.globalConfigService.clearCache();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
getCacheStats() {
|
|
245
|
+
return {
|
|
246
|
+
unlockOptions: this.unlockOptionsService.getCacheStats(),
|
|
247
|
+
globalConfig: this.globalConfigService.getCacheStats()
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
clearExpiredCache(): void {
|
|
252
|
+
this.unlockOptionsService.clearExpiredCache();
|
|
253
|
+
this.globalConfigService.clearExpiredCache();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
@@ -1,93 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} from './reward
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const priorityMap: Record<RewardType, number> = {
|
|
19
|
-
JOLI_GEM: 3,
|
|
20
|
-
JOLI_GEM_ONLY: 3,
|
|
21
|
-
JOLI_COIN: 2,
|
|
22
|
-
JOLI_COIN_ONLY: 2,
|
|
23
|
-
ADS: 1
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
return priorityMap[b] - priorityMap[a];
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const sortRewards = (rewardsTypes: RewardType[]): RewardType[] => {
|
|
31
|
-
if (!rewardsTypes.length) return ['ADS'];
|
|
32
|
-
if (rewardsTypes.includes('JOLI_GEM') && rewardsTypes.length <= 1) return ['JOLI_GEM_ONLY'];
|
|
33
|
-
if (rewardsTypes.includes('JOLI_COIN') && rewardsTypes.length <= 1) return ['JOLI_COIN_ONLY'];
|
|
34
|
-
|
|
35
|
-
return rewardsTypes.sort(priority());
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export const createRewardFetcher = (rewardsHelper: RewardsHelper) => {
|
|
39
|
-
rewardsHelper.registerRewardsFetcher(async (httpClient: IHttpClient) => {
|
|
40
|
-
const defaultRewards: RewardType[] = ['ADS'];
|
|
41
|
-
try {
|
|
42
|
-
const res = await httpClient.post<IJolicoinRewardOption>('/api/games/unlock-options', {});
|
|
43
|
-
if (res.code !== 'SUCCESS') {
|
|
44
|
-
return defaultRewards;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
console.log('-----res fetch reward-----', res);
|
|
48
|
-
rewardsEmitter.emit(UnlockOptionsEventName, {
|
|
49
|
-
options: res.data?.unlockOptions || [],
|
|
50
|
-
userJoliCoin: res.extra?.joliCoin || {
|
|
51
|
-
balance: 0,
|
|
52
|
-
enableAutoDeduct: false
|
|
53
|
-
},
|
|
54
|
-
userGem: res.extra?.joliGem || {
|
|
55
|
-
balance: 0,
|
|
56
|
-
enableAutoDeduct: false
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
const rewardsTypes =
|
|
61
|
-
res.data?.unlockOptions?.map((option) => option.type) || Array.from(defaultRewards);
|
|
62
|
-
// Sort reward types with JOLI_COIN having higher priority than ADS
|
|
63
|
-
return sortRewards(rewardsTypes);
|
|
64
|
-
} catch (e) {
|
|
65
|
-
console.error('getRewardOptions error:', e);
|
|
66
|
-
return defaultRewards;
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
export const createRewardFrequencyConfigFetcher = (rewardsHelper: RewardsHelper) => {
|
|
72
|
-
rewardsHelper.registerRewardFrequencyConfigFetcher(async (httpClient: IHttpClient) => {
|
|
73
|
-
const res = await httpClient.get<StandardResponse<GlobalConfig & IUseModalFrequencyConfig>>(
|
|
74
|
-
'/api/fe-configs/web-common/global-config',
|
|
75
|
-
{}
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
rewardsEmitter.emit(UseModalFrequencyEventName, {
|
|
79
|
-
joliCoinUseAndCharge: res.data?.joliCoinUseAndCharge || DefaltJoliCoinUseAndCharge,
|
|
80
|
-
loginGuide: res.data?.loginGuide || DefaltLoginGuide
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
res.data &&
|
|
84
|
-
hostEmitter.emit('onGlobalConfigChanged', {
|
|
85
|
-
globalConfig: res.data
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
joliCoinUseAndCharge: res.data?.joliCoinUseAndCharge || DefaltJoliCoinUseAndCharge,
|
|
90
|
-
loginGuide: res.data?.loginGuide || DefaltLoginGuide
|
|
91
|
-
};
|
|
92
|
-
});
|
|
93
|
-
};
|
|
1
|
+
export {
|
|
2
|
+
createCachedRewardFetcher as createRewardFetcher,
|
|
3
|
+
createCachedRewardFrequencyConfigFetcher as createRewardFrequencyConfigFetcher,
|
|
4
|
+
// cache
|
|
5
|
+
getRewardCacheService,
|
|
6
|
+
refreshRewardCache,
|
|
7
|
+
refreshGlobalConfigCache,
|
|
8
|
+
warmupRewardCache,
|
|
9
|
+
clearRewardCache,
|
|
10
|
+
getRewardCacheStats
|
|
11
|
+
} from './cached-fetch-reward';
|
|
12
|
+
|
|
13
|
+
// original version
|
|
14
|
+
export {
|
|
15
|
+
createRewardFetcher as createRewardFetcherNonCached,
|
|
16
|
+
createRewardFrequencyConfigFetcher as createRewardFrequencyConfigFetcherNonCached
|
|
17
|
+
} from './cached-fetch-reward';
|
|
@@ -10,3 +10,6 @@ export * from './registers/use-jolicoin';
|
|
|
10
10
|
export * from './registers/use-jolicoin-only';
|
|
11
11
|
export * from './registers/use-gem';
|
|
12
12
|
export * from './registers/use-gem-only';
|
|
13
|
+
|
|
14
|
+
export { warmupBalanceCache } from './registers/utils/coins/jolicoin/cached-fetch-balance';
|
|
15
|
+
export { warmupGemBalanceCache } from './registers/utils/coins/joligem/cached-fetch-gem-balance';
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
InvokePaymentEventName
|
|
11
11
|
} from '@/common/rewards/reward-emitter';
|
|
12
12
|
import { CURRENCY_HANDLERS, CurrencyType } from './currency-handlers';
|
|
13
|
+
import { refreshGemBalanceCache } from '../joligem/cached-fetch-gem-balance';
|
|
14
|
+
import { refreshBalanceCache } from '../jolicoin/cached-fetch-balance';
|
|
13
15
|
|
|
14
16
|
export const registerUsePaymentCommand = <T extends IJoliCoin | IGem>(
|
|
15
17
|
prefix: 'JOLI_COIN' | 'ADS-JOLI_COIN' | 'JOLI_GEM' | 'ADS-JOLI_GEM',
|
|
@@ -48,6 +50,12 @@ export const registerUsePaymentCommand = <T extends IJoliCoin | IGem>(
|
|
|
48
50
|
currency: currency as T extends IJoliCoin ? 'JOLI_COIN' : 'JOLI_GEM'
|
|
49
51
|
});
|
|
50
52
|
|
|
53
|
+
if (currency === 'JOLI_GEM') {
|
|
54
|
+
refreshGemBalanceCache(httpClient);
|
|
55
|
+
} else {
|
|
56
|
+
refreshBalanceCache(httpClient);
|
|
57
|
+
}
|
|
58
|
+
|
|
51
59
|
return {
|
|
52
60
|
result: paymentResult === 'SUCCESS' ? 'SUCCESS' : 'FAILED'
|
|
53
61
|
};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { RequestCacheService, RequestAdapter } from '@/common/cache/request-cache-service';
|
|
2
|
+
import { IHttpClient } from '@/common/http';
|
|
3
|
+
import { StandardResponse } from '@jolibox/types';
|
|
4
|
+
|
|
5
|
+
interface BalanceRequest {
|
|
6
|
+
endpoint: string;
|
|
7
|
+
query?: Record<string, unknown>;
|
|
8
|
+
method?: 'GET' | 'POST';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// 定义余额响应类型
|
|
12
|
+
interface BalanceResponse {
|
|
13
|
+
balance: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface BalanceCacheData {
|
|
17
|
+
balance: number;
|
|
18
|
+
timestamp: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class BalanceRequestAdapter
|
|
22
|
+
implements RequestAdapter<BalanceRequest, StandardResponse<BalanceResponse>, BalanceCacheData, undefined>
|
|
23
|
+
{
|
|
24
|
+
constructor(private httpClient: IHttpClient) {}
|
|
25
|
+
|
|
26
|
+
async execute(endpoint: string, options?: BalanceRequest): Promise<StandardResponse<BalanceResponse>> {
|
|
27
|
+
const method = options?.method || 'GET';
|
|
28
|
+
const query = options?.query || {};
|
|
29
|
+
|
|
30
|
+
if (method === 'GET') {
|
|
31
|
+
return await this.httpClient.get<StandardResponse<BalanceResponse>>(endpoint, {
|
|
32
|
+
query: query as Record<string, string>
|
|
33
|
+
});
|
|
34
|
+
} else {
|
|
35
|
+
return await this.httpClient.post<StandardResponse<BalanceResponse>>(endpoint, {
|
|
36
|
+
query: query as Record<string, string>
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
extractCacheableData(response: StandardResponse<BalanceResponse>): BalanceCacheData {
|
|
42
|
+
return {
|
|
43
|
+
balance: response.data?.balance || 0,
|
|
44
|
+
timestamp: Date.now()
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 余额使用纯缓存模式
|
|
49
|
+
extractRealTimeData?: undefined;
|
|
50
|
+
mergeData?: undefined;
|
|
51
|
+
|
|
52
|
+
processCachedData(cached: BalanceCacheData): StandardResponse<BalanceResponse> {
|
|
53
|
+
return {
|
|
54
|
+
code: 'SUCCESS',
|
|
55
|
+
message: 'success from cache',
|
|
56
|
+
data: {
|
|
57
|
+
balance: cached.balance
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 缓存余额服务
|
|
64
|
+
export class CachedBalanceService {
|
|
65
|
+
private balanceService: RequestCacheService<
|
|
66
|
+
BalanceRequest,
|
|
67
|
+
StandardResponse<BalanceResponse>,
|
|
68
|
+
BalanceCacheData,
|
|
69
|
+
undefined
|
|
70
|
+
>;
|
|
71
|
+
|
|
72
|
+
constructor(httpClient: IHttpClient) {
|
|
73
|
+
this.balanceService = new (RequestCacheService as new (...args: unknown[]) => RequestCacheService<
|
|
74
|
+
BalanceRequest,
|
|
75
|
+
StandardResponse<BalanceResponse>,
|
|
76
|
+
BalanceCacheData,
|
|
77
|
+
undefined
|
|
78
|
+
>)(new BalanceRequestAdapter(httpClient), {
|
|
79
|
+
duration: 20 * 60 * 1000, // 10min cache, force update every query
|
|
80
|
+
timeout: 1000, // 1s timeout for request
|
|
81
|
+
keyGenerator: (endpoint: string, params?: BalanceRequest) => {
|
|
82
|
+
const queryStr = params?.query ? JSON.stringify(params.query) : '';
|
|
83
|
+
return `${endpoint}:${queryStr}`;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async getBalance(type = 'JOLI_COIN'): Promise<BalanceResponse | undefined> {
|
|
89
|
+
const response = await this.balanceService.request('/api/joli-coin/balance', {
|
|
90
|
+
endpoint: '/api/joli-coin/balance',
|
|
91
|
+
query: { type },
|
|
92
|
+
method: 'GET'
|
|
93
|
+
});
|
|
94
|
+
return response.data;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async refreshBalance(type = 'JOLI_COIN'): Promise<BalanceResponse | undefined> {
|
|
98
|
+
const response = await this.balanceService.forceRequest('/api/joli-coin/balance', {
|
|
99
|
+
endpoint: '/api/joli-coin/balance',
|
|
100
|
+
query: { type },
|
|
101
|
+
method: 'GET'
|
|
102
|
+
});
|
|
103
|
+
return response.data;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async warmupBalance(type = 'JOLI_COIN'): Promise<void> {
|
|
107
|
+
await this.balanceService.warmupCache('/api/joli-coin/balance', {
|
|
108
|
+
endpoint: '/api/joli-coin/balance',
|
|
109
|
+
query: { type },
|
|
110
|
+
method: 'GET'
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
clearBalanceCache(): void {
|
|
115
|
+
this.balanceService.clearCache();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
getBalanceCacheStats() {
|
|
119
|
+
return this.balanceService.getCacheStats();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
clearExpiredCache(): void {
|
|
123
|
+
this.balanceService.clearExpiredCache();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// create instance
|
|
128
|
+
let cachedBalanceServiceInstance: CachedBalanceService | null = null;
|
|
129
|
+
|
|
130
|
+
function getCachedBalanceService(httpClient: IHttpClient): CachedBalanceService {
|
|
131
|
+
if (!cachedBalanceServiceInstance) {
|
|
132
|
+
cachedBalanceServiceInstance = new CachedBalanceService(httpClient);
|
|
133
|
+
}
|
|
134
|
+
return cachedBalanceServiceInstance;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 缓存版本的 fetchBalance 函数
|
|
139
|
+
*/
|
|
140
|
+
export const fetchBalanceCached = async (httpClient: IHttpClient, type = 'JOLI_COIN') => {
|
|
141
|
+
const cachedBalanceService = getCachedBalanceService(httpClient);
|
|
142
|
+
return await cachedBalanceService.getBalance(type);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 强制刷新余额缓存
|
|
147
|
+
*/
|
|
148
|
+
export const refreshBalanceCache = async (httpClient: IHttpClient, type = 'JOLI_COIN') => {
|
|
149
|
+
const cachedBalanceService = getCachedBalanceService(httpClient);
|
|
150
|
+
const result = await cachedBalanceService.refreshBalance(type);
|
|
151
|
+
console.log(`Balance cache refreshed for type: ${type}`);
|
|
152
|
+
return result;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 预热余额缓存
|
|
157
|
+
*/
|
|
158
|
+
export const warmupBalanceCache = async (httpClient: IHttpClient, type = 'JOLI_COIN') => {
|
|
159
|
+
const cachedBalanceService = getCachedBalanceService(httpClient);
|
|
160
|
+
await cachedBalanceService.warmupBalance(type);
|
|
161
|
+
console.log(`Balance cache warmed up for type: ${type}`);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export const clearBalanceCache = (httpClient: IHttpClient) => {
|
|
165
|
+
const cachedBalanceService = getCachedBalanceService(httpClient);
|
|
166
|
+
cachedBalanceService.clearBalanceCache();
|
|
167
|
+
console.log('Balance cache cleared');
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export const getBalanceCacheStats = (httpClient: IHttpClient) => {
|
|
171
|
+
const cachedBalanceService = getCachedBalanceService(httpClient);
|
|
172
|
+
return cachedBalanceService.getBalanceCacheStats();
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export const getBalanceCacheService = (httpClient: IHttpClient): CachedBalanceService => {
|
|
176
|
+
return getCachedBalanceService(httpClient);
|
|
177
|
+
};
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import { IHttpClient } from '@/common/http';
|
|
2
2
|
import { StandardResponse } from '@jolibox/types';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// 重新导出缓存版本作为默认实现
|
|
5
|
+
export {
|
|
6
|
+
fetchBalanceCached as fetchBalance,
|
|
7
|
+
// 提供缓存管理功能
|
|
8
|
+
refreshBalanceCache,
|
|
9
|
+
warmupBalanceCache,
|
|
10
|
+
clearBalanceCache,
|
|
11
|
+
getBalanceCacheStats,
|
|
12
|
+
getBalanceCacheService
|
|
13
|
+
} from './cached-fetch-balance';
|
|
14
|
+
|
|
15
|
+
// original version
|
|
16
|
+
export const fetchBalanceNonCached = async (httpClient: IHttpClient) => {
|
|
5
17
|
const res = await httpClient.get<
|
|
6
18
|
StandardResponse<{
|
|
7
19
|
balance: number;
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
} from '../commands';
|
|
17
17
|
import { rewardsCommands } from '../rewards-command';
|
|
18
18
|
import { RewardsCommandType } from '@jolibox/types';
|
|
19
|
+
import { refreshBalanceCache, warmupBalanceCache } from './cached-fetch-balance';
|
|
19
20
|
|
|
20
21
|
interface ICurrencyUnlockRes {
|
|
21
22
|
code: 'SUCCESS' | 'BALANCE_NOT_ENOUGH' | 'EPISODE_LOCK_JUMP' | 'EPISODE_UNLOCK_ALREADY';
|
|
@@ -103,6 +104,7 @@ const createCommonCurrencyRewardHandler = (
|
|
|
103
104
|
if ('adViewed' in params) {
|
|
104
105
|
params.adViewed?.();
|
|
105
106
|
}
|
|
107
|
+
refreshBalanceCache(httpClient);
|
|
106
108
|
} catch (e) {
|
|
107
109
|
console.error('-----unlockWithJolicoin adBreakDone error-----', e);
|
|
108
110
|
}
|