102 lines
3.6 KiB
TypeScript
102 lines
3.6 KiB
TypeScript
/**
|
||
* User 状态机:Actors(异步服务)
|
||
*
|
||
* 从 `user-machine.ts` 抽出的"Actors"段:
|
||
* - `userInitActor`(fromPromise:读 UserStorage 并行 user + avatarUrl)
|
||
* - `userFetchActor`(fromPromise:调 API + 持久化到本地)
|
||
* - `userLogoutActor`(fromPromise:auth logout + 清本地数据)
|
||
*
|
||
* 设计:
|
||
* - 依赖 `user-machine.helpers.ts`(toView, readInitData, userStorage)
|
||
* - 仓库以接口类型注入(调用面只看接口,运行时仍是同一单例)
|
||
* - 命名约定:`[user-machine]` 前缀日志(如未来加)—— 与 chat 模块对齐
|
||
*/
|
||
|
||
import { fromPromise } from "xstate";
|
||
|
||
import { userRepository } from "@/data/repositories/user_repository";
|
||
import { authRepository } from "@/data/repositories/auth_repository";
|
||
import type {
|
||
IAuthRepository,
|
||
IUserRepository,
|
||
} from "@/data/repositories/interfaces";
|
||
import { Result } from "@/utils";
|
||
|
||
import {
|
||
applyEntitlementSnapshotToView,
|
||
type InitData,
|
||
readInitData,
|
||
toEntitlementSnapshot,
|
||
toView,
|
||
type UserFetchData,
|
||
userStorage,
|
||
} from "./user-machine.helpers";
|
||
|
||
// ============================================================
|
||
// 仓库注入
|
||
// ============================================================
|
||
// 仓库以接口类型注入:调用面只看接口,运行时仍是同一单例
|
||
const userRepo: IUserRepository = userRepository;
|
||
const authRepo: IAuthRepository = authRepository;
|
||
|
||
// ============================================================
|
||
// Init actor:从 UserStorage 读 user + avatarUrl
|
||
// ============================================================
|
||
/**
|
||
* 读本地 user + avatarUrl(并行)。
|
||
* - 失败/无数据 → 返回 null,不抛错(machine 走 onDone 回到 idle,context 是 null)
|
||
* - onDone 赋 currentUser + avatarUrl 到 context
|
||
*/
|
||
export const userInitActor = fromPromise<InitData>(async () => readInitData());
|
||
|
||
// ============================================================
|
||
// Fetch actor:调 API 拉当前用户 + 持久化
|
||
// ============================================================
|
||
/**
|
||
* 调 `userRepo.getCurrentUser + getEntitlements` 拉当前 user 与权益:
|
||
* - user 成功 → 转 UserView + 写本地(userStorage.setUser)
|
||
* - entitlements 成功 → 提取 isVip/creditBalance 写本地轻量快照
|
||
* - 任一失败都不阻断另一方,machine 层负责保留已有 context
|
||
*/
|
||
export const userFetchActor = fromPromise<UserFetchData>(async () => {
|
||
const [userResult, entitlementsResult] = await Promise.all([
|
||
userRepo.getCurrentUser(),
|
||
userRepo.getEntitlements(),
|
||
]);
|
||
|
||
const snapshot = Result.isOk(entitlementsResult)
|
||
? toEntitlementSnapshot({
|
||
isVip: entitlementsResult.data.isVip,
|
||
creditBalance: entitlementsResult.data.creditBalance,
|
||
})
|
||
: null;
|
||
if (snapshot) {
|
||
await userStorage.setEntitlementSnapshot(snapshot);
|
||
}
|
||
|
||
if (Result.isErr(userResult)) {
|
||
return { user: null, snapshot };
|
||
}
|
||
|
||
const view = applyEntitlementSnapshotToView(
|
||
toView(userResult.data.toJson()),
|
||
snapshot,
|
||
);
|
||
// 持久化到本地
|
||
await userStorage.setUser(userResult.data.toJson());
|
||
return { user: view, snapshot };
|
||
});
|
||
|
||
// ============================================================
|
||
// Logout actor:后端登出 + 清本地用户数据
|
||
// ============================================================
|
||
/**
|
||
* 登出:
|
||
* - 调 `authRepo.logout` 让后端吊销 token
|
||
* - `userStorage.clearUserData` 清本地 user / avatar 等
|
||
* - onDone / onError 都跑 `clearUser` action(machine 层)—— 失败也清本地
|
||
*/
|
||
export const userLogoutActor = fromPromise<void>(async () => {
|
||
await authRepo.logout();
|
||
});
|