Merge branch 'master' of https://git.genarrative.world/GenarrativeAI/Genarrative
This commit is contained in:
@@ -69,14 +69,6 @@ describe('babyObjectMatchClient', () => {
|
||||
generationProvider: 'vector-engine-gpt-image-2',
|
||||
prompt: 'background prompt',
|
||||
},
|
||||
{
|
||||
assetId: 'server-ui',
|
||||
assetKind: 'ui-frame',
|
||||
imageSrc: 'data:image/png;base64,ui',
|
||||
assetObjectId: null,
|
||||
generationProvider: 'vector-engine-gpt-image-2',
|
||||
prompt: 'ui prompt',
|
||||
},
|
||||
{
|
||||
assetId: 'server-gift',
|
||||
assetKind: 'gift-box',
|
||||
@@ -93,14 +85,6 @@ describe('babyObjectMatchClient', () => {
|
||||
generationProvider: 'vector-engine-gpt-image-2',
|
||||
prompt: 'basket prompt',
|
||||
},
|
||||
{
|
||||
assetId: 'server-smoke',
|
||||
assetKind: 'smoke-puff',
|
||||
imageSrc: 'data:image/png;base64,smoke',
|
||||
assetObjectId: null,
|
||||
generationProvider: 'vector-engine-gpt-image-2',
|
||||
prompt: 'smoke prompt',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
@@ -127,7 +111,7 @@ describe('babyObjectMatchClient', () => {
|
||||
expect(response.draft.itemAssets[0]?.generationProvider).toBe(
|
||||
'vector-engine-gpt-image-2',
|
||||
);
|
||||
expect(response.draft.visualPackage?.assets).toHaveLength(5);
|
||||
expect(response.draft.visualPackage?.assets).toHaveLength(3);
|
||||
expect(response.draft.visualPackage?.assets[0]?.generationProvider).toBe(
|
||||
'vector-engine-gpt-image-2',
|
||||
);
|
||||
@@ -169,7 +153,7 @@ describe('babyObjectMatchClient', () => {
|
||||
expect(response.draft.visualPackage?.themePrompt).toBe('果园主题视觉包装');
|
||||
expect(
|
||||
response.draft.visualPackage?.assets.map((asset) => asset.assetKind),
|
||||
).toEqual(['background', 'ui-frame', 'gift-box', 'basket', 'smoke-puff']);
|
||||
).toEqual(['background', 'gift-box', 'basket']);
|
||||
expect(response.draft.visualPackage?.assets[0]).toMatchObject({
|
||||
assetId: 'baby-object-visual-background',
|
||||
generationProvider: 'vector-engine-gpt-image-2',
|
||||
|
||||
@@ -28,7 +28,7 @@ const BABY_OBJECT_MATCH_ASSET_REQUEST_RETRY: ApiRetryOptions = {
|
||||
maxRetries: 0,
|
||||
};
|
||||
const BABY_OBJECT_MATCH_REQUIRED_VISUAL_KINDS: BabyObjectMatchVisualAssetKind[] =
|
||||
['background', 'ui-frame', 'gift-box', 'basket', 'smoke-puff'];
|
||||
['background', 'gift-box', 'basket'];
|
||||
const DRAFT_DB_NAME = 'genarrative-edutainment-baby-object-drafts';
|
||||
const DRAFT_DB_VERSION = 1;
|
||||
const DRAFT_STORE_NAME = 'drafts';
|
||||
|
||||
118
src/services/payment/paymentPlatform.test.ts
Normal file
118
src/services/payment/paymentPlatform.test.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
import {
|
||||
resolveProfileRechargePaymentChannel,
|
||||
shouldShowRechargeEntry,
|
||||
WECHAT_H5_PAYMENT_CHANNEL,
|
||||
WECHAT_MINI_PROGRAM_PAYMENT_CHANNEL,
|
||||
WECHAT_NATIVE_PAYMENT_CHANNEL,
|
||||
} from './paymentPlatform';
|
||||
|
||||
describe('resolveProfileRechargePaymentChannel', () => {
|
||||
test('小程序运行态选择 wechat_mp', () => {
|
||||
expect(
|
||||
resolveProfileRechargePaymentChannel({
|
||||
location: { search: '?clientRuntime=wechat_mini_program' },
|
||||
navigator: { userAgent: 'Mozilla/5.0 (iPhone)' },
|
||||
}),
|
||||
).toBe(WECHAT_MINI_PROGRAM_PAYMENT_CHANNEL);
|
||||
});
|
||||
|
||||
test('移动网页选择 wechat_h5', () => {
|
||||
expect(
|
||||
resolveProfileRechargePaymentChannel({
|
||||
location: { search: '' },
|
||||
navigator: {
|
||||
userAgent:
|
||||
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) Mobile',
|
||||
},
|
||||
}),
|
||||
).toBe(WECHAT_H5_PAYMENT_CHANNEL);
|
||||
});
|
||||
|
||||
test('微信内 H5 首版仍选择 wechat_h5', () => {
|
||||
expect(
|
||||
resolveProfileRechargePaymentChannel({
|
||||
location: { search: '' },
|
||||
navigator: {
|
||||
userAgent:
|
||||
'Mozilla/5.0 (Linux; Android 14) AppleWebKit MicroMessenger/8.0 Mobile',
|
||||
},
|
||||
}),
|
||||
).toBe(WECHAT_H5_PAYMENT_CHANNEL);
|
||||
});
|
||||
|
||||
test('桌面网页选择 wechat_native', () => {
|
||||
expect(
|
||||
resolveProfileRechargePaymentChannel({
|
||||
location: { search: '' },
|
||||
navigator: { userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' },
|
||||
matchMedia: () => ({ matches: false }) as unknown as MediaQueryList,
|
||||
}),
|
||||
).toBe(WECHAT_NATIVE_PAYMENT_CHANNEL);
|
||||
});
|
||||
|
||||
test('桌面微信内网页选择 wechat_native', () => {
|
||||
expect(
|
||||
resolveProfileRechargePaymentChannel({
|
||||
location: { search: '' },
|
||||
navigator: {
|
||||
userAgent:
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit MicroMessenger/8.0',
|
||||
},
|
||||
matchMedia: () => ({ matches: false }) as unknown as MediaQueryList,
|
||||
}),
|
||||
).toBe(WECHAT_NATIVE_PAYMENT_CHANNEL);
|
||||
});
|
||||
|
||||
test('默认路径永远不会解析成 mock', () => {
|
||||
expect(
|
||||
resolveProfileRechargePaymentChannel({
|
||||
location: { search: '' },
|
||||
navigator: { userAgent: '' },
|
||||
matchMedia: () => ({ matches: false }) as unknown as MediaQueryList,
|
||||
}),
|
||||
).not.toBe('mock');
|
||||
});
|
||||
});
|
||||
|
||||
describe('shouldShowRechargeEntry', () => {
|
||||
test('小程序运行态显示充值入口', () => {
|
||||
expect(
|
||||
shouldShowRechargeEntry({
|
||||
location: { search: '?clientRuntime=wechat_mini_program' },
|
||||
navigator: { userAgent: 'Mozilla/5.0 (iPhone)' },
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('微信内网页显示充值入口', () => {
|
||||
expect(
|
||||
shouldShowRechargeEntry({
|
||||
location: { search: '' },
|
||||
navigator: {
|
||||
userAgent:
|
||||
'Mozilla/5.0 (Linux; Android 14) AppleWebKit MicroMessenger/8.0 Mobile',
|
||||
},
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('普通浏览器不显示充值入口', () => {
|
||||
expect(
|
||||
shouldShowRechargeEntry({
|
||||
location: { search: '' },
|
||||
navigator: {
|
||||
userAgent:
|
||||
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) Mobile',
|
||||
},
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
shouldShowRechargeEntry({
|
||||
location: { search: '' },
|
||||
navigator: { userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' },
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
95
src/services/payment/paymentPlatform.ts
Normal file
95
src/services/payment/paymentPlatform.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
export const WECHAT_MINI_PROGRAM_PAYMENT_CHANNEL = 'wechat_mp';
|
||||
export const WECHAT_H5_PAYMENT_CHANNEL = 'wechat_h5';
|
||||
export const WECHAT_NATIVE_PAYMENT_CHANNEL = 'wechat_native';
|
||||
export const MOCK_PAYMENT_CHANNEL = 'mock';
|
||||
|
||||
export type ProfileRechargeWechatPaymentChannel =
|
||||
| typeof WECHAT_MINI_PROGRAM_PAYMENT_CHANNEL
|
||||
| typeof WECHAT_H5_PAYMENT_CHANNEL
|
||||
| typeof WECHAT_NATIVE_PAYMENT_CHANNEL;
|
||||
|
||||
type PaymentPlatformNavigator = Pick<Navigator, 'userAgent' | 'maxTouchPoints'>;
|
||||
|
||||
export type PaymentPlatformContext = {
|
||||
location?: Pick<Location, 'search'> | null;
|
||||
navigator?: Partial<PaymentPlatformNavigator> | null;
|
||||
matchMedia?: Window['matchMedia'] | null;
|
||||
};
|
||||
|
||||
export function shouldShowRechargeEntry(
|
||||
context: PaymentPlatformContext = {},
|
||||
) {
|
||||
const location =
|
||||
context.location ?? (typeof window !== 'undefined' ? window.location : null);
|
||||
const navigatorLike =
|
||||
context.navigator ?? (typeof navigator !== 'undefined' ? navigator : null);
|
||||
|
||||
return (
|
||||
isWechatMiniProgramRuntime(location) ||
|
||||
isWechatBrowserRuntime(navigatorLike)
|
||||
);
|
||||
}
|
||||
|
||||
export function resolveProfileRechargePaymentChannel(
|
||||
context: PaymentPlatformContext = {},
|
||||
): ProfileRechargeWechatPaymentChannel {
|
||||
const location =
|
||||
context.location ??
|
||||
(typeof window !== 'undefined' ? window.location : null);
|
||||
const navigatorLike =
|
||||
context.navigator ?? (typeof navigator !== 'undefined' ? navigator : null);
|
||||
const matchMedia =
|
||||
context.matchMedia ??
|
||||
(typeof window !== 'undefined' && typeof window.matchMedia === 'function'
|
||||
? window.matchMedia.bind(window)
|
||||
: null);
|
||||
|
||||
if (isWechatMiniProgramRuntime(location)) {
|
||||
return WECHAT_MINI_PROGRAM_PAYMENT_CHANNEL;
|
||||
}
|
||||
|
||||
if (isMobileWebRuntime(navigatorLike, matchMedia)) {
|
||||
return WECHAT_H5_PAYMENT_CHANNEL;
|
||||
}
|
||||
|
||||
return WECHAT_NATIVE_PAYMENT_CHANNEL;
|
||||
}
|
||||
|
||||
export function isManualMockPaymentChannel(paymentChannel: string) {
|
||||
return paymentChannel.trim() === MOCK_PAYMENT_CHANNEL;
|
||||
}
|
||||
|
||||
function isWechatMiniProgramRuntime(
|
||||
location: Pick<Location, 'search'> | null | undefined,
|
||||
) {
|
||||
const params = new URLSearchParams(location?.search ?? '');
|
||||
return (
|
||||
params.get('clientRuntime') === 'wechat_mini_program' ||
|
||||
params.get('clientType') === 'mini_program'
|
||||
);
|
||||
}
|
||||
|
||||
function isWechatBrowserRuntime(
|
||||
navigatorLike: Partial<PaymentPlatformNavigator> | null | undefined,
|
||||
) {
|
||||
return (
|
||||
navigatorLike?.userAgent?.toLowerCase().includes('micromessenger') ??
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
function isMobileWebRuntime(
|
||||
navigatorLike: Partial<PaymentPlatformNavigator> | null | undefined,
|
||||
matchMedia: Window['matchMedia'] | null | undefined,
|
||||
) {
|
||||
const userAgent = navigatorLike?.userAgent?.toLowerCase() ?? '';
|
||||
if (/android|iphone|ipad|ipod|mobile|windows phone/u.test(userAgent)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((navigatorLike?.maxTouchPoints ?? 0) > 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Boolean(matchMedia?.('(max-width: 767px)').matches);
|
||||
}
|
||||
3
src/services/payment/paymentRedirect.ts
Normal file
3
src/services/payment/paymentRedirect.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export function redirectToPaymentUrl(url: string) {
|
||||
window.location.assign(url);
|
||||
}
|
||||
@@ -67,9 +67,7 @@ export function getRpgProfileDashboard(options: RuntimeRequestOptions = {}) {
|
||||
);
|
||||
}
|
||||
|
||||
export function getRpgProfileWalletLedger(
|
||||
options: RuntimeRequestOptions = {},
|
||||
) {
|
||||
export function getRpgProfileWalletLedger(options: RuntimeRequestOptions = {}) {
|
||||
return requestRpgRuntimeJson<ProfileWalletLedgerResponse>(
|
||||
'/profile/wallet-ledger',
|
||||
{ method: 'GET' },
|
||||
@@ -91,7 +89,7 @@ export function getRpgProfileRechargeCenter(
|
||||
|
||||
export function createRpgProfileRechargeOrder(
|
||||
productId: string,
|
||||
paymentChannel = 'mock',
|
||||
paymentChannel: string,
|
||||
options: RuntimeRequestOptions = {},
|
||||
) {
|
||||
return requestRpgRuntimeJson<CreateProfileRechargeOrderResponse>(
|
||||
@@ -227,12 +225,13 @@ export async function resumeRpgProfileSaveArchive(
|
||||
worldKey: string,
|
||||
options: RuntimeRequestOptions = {},
|
||||
) {
|
||||
const response = await requestRpgRuntimeJson<ProfileSaveArchiveResumeResponse>(
|
||||
`/profile/save-archives/${encodeURIComponent(worldKey)}`,
|
||||
{ method: 'POST' },
|
||||
'恢复存档失败',
|
||||
options,
|
||||
);
|
||||
const response =
|
||||
await requestRpgRuntimeJson<ProfileSaveArchiveResumeResponse>(
|
||||
`/profile/save-archives/${encodeURIComponent(worldKey)}`,
|
||||
{ method: 'POST' },
|
||||
'恢复存档失败',
|
||||
options,
|
||||
);
|
||||
|
||||
return {
|
||||
entry: response.entry,
|
||||
|
||||
@@ -90,8 +90,10 @@ describe('parseMocapPacket', () => {
|
||||
limb_nodes: [
|
||||
{ name: 'left_shoulder', x: 0.28, y: 0.42 },
|
||||
{ name: 'left_elbow', x: 0.24, y: 0.5 },
|
||||
{ name: 'left_wrist', x: 0.2, y: 0.57 },
|
||||
{ name: 'right_shoulder', x: 0.72, y: 0.42 },
|
||||
{ name: 'right_elbow', x: 0.76, y: 0.5 },
|
||||
{ name: 'right_wrist', x: 0.8, y: 0.57 },
|
||||
],
|
||||
},
|
||||
actions: [{ gesture: 'wave-left-hand' }],
|
||||
@@ -120,8 +122,10 @@ describe('parseMocapPacket', () => {
|
||||
expect(command.bodyJoints).toEqual({
|
||||
leftShoulder: {x: 0.28, y: 0.42},
|
||||
leftElbow: {x: 0.24, y: 0.5},
|
||||
leftWrist: {x: 0.2, y: 0.57},
|
||||
rightShoulder: {x: 0.72, y: 0.42},
|
||||
rightElbow: {x: 0.76, y: 0.5},
|
||||
rightWrist: {x: 0.8, y: 0.57},
|
||||
});
|
||||
expect(command.actions).toEqual(
|
||||
expect.arrayContaining(['wave_left_hand', 'open_palm']),
|
||||
|
||||
@@ -27,6 +27,8 @@ export type MocapBodyJointsInput = {
|
||||
rightShoulder?: MocapPointInput | null;
|
||||
leftElbow?: MocapPointInput | null;
|
||||
rightElbow?: MocapPointInput | null;
|
||||
leftWrist?: MocapPointInput | null;
|
||||
rightWrist?: MocapPointInput | null;
|
||||
};
|
||||
|
||||
export type MocapInputCommand = {
|
||||
@@ -289,6 +291,14 @@ function normalizeBodyJointName(name: unknown) {
|
||||
return 'rightElbow' as const;
|
||||
}
|
||||
|
||||
if (normalized === 'left_wrist' || normalized === 'leftwrist') {
|
||||
return 'leftWrist' as const;
|
||||
}
|
||||
|
||||
if (normalized === 'right_wrist' || normalized === 'rightwrist') {
|
||||
return 'rightWrist' as const;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user