75 lines
2.1 KiB
TypeScript
75 lines
2.1 KiB
TypeScript
import i18n from 'i18next';
|
||
|
||
import { API_BASE_URL } from '@/src/constants/env';
|
||
import type { UserProfileV1_2 } from '@/src/features/userProfileScoring';
|
||
|
||
export type RecommendedItem = {
|
||
content_id: number;
|
||
text: string;
|
||
final_score: number;
|
||
fallback_level_final: number;
|
||
explanations?: Record<string, unknown> | null;
|
||
};
|
||
|
||
export type RecoMeta = Record<string, unknown>;
|
||
|
||
export type RecoEngineResult = {
|
||
items: RecommendedItem[];
|
||
meta: RecoMeta;
|
||
};
|
||
|
||
export type RecoRequest = {
|
||
k?: number;
|
||
user_profile: UserProfileV1_2;
|
||
already_recommended_ids?: Array<string | number>;
|
||
touched_or_viewed_ids?: Array<string | number>;
|
||
now?: string; // ISO8601(可选)
|
||
};
|
||
|
||
function withTimeout(ms: number): AbortController {
|
||
const controller = new AbortController();
|
||
setTimeout(() => controller.abort(), ms);
|
||
return controller;
|
||
}
|
||
|
||
export async function fetchRecoFeed(req: RecoRequest): Promise<RecoEngineResult> {
|
||
const controller = withTimeout(12_000);
|
||
const url = `${API_BASE_URL}/v1/reco/feed`;
|
||
const acceptLanguage = i18n.language?.toLowerCase().startsWith('zh') ? 'tc' : 'en';
|
||
|
||
const headers: Record<string, string> = {
|
||
'Content-Type': 'application/json',
|
||
// 让后端做 locale 选择(目前后端只区分 en/tc)
|
||
'Accept-Language': acceptLanguage,
|
||
};
|
||
const bodyObj = {
|
||
k: req.k,
|
||
user_profile: req.user_profile,
|
||
already_recommended_ids: req.already_recommended_ids ?? [],
|
||
touched_or_viewed_ids: req.touched_or_viewed_ids ?? [],
|
||
now: req.now,
|
||
};
|
||
|
||
// 仅在开发环境打印,避免生产环境日志泄露敏感信息
|
||
if (__DEV__) {
|
||
console.log('[Feed API] 请求地址:', url);
|
||
console.log('[Feed API] Feed的API请求头:', headers);
|
||
console.log('[Feed API] Feed的API请求体:', bodyObj);
|
||
}
|
||
|
||
const res = await fetch(url, {
|
||
method: 'POST',
|
||
headers,
|
||
body: JSON.stringify(bodyObj),
|
||
signal: controller.signal,
|
||
});
|
||
|
||
if (!res.ok) {
|
||
const text = await res.text().catch(() => '');
|
||
throw new Error(`推荐接口请求失败:${res.status} ${res.statusText} ${text}`.trim());
|
||
}
|
||
|
||
return (await res.json()) as RecoEngineResult;
|
||
}
|
||
|