Files
Genarrative/src/services/platform-entry/platformProfileClient.ts
T
kdletters 9c9c8f468a 恢复平台个人页并阻断旧业务回流
恢复创作、项目、我的平台侧栏与现役搜索
恢复全宽个人页及资料编辑、充值兑换、社区反馈和 API Key 入口
迁移平台资料、钱包和设置客户端并移除旧创作依赖
新增 Vite 与 ESLint 退役模块门禁并补齐界面和搜索测试
同步旧创作退役方案、平台入口决策与踩坑记录
2026-07-18 17:29:06 +08:00

365 lines
9.2 KiB
TypeScript

import type {
ConfirmWechatProfileRechargeOrderResponse,
CreateProfileRechargeOrderResponse,
ExternalApiKeyCreateResponse,
ExternalApiKeyListResponse,
ExternalApiKeyMutationResponse,
ProfileDashboardSummary,
ProfileRechargeCenterResponse,
ProfileRechargeOrder,
ProfileReferralInviteCenterResponse,
ProfileWalletLedgerResponse,
RedeemProfileReferralInviteCodeResponse,
RedeemProfileRewardCodeResponse,
} from '../../../packages/shared/src/contracts/runtime';
import {
appendApiErrorRequestId,
parseApiErrorMessage,
} from '../../../packages/shared/src/http';
import {
type ApiAuthImpact,
type ApiRetryOptions,
fetchWithApiAuth,
requestJson,
} from '../apiClient';
import { readSseJsonStream } from '../sseStream';
const PLATFORM_PROFILE_API_BASE = '/api/profile';
const PLATFORM_PROFILE_READ_RETRY: ApiRetryOptions = {
maxRetries: 1,
baseDelayMs: 180,
maxDelayMs: 480,
};
const PLATFORM_PROFILE_WRITE_RETRY: ApiRetryOptions = {
maxRetries: 1,
baseDelayMs: 240,
maxDelayMs: 640,
retryUnsafeMethods: true,
};
export type PlatformProfileRequestOptions = {
signal?: AbortSignal;
retry?: ApiRetryOptions;
skipAuth?: boolean;
skipRefresh?: boolean;
authImpact?: ApiAuthImpact;
notifyAuthStateChange?: boolean;
clearAuthOnUnauthorized?: boolean;
};
function requestPlatformProfileJson<T>(
path: string,
init: RequestInit,
fallbackMessage: string,
options: PlatformProfileRequestOptions = {},
) {
const method = (init.method ?? 'GET').toUpperCase();
const retry =
options.retry ??
(method === 'GET'
? PLATFORM_PROFILE_READ_RETRY
: PLATFORM_PROFILE_WRITE_RETRY);
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
return requestJson<T>(
`${PLATFORM_PROFILE_API_BASE}${normalizedPath}`,
{
...init,
signal: options.signal,
},
fallbackMessage,
{
retry,
skipAuth: options.skipAuth,
skipRefresh: options.skipRefresh,
authImpact: options.authImpact,
notifyAuthStateChange: options.notifyAuthStateChange,
clearAuthOnUnauthorized: options.clearAuthOnUnauthorized,
},
);
}
export function getPlatformProfileDashboard(
options: PlatformProfileRequestOptions = {},
) {
return requestPlatformProfileJson<ProfileDashboardSummary>(
'/dashboard',
{ method: 'GET' },
'读取个人看板失败',
options,
);
}
export function getPlatformProfileWalletLedger(
options: PlatformProfileRequestOptions = {},
) {
return requestPlatformProfileJson<ProfileWalletLedgerResponse>(
'/wallet-ledger',
{ method: 'GET' },
'读取资产流水失败',
options,
);
}
export function listPlatformProfileExternalApiKeys(
options: PlatformProfileRequestOptions = {},
) {
return requestPlatformProfileJson<ExternalApiKeyListResponse>(
'/api-keys',
{ method: 'GET' },
'读取 API Key 失败',
options,
);
}
export function createPlatformProfileExternalApiKey(
name: string,
options: PlatformProfileRequestOptions = {},
) {
return requestPlatformProfileJson<ExternalApiKeyCreateResponse>(
'/api-keys',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name }),
},
'创建 API Key 失败',
options,
);
}
export function revokePlatformProfileExternalApiKey(
keyId: string,
options: PlatformProfileRequestOptions = {},
) {
return requestPlatformProfileJson<ExternalApiKeyMutationResponse>(
`/api-keys/${encodeURIComponent(keyId)}`,
{ method: 'DELETE' },
'撤销 API Key 失败',
options,
);
}
export function getPlatformProfileRechargeCenter(
options: PlatformProfileRequestOptions = {},
) {
return requestPlatformProfileJson<ProfileRechargeCenterResponse>(
'/recharge-center',
{ method: 'GET' },
'读取泥点购买信息失败',
options,
);
}
export function createPlatformProfileRechargeOrder(
productId: string,
paymentChannel: string,
options: PlatformProfileRequestOptions = {},
) {
return requestPlatformProfileJson<CreateProfileRechargeOrderResponse>(
'/recharge/orders',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ productId, paymentChannel }),
},
'充值失败',
options,
);
}
export function confirmWechatPlatformProfileRechargeOrder(
orderId: string,
options: PlatformProfileRequestOptions = {},
) {
return requestPlatformProfileJson<ConfirmWechatProfileRechargeOrderResponse>(
`/recharge/orders/${encodeURIComponent(orderId)}/wechat/confirm`,
{ method: 'POST' },
'确认微信支付订单失败',
options,
);
}
type PlatformProfileRechargeOrderSseEvent =
| {
type: 'order';
payload: ConfirmWechatProfileRechargeOrderResponse;
}
| {
type: 'done';
payload: { orderId: string; status: string };
}
| {
type: 'error';
payload: { message: string };
};
function normalizePlatformProfileRechargeOrderSseEvent(
eventName: string,
parsed: Record<string, unknown>,
): PlatformProfileRechargeOrderSseEvent | null {
if (eventName === 'order' && parsed.order && parsed.center) {
return {
type: 'order',
payload: parsed as ConfirmWechatProfileRechargeOrderResponse,
};
}
if (eventName === 'done') {
const orderId =
typeof parsed.orderId === 'string' ? parsed.orderId.trim() : '';
const status =
typeof parsed.status === 'string' ? parsed.status.trim() : '';
if (orderId && status) {
return {
type: 'done',
payload: { orderId, status },
};
}
}
if (eventName === 'error') {
const message =
typeof parsed.message === 'string' && parsed.message.trim()
? parsed.message.trim()
: '';
return {
type: 'error',
payload: { message },
};
}
return null;
}
function isPlatformProfileRechargeOrderTerminal(
order: Pick<ProfileRechargeOrder, 'status' | 'expirationCheckedAt'>,
) {
if (order.status === 'pending') {
return false;
}
if (order.status === 'expired' && !order.expirationCheckedAt) {
return false;
}
return true;
}
export async function watchWechatPlatformProfileRechargeOrder(
orderId: string,
options: PlatformProfileRequestOptions = {},
): Promise<ConfirmWechatProfileRechargeOrderResponse> {
const response = await fetchWithApiAuth(
`${PLATFORM_PROFILE_API_BASE}/recharge/orders/${encodeURIComponent(orderId)}/wechat/events`,
{
method: 'GET',
headers: { Accept: 'text/event-stream' },
signal: options.signal,
},
{
skipRefresh: options.skipRefresh,
skipAuth: options.skipAuth,
authImpact: options.authImpact,
notifyAuthStateChange: options.notifyAuthStateChange,
clearAuthOnUnauthorized: options.clearAuthOnUnauthorized,
},
);
if (!response.ok) {
const responseText = await response.text();
throw new Error(
appendApiErrorRequestId(
parseApiErrorMessage(responseText, '订阅充值订单状态失败'),
response.headers.get('x-request-id'),
),
);
}
if (!response.body) {
throw new Error('streaming response body is unavailable');
}
let finalResponse: ConfirmWechatProfileRechargeOrderResponse | null = null;
let lastResponse: ConfirmWechatProfileRechargeOrderResponse | null = null;
await readSseJsonStream(response, ({ eventName, parsed }) => {
const normalized = normalizePlatformProfileRechargeOrderSseEvent(
eventName,
parsed,
);
if (!normalized) {
return;
}
if (normalized.type === 'order') {
lastResponse = normalized.payload;
if (isPlatformProfileRechargeOrderTerminal(normalized.payload.order)) {
finalResponse = normalized.payload;
return false;
}
return;
}
if (normalized.type === 'done') {
if (
!finalResponse &&
lastResponse &&
isPlatformProfileRechargeOrderTerminal(lastResponse.order)
) {
finalResponse = lastResponse;
}
return false;
}
throw new Error(normalized.payload.message || '订阅充值订单状态失败');
});
if (!finalResponse) {
throw new Error('充值订单状态流返回不完整');
}
return finalResponse;
}
export function getPlatformProfileReferralInviteCenter(
options: PlatformProfileRequestOptions = {},
) {
return requestPlatformProfileJson<ProfileReferralInviteCenterResponse>(
'/referrals/invite-center',
{ method: 'GET' },
'读取邀请码失败',
options,
);
}
export function redeemPlatformProfileReferralInviteCode(
inviteCode: string,
options: PlatformProfileRequestOptions = {},
) {
return requestPlatformProfileJson<RedeemProfileReferralInviteCodeResponse>(
'/referrals/redeem-code',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ inviteCode }),
},
'填写邀请码失败',
options,
);
}
export function redeemPlatformProfileRewardCode(
code: string,
options: PlatformProfileRequestOptions = {},
) {
return requestPlatformProfileJson<RedeemProfileRewardCodeResponse>(
'/redeem-codes/redeem',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code }),
},
'兑换失败',
options,
);
}