@jolibox/implement 1.2.4 → 1.2.5-beta.3
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 +36 -23
- package/CHANGELOG.json +11 -0
- package/CHANGELOG.md +9 -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 +3 -0
- package/dist/common/rewards/registers/use-subscription.d.ts +7 -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/common/rewards/registers/utils/subscription/commands/index.d.ts +1 -0
- package/dist/common/rewards/registers/utils/subscription/commands/use-subscription.d.ts +4 -0
- package/dist/common/rewards/registers/utils/subscription/sub-handler.d.ts +13 -0
- package/dist/common/rewards/reward-emitter.d.ts +7 -0
- package/dist/common/rewards/reward-helper.d.ts +2 -1
- package/dist/common/utils/index.d.ts +18 -0
- package/dist/h5/api/platformAdsHandle/JoliboxAdsHandler.d.ts +1 -0
- package/dist/h5/bootstrap/auth/__tests__/auth.test.d.ts +1 -0
- package/dist/h5/bootstrap/auth/index.d.ts +2 -0
- package/dist/h5/bootstrap/auth/sub.d.ts +2 -0
- package/dist/index.js +9 -9
- package/dist/index.native.js +49 -49
- package/dist/native/payment/payment-service.d.ts +36 -30
- package/implement.build.log +2 -2
- package/package.json +7 -7
- package/src/common/report/base-tracker.ts +2 -2
- package/src/common/rewards/cached-fetch-reward.ts +258 -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 +4 -0
- package/src/common/rewards/registers/use-subscription.ts +34 -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/common/rewards/registers/utils/subscription/commands/index.ts +1 -0
- package/src/common/rewards/registers/utils/subscription/commands/use-subscription.ts +29 -0
- package/src/common/rewards/registers/utils/subscription/sub-handler.ts +88 -0
- package/src/common/rewards/reward-emitter.ts +8 -0
- package/src/common/rewards/reward-helper.ts +8 -1
- package/src/common/utils/index.ts +23 -0
- package/src/h5/api/ads.ts +18 -12
- package/src/h5/api/platformAdsHandle/JoliboxAdsHandler.ts +25 -1
- package/src/h5/api/storage.ts +2 -2
- package/src/h5/bootstrap/auth/__tests__/auth.test.ts +308 -0
- package/src/h5/bootstrap/auth/index.ts +20 -0
- package/src/h5/bootstrap/auth/sub.ts +56 -0
- package/src/h5/bootstrap/index.ts +4 -19
- package/src/h5/http/index.ts +2 -2
- package/src/h5/report/event-tracker.ts +2 -2
- package/src/h5/rewards/index.ts +18 -1
- 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
|
@@ -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,7 @@ 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
|
+
export * from './registers/use-subscription';
|
|
14
|
+
|
|
15
|
+
export { warmupBalanceCache } from './registers/utils/coins/jolicoin/cached-fetch-balance';
|
|
16
|
+
export { warmupGemBalanceCache } from './registers/utils/coins/joligem/cached-fetch-gem-balance';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { IHttpClient } from '@/common/http';
|
|
2
|
+
import { IAdBreakParams } from '@/common/ads';
|
|
3
|
+
import { unlockOptionsHandler } from './utils/common';
|
|
4
|
+
import { createCommonSubscriptionHandler } from './utils/subscription/sub-handler';
|
|
5
|
+
import { createShowSubscriptionModal } from './utils/subscription/commands/use-subscription';
|
|
6
|
+
|
|
7
|
+
export type SubscriptionRewardsHandler = (params: IAdBreakParams) => Promise<boolean>;
|
|
8
|
+
export const createSubscriptionRewardHandler = (
|
|
9
|
+
httpClient: IHttpClient,
|
|
10
|
+
{
|
|
11
|
+
onSubSuccess,
|
|
12
|
+
onSubFailed
|
|
13
|
+
}: {
|
|
14
|
+
onSubSuccess?: () => void;
|
|
15
|
+
onSubFailed?: () => void;
|
|
16
|
+
}
|
|
17
|
+
): SubscriptionRewardsHandler => {
|
|
18
|
+
const handleUnlockFailed = () => {
|
|
19
|
+
onSubFailed?.();
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const showSubscriptionModal = createShowSubscriptionModal('SUBSCRIPTION');
|
|
23
|
+
|
|
24
|
+
const subscriptionRewardHandler = createCommonSubscriptionHandler('SUBSCRIPTION', httpClient, {
|
|
25
|
+
handlers: {
|
|
26
|
+
handleSubSuccess: onSubSuccess,
|
|
27
|
+
handleSubFailed: handleUnlockFailed,
|
|
28
|
+
unlockOptionsHandler,
|
|
29
|
+
showSubscriptionModal
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return subscriptionRewardHandler;
|
|
34
|
+
};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { RequestCacheService, RequestAdapter } from '@jolibox/common';
|
|
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
|
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { RequestCacheService, RequestAdapter } from '@jolibox/common';
|
|
2
|
+
import { IHttpClient } from '@/common/http';
|
|
3
|
+
import { StandardResponse } from '@jolibox/types';
|
|
4
|
+
|
|
5
|
+
interface GemBalanceRequest {
|
|
6
|
+
endpoint: string;
|
|
7
|
+
query?: Record<string, unknown>;
|
|
8
|
+
method?: 'GET' | 'POST';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// 定义宝石余额响应类型
|
|
12
|
+
interface GemBalanceResponse {
|
|
13
|
+
balance: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface GemBalanceCacheData {
|
|
17
|
+
balance: number;
|
|
18
|
+
timestamp: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class GemBalanceRequestAdapter
|
|
22
|
+
implements
|
|
23
|
+
RequestAdapter<GemBalanceRequest, StandardResponse<GemBalanceResponse>, GemBalanceCacheData, undefined>
|
|
24
|
+
{
|
|
25
|
+
constructor(private httpClient: IHttpClient) {}
|
|
26
|
+
|
|
27
|
+
async execute(
|
|
28
|
+
endpoint: string,
|
|
29
|
+
options?: GemBalanceRequest
|
|
30
|
+
): Promise<StandardResponse<GemBalanceResponse>> {
|
|
31
|
+
const method = options?.method || 'GET';
|
|
32
|
+
const query = options?.query || {};
|
|
33
|
+
|
|
34
|
+
if (method === 'GET') {
|
|
35
|
+
return await this.httpClient.get<StandardResponse<GemBalanceResponse>>(endpoint, {
|
|
36
|
+
query: query as Record<string, string>
|
|
37
|
+
});
|
|
38
|
+
} else {
|
|
39
|
+
return await this.httpClient.post<StandardResponse<GemBalanceResponse>>(endpoint, {
|
|
40
|
+
query: query as Record<string, string>
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
extractCacheableData(response: StandardResponse<GemBalanceResponse>): GemBalanceCacheData {
|
|
46
|
+
return {
|
|
47
|
+
balance: response.data?.balance || 0,
|
|
48
|
+
timestamp: Date.now()
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 宝石余额使用纯缓存模式
|
|
53
|
+
extractRealTimeData?: undefined;
|
|
54
|
+
mergeData?: undefined;
|
|
55
|
+
|
|
56
|
+
processCachedData(cached: GemBalanceCacheData): StandardResponse<GemBalanceResponse> {
|
|
57
|
+
return {
|
|
58
|
+
code: 'SUCCESS',
|
|
59
|
+
message: 'success from cache',
|
|
60
|
+
data: {
|
|
61
|
+
balance: cached.balance
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 缓存宝石余额服务
|
|
68
|
+
export class CachedGemBalanceService {
|
|
69
|
+
private gemBalanceService: RequestCacheService<
|
|
70
|
+
GemBalanceRequest,
|
|
71
|
+
StandardResponse<GemBalanceResponse>,
|
|
72
|
+
GemBalanceCacheData,
|
|
73
|
+
undefined
|
|
74
|
+
>;
|
|
75
|
+
|
|
76
|
+
constructor(httpClient: IHttpClient) {
|
|
77
|
+
this.gemBalanceService = new (RequestCacheService as new (...args: unknown[]) => RequestCacheService<
|
|
78
|
+
GemBalanceRequest,
|
|
79
|
+
StandardResponse<GemBalanceResponse>,
|
|
80
|
+
GemBalanceCacheData,
|
|
81
|
+
undefined
|
|
82
|
+
>)(new GemBalanceRequestAdapter(httpClient), {
|
|
83
|
+
duration: 20 * 60 * 1000, // 20分钟缓存(宝石余额变化较少)
|
|
84
|
+
timeout: 1000, // 1s timeout for request
|
|
85
|
+
keyGenerator: (endpoint: string, params?: GemBalanceRequest) => {
|
|
86
|
+
const queryStr = params?.query ? JSON.stringify(params.query) : '';
|
|
87
|
+
return `${endpoint}:${queryStr}`;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async getGemBalance(type = 'GEM'): Promise<GemBalanceResponse | undefined> {
|
|
93
|
+
const response = await this.gemBalanceService.request('/api/joli-gem/balance', {
|
|
94
|
+
endpoint: '/api/joli-gem/balance',
|
|
95
|
+
query: { type },
|
|
96
|
+
method: 'GET'
|
|
97
|
+
});
|
|
98
|
+
return response.data;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async refreshGemBalance(type = 'GEM'): Promise<GemBalanceResponse | undefined> {
|
|
102
|
+
const response = await this.gemBalanceService.forceRequest('/api/joli-gem/balance', {
|
|
103
|
+
endpoint: '/api/joli-gem/balance',
|
|
104
|
+
query: { type },
|
|
105
|
+
method: 'GET'
|
|
106
|
+
});
|
|
107
|
+
return response.data;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async warmupGemBalance(type = 'GEM'): Promise<void> {
|
|
111
|
+
await this.gemBalanceService.warmupCache('/api/joli-gem/balance', {
|
|
112
|
+
endpoint: '/api/joli-gem/balance',
|
|
113
|
+
query: { type },
|
|
114
|
+
method: 'GET'
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
clearGemBalanceCache(): void {
|
|
119
|
+
this.gemBalanceService.clearCache();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
getGemBalanceCacheStats() {
|
|
123
|
+
return this.gemBalanceService.getCacheStats();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
clearExpiredCache(): void {
|
|
127
|
+
this.gemBalanceService.clearExpiredCache();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// create instance
|
|
132
|
+
let cachedGemBalanceServiceInstance: CachedGemBalanceService | null = null;
|
|
133
|
+
|
|
134
|
+
function getCachedGemBalanceService(httpClient: IHttpClient): CachedGemBalanceService {
|
|
135
|
+
if (!cachedGemBalanceServiceInstance) {
|
|
136
|
+
cachedGemBalanceServiceInstance = new CachedGemBalanceService(httpClient);
|
|
137
|
+
}
|
|
138
|
+
return cachedGemBalanceServiceInstance;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 缓存版本的 fetchGemBalance 函数
|
|
143
|
+
*/
|
|
144
|
+
export const fetchGemBalanceCached = async (httpClient: IHttpClient, type = 'GEM') => {
|
|
145
|
+
const cachedGemBalanceService = getCachedGemBalanceService(httpClient);
|
|
146
|
+
return await cachedGemBalanceService.getGemBalance(type);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* 强制刷新宝石余额缓存
|
|
151
|
+
*/
|
|
152
|
+
export const refreshGemBalanceCache = async (httpClient: IHttpClient, type = 'GEM') => {
|
|
153
|
+
const cachedGemBalanceService = getCachedGemBalanceService(httpClient);
|
|
154
|
+
const result = await cachedGemBalanceService.refreshGemBalance(type);
|
|
155
|
+
console.log(`Gem balance cache refreshed for type: ${type}`);
|
|
156
|
+
return result;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 预热宝石余额缓存
|
|
161
|
+
*/
|
|
162
|
+
export const warmupGemBalanceCache = async (httpClient: IHttpClient, type = 'GEM') => {
|
|
163
|
+
const cachedGemBalanceService = getCachedGemBalanceService(httpClient);
|
|
164
|
+
await cachedGemBalanceService.warmupGemBalance(type);
|
|
165
|
+
console.log(`Gem balance cache warmed up for type: ${type}`);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export const clearGemBalanceCache = (httpClient: IHttpClient) => {
|
|
169
|
+
const cachedGemBalanceService = getCachedGemBalanceService(httpClient);
|
|
170
|
+
cachedGemBalanceService.clearGemBalanceCache();
|
|
171
|
+
console.log('Gem balance cache cleared');
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
export const getGemBalanceCacheStats = (httpClient: IHttpClient) => {
|
|
175
|
+
const cachedGemBalanceService = getCachedGemBalanceService(httpClient);
|
|
176
|
+
return cachedGemBalanceService.getGemBalanceCacheStats();
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export const getGemBalanceCacheService = (httpClient: IHttpClient): CachedGemBalanceService => {
|
|
180
|
+
return getCachedGemBalanceService(httpClient);
|
|
181
|
+
};
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import { IHttpClient } from '@/common/http';
|
|
2
2
|
import { StandardResponse } from '@jolibox/types';
|
|
3
3
|
|
|
4
|
-
export
|
|
4
|
+
// export cached version as default implementation
|
|
5
|
+
export {
|
|
6
|
+
fetchGemBalanceCached as fetchGemBalance,
|
|
7
|
+
// 提供缓存管理功能
|
|
8
|
+
refreshGemBalanceCache,
|
|
9
|
+
warmupGemBalanceCache,
|
|
10
|
+
clearGemBalanceCache,
|
|
11
|
+
getGemBalanceCacheStats,
|
|
12
|
+
getGemBalanceCacheService
|
|
13
|
+
} from './cached-fetch-gem-balance';
|
|
14
|
+
|
|
15
|
+
// original version
|
|
16
|
+
export const fetchGemBalanceNonCached = async (httpClient: IHttpClient) => {
|
|
5
17
|
const res = await httpClient.get<
|
|
6
18
|
StandardResponse<{
|
|
7
19
|
balance: number;
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
import { registerUseJolicoinCommand, createShowUnlockWithCurrencyModal } from '../commands/use-jolicoin';
|
|
16
16
|
import { rewardsCommands } from '../rewards-command';
|
|
17
17
|
import { RewardsCommandType } from '@jolibox/types';
|
|
18
|
+
import { refreshGemBalanceCache, warmupGemBalanceCache } from './cached-fetch-gem-balance';
|
|
18
19
|
|
|
19
20
|
interface IGemUnlockRes {
|
|
20
21
|
code: 'SUCCESS' | 'BALANCE_NOT_ENOUGH' | 'EPISODE_LOCK_JUMP' | 'EPISODE_UNLOCK_ALREADY';
|
|
@@ -103,6 +104,7 @@ export const createCommonGemRewardHandler = (
|
|
|
103
104
|
if ('adViewed' in params) {
|
|
104
105
|
params.adViewed?.();
|
|
105
106
|
}
|
|
107
|
+
refreshGemBalanceCache(httpClient);
|
|
106
108
|
} catch (e) {
|
|
107
109
|
console.error('-----unlockWithGem adBreakDone error-----', e);
|
|
108
110
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './use-subscription';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { rewardsCommands } from '../../coins/rewards-command';
|
|
2
|
+
import { createEventPromiseHandler } from '../../event-listener';
|
|
3
|
+
import { IUseSubscriptionResultEvent, UseSubscriptionResultEventName } from '@/common/rewards/reward-emitter';
|
|
4
|
+
import { rewardsEmitter, InvokeSubscriptionEventName } from '@/common/rewards/reward-emitter';
|
|
5
|
+
|
|
6
|
+
export const registerUseSubscriptionCommand = (params: {
|
|
7
|
+
showSubscriptionModal: () => Promise<'SUCCESS' | 'CANCEL' | 'FAILED'>;
|
|
8
|
+
}) => {
|
|
9
|
+
const { showSubscriptionModal } = params;
|
|
10
|
+
rewardsCommands.registerCommand(`Rewards.subscription`, async () => {
|
|
11
|
+
const subscriptionResult = await showSubscriptionModal();
|
|
12
|
+
return {
|
|
13
|
+
result: subscriptionResult === 'SUCCESS' ? 'SUCCESS' : 'FAILED'
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// EVENTS
|
|
19
|
+
export const createShowSubscriptionModal = (type: 'SUBSCRIPTION') => {
|
|
20
|
+
return async () => {
|
|
21
|
+
const modalHandler = createEventPromiseHandler<
|
|
22
|
+
IUseSubscriptionResultEvent,
|
|
23
|
+
typeof UseSubscriptionResultEventName
|
|
24
|
+
>(rewardsEmitter, UseSubscriptionResultEventName);
|
|
25
|
+
rewardsEmitter.emit(InvokeSubscriptionEventName, type);
|
|
26
|
+
const modalResult = await modalHandler.getFreshData();
|
|
27
|
+
return modalResult.result;
|
|
28
|
+
};
|
|
29
|
+
};
|