跨端: feat(推送客户端) 收尾两处接线(pi 邮件 66bbd929 分工)+ 换账号补报
`PushService.ets` 与 `pendingRoute` 都早就写好了,但**没人调用/没人读** —— 而"写好了"与"接上了"是两件事:ArkTS 只编译可达模块,往一个无人 import 的 文件里放必然报错的类型错误,`assembleHap` 照样 BUILD SUCCESSFUL (pi 与我各复现过一个方向)。这次补的是**边**,不是点。 ① 登录成功补报(LoginPage):`EntryAbility.onCreate` 那次在**登录之前**跑, 那时 `ApiClient` 还没有 token ⇒ 服务端 401,而 `reportToken` 的失败是静默的 ⇒ "启动时报过"不等于"这个账号登记过"。补三条登录路径,一条都不能漏: 快速路径(已有账号直接进主界面,老用户走这条)/ tryRestore / doLogin。 ② 换账号补报(SettingsPage.switchTo,**我加的第三处**):服务端语义是同一 token 换账号**转移**而非并存 ⇒ 新账号其实没登记过。幂等标记按 `accountKey|token` 存,所以这里调一次必然重发,不会被"我报过"挡掉。 不补的症状:切完账号,通知仍推给上一个账号。 ③ pendingRoute 消费端(MainPage.CommPage):`EntryAbility` 已把通知 data 解析成 `pendingRoute`(冷启 onCreate / 热启 onNewWant 都写了), **但没有任何东西读它** ⇒ 点通知只拉起 App、停在列表页。 在 CommPage 消费(详情页是这个 Navigation 的 navPathStack 上的路由, 栈只有它持有),消费后**立刻清空**(它是"待处理"不是"当前页", 不清则返回列表再进来会被反复跳走)。 判据:`harmony-push.test.mjs` 15 → 18 条,三条新判据钉的就是上面这三条边, **三条都做了变异验证**(各自删掉对应接线即红、恢复即绿)。 `LoginPage` 那条数**恰好 3 处**调用:少了快速路径=老用户永远不补报, 多了要问清是哪条路径。 编译验证(不是"命令成功",是"它真读了这个文件"):三处改动各植入一次必然 类型错误 ⇒ BUILD FAILED 且 `At File:` 指到**新代码那一行**(MainPage:1173 / LoginPage:108 / SettingsPage:456),恢复后 BUILD SUCCESSFUL。 产物侧证:`modules.abc` 里能查到 `consumePendingRoute`(2) / `reportPushToken`(2)。
This commit is contained in:
@ -11,6 +11,8 @@ import { SseService } from '../api/SseService';
|
||||
import { Me } from '../model/Models';
|
||||
import { DEFAULT_API_BASE, EMULATOR_HOST_BASE } from '../common/Config';
|
||||
import { ApiBaseCheck, validateApiBase } from '../model/ApiBase';
|
||||
import { PushService } from '../api/PushService';
|
||||
import { common } from '@kit.AbilityKit';
|
||||
import { hilog } from '@kit.PerformanceAnalysisKit';
|
||||
|
||||
@Entry
|
||||
@ -47,6 +49,12 @@ struct LoginPage {
|
||||
this.serverAddr = active.server;
|
||||
// 为所有账号建立 SSE 连接
|
||||
SseService.getInstance().connectAll(acctMgr);
|
||||
/*
|
||||
* ★ 这条快速路径也是"一次登录",所以同样要补报:
|
||||
* 它**不经** doLogin/tryRestore,是"已有账号直接进主界面"。
|
||||
* 漏了这里,老用户(最需要推送的那批)恰好永远不补报。
|
||||
*/
|
||||
this.reportPushToken(client);
|
||||
this.getUIContext().getRouter().pushUrl({ url: 'pages/MainPage' });
|
||||
return;
|
||||
}
|
||||
@ -73,6 +81,7 @@ struct LoginPage {
|
||||
const user = await new AuthApi(c).loginWithKey(token);
|
||||
this.me = user;
|
||||
this.loggedIn = true;
|
||||
this.reportPushToken(c);
|
||||
} catch (e) {
|
||||
const c2: ApiClient | null = this.client;
|
||||
if (c2 !== null) {
|
||||
@ -83,6 +92,33 @@ struct LoginPage {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录成功后的**补报**(收尾项,pi 邮件 `66bbd929` §三)。
|
||||
*
|
||||
* 为什么必须有这一处:`EntryAbility.onCreate` 里那次上报发生在**登录之前** ——
|
||||
* 那时 `ApiClient` 还没有 token,服务端回 401,而 `reportToken` 的失败是**静默**的
|
||||
* (设计如此),所以"启动时报过一次"并不等于"这个账号登记过"。
|
||||
* 少了这一处,链路的症状是:App 能登录、能 GET,但**永远不 POST** token。
|
||||
*
|
||||
* 三条性质沿用 `PushService.reportToken` 自己的:开关 gate 在最前(关着就零开销)、
|
||||
* 失败全静默(不弹、不阻塞登录跳转)、且**幂等**(已报过且账号没换就不打网络)。
|
||||
* 所以这里不 await、不 catch 提示 —— 登录跳转不该被一个可选通道拖住。
|
||||
*/
|
||||
private reportPushToken(c: ApiClient): void {
|
||||
const ctx: Context | undefined = this.getUIContext().getHostContext();
|
||||
if (ctx === undefined) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const push: PushService = PushService.getInstance(ctx as common.UIAbilityContext);
|
||||
push.reportToken(c).catch((err: Object) => {
|
||||
hilog.info(0x0001, 'LoginPage', '登录后补报失败(静默):%{public}s', JSON.stringify(err));
|
||||
});
|
||||
} catch (e) {
|
||||
hilog.info(0x0001, 'LoginPage', '登录后补报跳过(静默):%{public}s', JSON.stringify(e));
|
||||
}
|
||||
}
|
||||
|
||||
async doLogin(): Promise<void> {
|
||||
if (this.loading) {
|
||||
return;
|
||||
@ -131,6 +167,8 @@ struct LoginPage {
|
||||
// 为该账号建立 SSE 连接
|
||||
SseService.getInstance().connectForAccount(acct.id, acct.server, acct.token);
|
||||
}
|
||||
// ★ 登录成功 ⇒ 补报一次推送 token(见 reportPushToken 的说明:启动那次是在登录之前)
|
||||
this.reportPushToken(c);
|
||||
// 跳转主界面(含 Tab 导航)
|
||||
await this.getUIContext().getRouter().pushUrl({ url: 'pages/MainPage' });
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user