@jolibox/implement 1.2.3 → 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 +44 -28
- package/.rush/temp/shrinkwrap-deps.json +2 -1
- package/CHANGELOG.json +11 -0
- package/CHANGELOG.md +9 -0
- package/dist/common/context/index.d.ts +5 -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 -13
- package/dist/index.native.js +49 -53
- package/dist/native/api/index.d.ts +1 -0
- package/dist/native/api/payment.d.ts +1 -0
- package/dist/native/payment/__tests__/payment-service-simple.test.d.ts +1 -0
- package/dist/native/payment/payment-helper.d.ts +8 -5
- package/dist/native/payment/payment-service.d.ts +44 -0
- package/implement.build.log +2 -2
- package/package.json +8 -7
- package/src/common/context/index.ts +12 -0
- 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/call-host-method.ts +1 -1
- package/src/native/api/index.ts +1 -0
- package/src/native/api/navigate.ts +10 -1
- package/src/native/api/payment.ts +56 -0
- package/src/native/payment/__tests__/payment-service-simple.test.ts +274 -0
- package/src/native/payment/payment-helper.ts +10 -4
- package/src/native/payment/payment-service.ts +293 -0
- package/src/native/payment/registers/jolicoin-iap.ts +4 -4
- package/src/native/report/index.ts +4 -1
- package/src/native/rewards/ui/payment-modal.ts +20 -60
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
export type PaymentType = 'JOLI_COIN' | 'JOLI_COIN_IAP' | 'JOLI_GEM_IAP';
|
|
2
2
|
import { StandardResponse } from '@jolibox/types';
|
|
3
|
-
type PaymentResult = StandardResponse<
|
|
3
|
+
export type PaymentResult<T> = StandardResponse<T>;
|
|
4
4
|
export interface PaymentHandlerMap {
|
|
5
|
-
JOLI_COIN: (productId: string) => Promise<PaymentResult
|
|
5
|
+
JOLI_COIN: (productId: string) => Promise<PaymentResult<void>>;
|
|
6
6
|
JOLI_COIN_IAP: (params: {
|
|
7
7
|
productId: string;
|
|
8
8
|
appStoreProductId: string;
|
|
9
|
-
}) => Promise<PaymentResult
|
|
9
|
+
}) => Promise<PaymentResult<{
|
|
10
|
+
totalAmount: string;
|
|
11
|
+
}>>;
|
|
10
12
|
JOLI_GEM_IAP: (params: {
|
|
11
13
|
productId: string;
|
|
12
14
|
appStoreProductId: string;
|
|
13
|
-
}) => Promise<PaymentResult
|
|
15
|
+
}) => Promise<PaymentResult<{
|
|
16
|
+
totalAmount: string;
|
|
17
|
+
}>>;
|
|
14
18
|
}
|
|
15
19
|
export type PaymentHandler<T extends PaymentType> = PaymentHandlerMap[T];
|
|
16
20
|
export declare function createPaymentHelper(): {
|
|
@@ -18,4 +22,3 @@ export declare function createPaymentHelper(): {
|
|
|
18
22
|
invokePayment<T extends PaymentType>(type: T, ...args: Parameters<PaymentHandler<T>>): Promise<any>;
|
|
19
23
|
};
|
|
20
24
|
export type PaymentHelper = ReturnType<typeof createPaymentHelper>;
|
|
21
|
-
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { IPaymentChoice } from '@/common/rewards/reward-emitter';
|
|
2
|
+
import type { PaymentResult } from './payment-helper';
|
|
3
|
+
import { RequestCacheService } from '@jolibox/common';
|
|
4
|
+
type PaymentPurchaseType = 'JOLI_COIN' | 'JOLI_GEM';
|
|
5
|
+
type PaymentRequest = Record<string, never>;
|
|
6
|
+
interface PaymentResponse {
|
|
7
|
+
balance: number;
|
|
8
|
+
enableAutoDeduct: boolean;
|
|
9
|
+
paymentChoices: IPaymentChoice[];
|
|
10
|
+
}
|
|
11
|
+
interface PaymentCacheData {
|
|
12
|
+
paymentChoices: IPaymentChoice[];
|
|
13
|
+
}
|
|
14
|
+
interface PaymentRealTimeData {
|
|
15
|
+
balance: number;
|
|
16
|
+
enableAutoDeduct: boolean;
|
|
17
|
+
}
|
|
18
|
+
declare class BasePaymentService extends RequestCacheService<PaymentRequest, PaymentResponse, PaymentCacheData, PaymentRealTimeData> {
|
|
19
|
+
private apiEndpoint;
|
|
20
|
+
private paymentType;
|
|
21
|
+
private static failureCounters;
|
|
22
|
+
private static readonly MAX_FAILURE_COUNT;
|
|
23
|
+
constructor(apiEndpoint: string, paymentType: PaymentPurchaseType);
|
|
24
|
+
getProductsInfo(): Promise<PaymentResponse | undefined>;
|
|
25
|
+
purchase(productId: string): Promise<PaymentResult<{
|
|
26
|
+
totalAmount: string;
|
|
27
|
+
}>>;
|
|
28
|
+
getProductsInfoWithBalance(): Promise<PaymentResponse | undefined>;
|
|
29
|
+
refreshProductsInfo(): Promise<PaymentResponse | undefined>;
|
|
30
|
+
static resetFailureCounters(): void;
|
|
31
|
+
static getFailureCount(endpoint: string): number;
|
|
32
|
+
}
|
|
33
|
+
export declare class JoliCoinPaymentService extends BasePaymentService {
|
|
34
|
+
constructor();
|
|
35
|
+
}
|
|
36
|
+
export declare class JoliGemPaymentService extends BasePaymentService {
|
|
37
|
+
constructor();
|
|
38
|
+
}
|
|
39
|
+
export declare const createJoliCoinPaymentService: () => JoliCoinPaymentService;
|
|
40
|
+
export declare const createJoliGemPaymentService: () => JoliGemPaymentService;
|
|
41
|
+
export declare const clearNativePriceCache: () => void;
|
|
42
|
+
export declare const resetFailureCounters: () => void;
|
|
43
|
+
export declare const getFailureCount: (endpoint: string) => number;
|
|
44
|
+
export {};
|
package/implement.build.log
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Invoking: npm run clean && npm run build:esm && tsc
|
|
2
2
|
|
|
3
|
-
> @jolibox/implement@1.2.3 clean
|
|
3
|
+
> @jolibox/implement@1.2.5-beta.3 clean
|
|
4
4
|
> rimraf ./dist
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
> @jolibox/implement@1.2.3 build:esm
|
|
7
|
+
> @jolibox/implement@1.2.5-beta.3 build:esm
|
|
8
8
|
> BUILD_VERSION=$(node -p "require('./package.json').version") node esbuild.config.js --format=esm
|
|
9
9
|
|
package/package.json
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jolibox/implement",
|
|
3
3
|
"description": "This project is Jolibox JS-SDk implement for Native && H5",
|
|
4
|
-
"version": "1.2.3",
|
|
4
|
+
"version": "1.2.5-beta.3",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@jolibox/common": "1.2.3",
|
|
10
|
-
"@jolibox/types": "1.2.3",
|
|
11
|
-
"@jolibox/native-bridge": "1.2.3",
|
|
12
|
-
"@jolibox/ads": "1.2.3",
|
|
9
|
+
"@jolibox/common": "1.2.5-beta.3",
|
|
10
|
+
"@jolibox/types": "1.2.5-beta.3",
|
|
11
|
+
"@jolibox/native-bridge": "1.2.5-beta.3",
|
|
12
|
+
"@jolibox/ads": "1.2.5-beta.3",
|
|
13
13
|
"localforage": "1.10.0",
|
|
14
|
-
"@jolibox/ui": "1.
|
|
14
|
+
"@jolibox/ui": "1.2.5-beta.3",
|
|
15
15
|
"web-vitals": "4.2.4"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"typescript": "5.7.3",
|
|
19
19
|
"@types/jest": "28.1.1",
|
|
20
|
+
"@types/node": "18.0.0",
|
|
20
21
|
"rimraf": "6.0.1",
|
|
21
22
|
"esbuild": "0.24.2",
|
|
22
|
-
"@jolibox/eslint-config": "1.0.
|
|
23
|
+
"@jolibox/eslint-config": "1.0.1-beta.11"
|
|
23
24
|
},
|
|
24
25
|
"scripts": {
|
|
25
26
|
"clean": "rimraf ./dist",
|
|
@@ -49,6 +49,11 @@ type Viewport = {
|
|
|
49
49
|
navigationBarHeight: number;
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
+
type MPInfo = {
|
|
53
|
+
mpName: string;
|
|
54
|
+
mpVersion?: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
52
57
|
function hasMetaTag(name: string, content: string): boolean {
|
|
53
58
|
return document?.head.querySelector(`meta[name="${name}"][content="${content}"]`) !== null;
|
|
54
59
|
}
|
|
@@ -165,6 +170,13 @@ const wrapContext = () => {
|
|
|
165
170
|
get abTests(): string[] {
|
|
166
171
|
return env.abValues?.split(',') ?? [];
|
|
167
172
|
},
|
|
173
|
+
get mpInfo(): MPInfo {
|
|
174
|
+
return (
|
|
175
|
+
env.mpInfo ?? {
|
|
176
|
+
mpName: 'unknown'
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
},
|
|
168
180
|
onEnvConfigChanged: (newConfig: Partial<Env>) => {
|
|
169
181
|
mergeWith(env, newConfig, mergeArray);
|
|
170
182
|
},
|
|
@@ -22,12 +22,12 @@ export abstract class EventTracker {
|
|
|
22
22
|
|
|
23
23
|
private samplesConfig: ISamplesConfig | null = null;
|
|
24
24
|
|
|
25
|
-
constructor() {
|
|
25
|
+
constructor(readonly apiHost: string) {
|
|
26
26
|
this.fetchSamplesConfig();
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
private async fetchSamplesConfig() {
|
|
30
|
-
const host =
|
|
30
|
+
const host = this.apiHost;
|
|
31
31
|
const path = `${host}/api/fe-configs/js-sdk/samples-config`;
|
|
32
32
|
const samplesConfig = (await (await fetch(path)).json()) as ISamplesConfig;
|
|
33
33
|
this.samplesConfig = samplesConfig;
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { IHttpClient } from '../http';
|
|
2
|
+
import { RewardsHelper, RewardType } from './reward-helper';
|
|
3
|
+
import { CachedRewardService } from './cached-reward-service';
|
|
4
|
+
import { IUnlockOption } from './type';
|
|
5
|
+
import {
|
|
6
|
+
UnlockOptionsEventName,
|
|
7
|
+
UseModalFrequencyEventName,
|
|
8
|
+
rewardsEmitter,
|
|
9
|
+
DefaltJoliCoinUseAndCharge,
|
|
10
|
+
DefaltLoginGuide
|
|
11
|
+
} from './reward-emitter';
|
|
12
|
+
import { hostEmitter } from '@jolibox/common';
|
|
13
|
+
import { StandardResponse, GlobalConfig } from '@jolibox/types';
|
|
14
|
+
|
|
15
|
+
const priority = () => {
|
|
16
|
+
return (a: RewardType, b: RewardType) => {
|
|
17
|
+
// Priority order: GEM > JOLI_COIN > ADS
|
|
18
|
+
const priorityMap: Record<RewardType, number> = {
|
|
19
|
+
SUBSCRIPTION: 4,
|
|
20
|
+
JOLI_GEM: 3,
|
|
21
|
+
JOLI_GEM_ONLY: 3,
|
|
22
|
+
JOLI_COIN: 2,
|
|
23
|
+
JOLI_COIN_ONLY: 2,
|
|
24
|
+
ADS: 1
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return priorityMap[b] - priorityMap[a];
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const sortRewards = (rewardsTypes: RewardType[]): RewardType[] => {
|
|
32
|
+
if (!rewardsTypes.length) return ['ADS'];
|
|
33
|
+
|
|
34
|
+
// Handle JOLI_GEM cases
|
|
35
|
+
if (rewardsTypes.includes('JOLI_GEM')) {
|
|
36
|
+
if (rewardsTypes.length === 1) {
|
|
37
|
+
return ['JOLI_GEM_ONLY'];
|
|
38
|
+
} else if (rewardsTypes.includes('SUBSCRIPTION') && rewardsTypes.length === 2) {
|
|
39
|
+
return ['SUBSCRIPTION', 'JOLI_GEM_ONLY'];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Handle JOLI_COIN cases
|
|
44
|
+
if (rewardsTypes.includes('JOLI_COIN')) {
|
|
45
|
+
if (rewardsTypes.length === 1) {
|
|
46
|
+
return ['JOLI_COIN_ONLY'];
|
|
47
|
+
} else if (rewardsTypes.includes('SUBSCRIPTION') && rewardsTypes.length === 2) {
|
|
48
|
+
return ['SUBSCRIPTION', 'JOLI_COIN_ONLY'];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return rewardsTypes.sort(priority());
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// 创建缓存奖励服务的单例
|
|
56
|
+
let cachedRewardServiceInstance: CachedRewardService | null = null;
|
|
57
|
+
|
|
58
|
+
function getCachedRewardService(httpClient: IHttpClient): CachedRewardService {
|
|
59
|
+
if (!cachedRewardServiceInstance) {
|
|
60
|
+
cachedRewardServiceInstance = new CachedRewardService(httpClient);
|
|
61
|
+
}
|
|
62
|
+
return cachedRewardServiceInstance;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 使用缓存的奖励获取器
|
|
67
|
+
* 优化版本:unlockOptions 使用缓存,余额信息实时获取
|
|
68
|
+
*/
|
|
69
|
+
export const createCachedRewardFetcher = (rewardsHelper: RewardsHelper) => {
|
|
70
|
+
rewardsHelper.registerRewardsFetcher(async (httpClient: IHttpClient) => {
|
|
71
|
+
const defaultRewards: RewardType[] = ['ADS'];
|
|
72
|
+
const cachedRewardService = getCachedRewardService(httpClient);
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const res = await cachedRewardService.getUnlockOptions({});
|
|
76
|
+
|
|
77
|
+
if (res.code !== 'SUCCESS') {
|
|
78
|
+
return defaultRewards;
|
|
79
|
+
}
|
|
80
|
+
// 发送事件通知
|
|
81
|
+
rewardsEmitter.emit(UnlockOptionsEventName, {
|
|
82
|
+
options: res.data?.unlockOptions || [],
|
|
83
|
+
userJoliCoin: res.extra?.joliCoin || {
|
|
84
|
+
balance: 0,
|
|
85
|
+
enableAutoDeduct: false
|
|
86
|
+
},
|
|
87
|
+
userGem: res.extra?.joliGem || {
|
|
88
|
+
balance: 0,
|
|
89
|
+
enableAutoDeduct: false
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const rewardsTypes =
|
|
94
|
+
res.data?.unlockOptions?.map((option: IUnlockOption) => option.type) || Array.from(defaultRewards);
|
|
95
|
+
|
|
96
|
+
return sortRewards(rewardsTypes);
|
|
97
|
+
} catch (e) {
|
|
98
|
+
console.error('getRewardOptions error (cached):', e);
|
|
99
|
+
return defaultRewards;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* 使用缓存的频率配置获取器
|
|
106
|
+
* 优化版本:全局配置使用纯缓存
|
|
107
|
+
*/
|
|
108
|
+
export const createCachedRewardFrequencyConfigFetcher = (rewardsHelper: RewardsHelper) => {
|
|
109
|
+
rewardsHelper.registerRewardFrequencyConfigFetcher(async (httpClient: IHttpClient) => {
|
|
110
|
+
const cachedRewardService = getCachedRewardService(httpClient);
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
const res = await cachedRewardService.getGlobalConfig();
|
|
114
|
+
|
|
115
|
+
// 发送事件通知
|
|
116
|
+
rewardsEmitter.emit(UseModalFrequencyEventName, {
|
|
117
|
+
joliCoinUseAndCharge: res.data?.joliCoinUseAndCharge || DefaltJoliCoinUseAndCharge,
|
|
118
|
+
loginGuide: res.data?.loginGuide || DefaltLoginGuide
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
res.data &&
|
|
122
|
+
hostEmitter.emit('onGlobalConfigChanged', {
|
|
123
|
+
globalConfig: res.data
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
joliCoinUseAndCharge: res.data?.joliCoinUseAndCharge || DefaltJoliCoinUseAndCharge,
|
|
128
|
+
loginGuide: res.data?.loginGuide || DefaltLoginGuide
|
|
129
|
+
};
|
|
130
|
+
} catch (e) {
|
|
131
|
+
console.error('getGlobalConfig error (cached):', e);
|
|
132
|
+
// 发送兜底事件通知
|
|
133
|
+
rewardsEmitter.emit(UseModalFrequencyEventName, {
|
|
134
|
+
joliCoinUseAndCharge: DefaltJoliCoinUseAndCharge,
|
|
135
|
+
loginGuide: DefaltLoginGuide
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
hostEmitter.emit('onGlobalConfigChanged', {
|
|
139
|
+
globalConfig: {
|
|
140
|
+
joliCoinUseAndCharge: DefaltJoliCoinUseAndCharge,
|
|
141
|
+
loginGuide: DefaltLoginGuide
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
return {
|
|
145
|
+
joliCoinUseAndCharge: DefaltJoliCoinUseAndCharge,
|
|
146
|
+
loginGuide: DefaltLoginGuide
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 获取缓存服务实例(用于手动管理缓存)
|
|
154
|
+
*/
|
|
155
|
+
export const getRewardCacheService = (httpClient: IHttpClient): CachedRewardService => {
|
|
156
|
+
return getCachedRewardService(httpClient);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 强制刷新奖励数据缓存
|
|
161
|
+
*/
|
|
162
|
+
export const refreshRewardCache = async (httpClient: IHttpClient) => {
|
|
163
|
+
const cachedRewardService = getCachedRewardService(httpClient);
|
|
164
|
+
await cachedRewardService.refreshUnlockOptions({});
|
|
165
|
+
console.log('Reward cache refreshed');
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 强制刷新全局配置缓存
|
|
170
|
+
*/
|
|
171
|
+
export const refreshGlobalConfigCache = async (httpClient: IHttpClient) => {
|
|
172
|
+
const cachedRewardService = getCachedRewardService(httpClient);
|
|
173
|
+
await cachedRewardService.refreshGlobalConfig();
|
|
174
|
+
console.log('Global config cache refreshed');
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 预热所有缓存
|
|
179
|
+
*/
|
|
180
|
+
export const warmupRewardCache = async (httpClient: IHttpClient) => {
|
|
181
|
+
const cachedRewardService = getCachedRewardService(httpClient);
|
|
182
|
+
await cachedRewardService.warmupCache();
|
|
183
|
+
console.log('Reward cache warmed up');
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* 清除所有奖励相关缓存
|
|
188
|
+
*/
|
|
189
|
+
export const clearRewardCache = (httpClient: IHttpClient) => {
|
|
190
|
+
const cachedRewardService = getCachedRewardService(httpClient);
|
|
191
|
+
cachedRewardService.clearAllCache();
|
|
192
|
+
console.log('All reward cache cleared');
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* 获取缓存统计信息
|
|
197
|
+
*/
|
|
198
|
+
export const getRewardCacheStats = (httpClient: IHttpClient) => {
|
|
199
|
+
const cachedRewardService = getCachedRewardService(httpClient);
|
|
200
|
+
return cachedRewardService.getCacheStats();
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
// 保持原有的创建器以便向后兼容
|
|
204
|
+
export const createRewardFetcher = (rewardsHelper: RewardsHelper) => {
|
|
205
|
+
rewardsHelper.registerRewardsFetcher(async (httpClient: IHttpClient) => {
|
|
206
|
+
const defaultRewards: RewardType[] = ['ADS'];
|
|
207
|
+
try {
|
|
208
|
+
const res = await httpClient.post('/api/games/unlock-options', {});
|
|
209
|
+
if (res.code !== 'SUCCESS') {
|
|
210
|
+
return defaultRewards;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
console.log('-----res fetch reward-----', res);
|
|
214
|
+
rewardsEmitter.emit(UnlockOptionsEventName, {
|
|
215
|
+
options: res.data?.unlockOptions || [],
|
|
216
|
+
userJoliCoin: res.extra?.joliCoin || {
|
|
217
|
+
balance: 0,
|
|
218
|
+
enableAutoDeduct: false
|
|
219
|
+
},
|
|
220
|
+
userGem: res.extra?.joliGem || {
|
|
221
|
+
balance: 0,
|
|
222
|
+
enableAutoDeduct: false
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const rewardsTypes =
|
|
227
|
+
res.data?.unlockOptions?.map((option: IUnlockOption) => option.type) || Array.from(defaultRewards);
|
|
228
|
+
return sortRewards(rewardsTypes);
|
|
229
|
+
} catch (e) {
|
|
230
|
+
console.error('getRewardOptions error:', e);
|
|
231
|
+
return defaultRewards;
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
export const createRewardFrequencyConfigFetcher = (rewardsHelper: RewardsHelper) => {
|
|
237
|
+
rewardsHelper.registerRewardFrequencyConfigFetcher(async (httpClient: IHttpClient) => {
|
|
238
|
+
const res: StandardResponse<GlobalConfig> = await httpClient.get(
|
|
239
|
+
'/api/fe-configs/web-common/global-config',
|
|
240
|
+
{}
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
rewardsEmitter.emit(UseModalFrequencyEventName, {
|
|
244
|
+
joliCoinUseAndCharge: res.data?.joliCoinUseAndCharge || DefaltJoliCoinUseAndCharge,
|
|
245
|
+
loginGuide: res.data?.loginGuide || DefaltLoginGuide
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
res.data &&
|
|
249
|
+
hostEmitter.emit('onGlobalConfigChanged', {
|
|
250
|
+
globalConfig: res.data
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
joliCoinUseAndCharge: res.data?.joliCoinUseAndCharge || DefaltJoliCoinUseAndCharge,
|
|
255
|
+
loginGuide: res.data?.loginGuide || DefaltLoginGuide
|
|
256
|
+
};
|
|
257
|
+
});
|
|
258
|
+
};
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { RequestCacheService, RequestAdapter } from '@jolibox/common';
|
|
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
|
+
}
|