fix:后端错误

This commit is contained in:
吕新雨
2026-02-10 16:47:18 +08:00
parent 1fbc0aa3f8
commit dec3ac82e1
5 changed files with 29 additions and 14 deletions

View File

@@ -12,7 +12,7 @@ import { buildUserProfileFromQuestionnaire, mapOnboardingSelectionsToQuestionnai
import { ensureDailyWidgetRecoUpToDate, syncWidgetConfig, syncWidgetUserProfileFromScoring } from '@/src/modules/dailyWidgetReco';
import { toBackendLocaleFromLanguageTag } from '@/src/i18n/locale';
import { fetchRecoFeed } from '@/src/services/recoApi';
import { getExpoPushTokenOrThrow, registerPushToken, setPushPreferences } from '@/src/services/pushApi';
import { ensurePushTokenRegisteredIfPermitted, setPushPreferences } from '@/src/services/pushApi';
import {
recordRecoFeedServed,
setOnboardingCompleted,
@@ -128,7 +128,8 @@ export default function OnboardingScreen() {
await setPushPromptState('unknown');
try {
const { status } = await Notifications.requestPermissionsAsync();
if (status !== 'granted') {
// iOS 可能出现 provisional临时授权也应视为“已授权”
if (status !== 'granted' && status !== ('provisional' as any)) {
await setPushPromptState('skipped');
return;
}
@@ -140,10 +141,8 @@ export default function OnboardingScreen() {
return;
}
// 1) 获取 Expo Push Token失败才认为“推送开启失败”)
const expoPushToken = await getExpoPushTokenOrThrow();
// 2) 上报 token 到后端(幂等;失败才认为“推送开启失败”)
await registerPushToken({ pushToken: expoPushToken });
// 1) 上报 token 到后端(幂等;失败才认为“推送开启失败”)
await ensurePushTokenRegisteredIfPermitted();
// 3) 上报推送偏好(幂等)
// 注意:这一步失败时,后端仍可能已成功接收 token。

View File

@@ -87,6 +87,17 @@ export default function RootLayout() {
});
}, []);
useEffect(() => {
// 兜底:当用户在系统弹窗/系统设置里变更权限后App 回到前台时再同步一次 token
const sub = AppState.addEventListener('change', (state) => {
if (state !== 'active') return;
ensurePushTokenRegisteredIfPermitted().catch(() => {
// ignore不阻塞
});
});
return () => sub.remove();
}, []);
useEffect(() => {
// 字体与 i18n 都准备好后,允许渲染 App原生 splash 的隐藏交给 onLayout避免“硬切/闪白”)
if (loaded && i18nReady) setAppReady(true);

View File

@@ -41,7 +41,7 @@ import QuestionIcon from '@/assets/images/home/Profile/widget/question_icon.svg'
import * as Notifications from 'expo-notifications';
import { changeLanguage } from '@/src/i18n';
import { fetchLegalLinks } from '@/src/services/legalApi';
import { ensurePushTokenRegisteredIfPermitted, getExpoPushTokenOrThrow, registerPushToken, setPushPreferences } from '@/src/services/pushApi';
import { ensurePushTokenRegisteredIfPermitted, setPushPreferences } from '@/src/services/pushApi';
const { width } = Dimensions.get('window');
@@ -430,14 +430,13 @@ function DailyReminderPage({ visible, onDone }: { visible: boolean; onDone: () =
// 调试:打印状态
console.log('Push Permission Status:', status);
if (status === 'granted') {
if (status === 'granted' || (status as any) === 'provisional') {
setPushEnabled(true);
setHasSystemPermission(true);
// 获取 token 并上报后端(幂等)
try {
const expoPushToken = await getExpoPushTokenOrThrow();
await registerPushToken({ pushToken: expoPushToken });
await ensurePushTokenRegisteredIfPermitted();
// 偏好同步失败不应被用户感知为“开启失败”
// (常见现象:后端已接收 token但偏好接口短暂失败/超时)
try {

View File

@@ -160,10 +160,15 @@ export async function registerPushToken(args: { pushToken: string }): Promise<vo
});
}
function isSystemNotificationPermissionGranted(status: unknown): boolean {
// iOS 可能出现 provisional临时授权在 Push 场景也应视为“可获取 token 并上报”
return status === 'granted' || status === 'provisional';
}
export async function ensurePushTokenRegisteredIfPermitted(): Promise<{ ok: boolean; reason: string }> {
// 只要系统权限已授权,就应尽早把 token 写入后端(不依赖用户在“每日提醒”里点确认)
const settings = await Notifications.getPermissionsAsync();
if (settings.status !== 'granted') return { ok: false, reason: 'permission_not_granted' };
if (!isSystemNotificationPermissionGranted(settings.status)) return { ok: false, reason: 'permission_not_granted' };
const token = await getExpoPushTokenOrThrow();