refactor(data): add I*Repository interfaces and depend on them in stores
为 /src/data/repositories/ 下每个仓库类增加对应的接口契约,
接口文件存放在独立的 interfaces/ 子目录中。
改动:
- 新建 4 个接口文件 (iauth_repository / ichat_repository /
imetrics_repository / iuser_repository),纯 type-only,方法签名
与实现类完全一致
- 4 个实现类加 implements I{Name}Repository 子句,编译器自动校验契约
- 3 个 stores 文件 (auth / chat / user state machines) 改为依赖接口
类型:通过 `const xxxRepo: I{Name}Repository = xxxRepository` 局部别名,
actor 内部调用全部走别名
- barrelsby.json 加入 interfaces/ 目录,让自动 barrel 能覆盖
- Auto-regenerated repositories/index.ts 与 interfaces/index.ts
参照 storage 层已有的 IAuthStorage / IChatStorage / IUserStorage 模式。
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import { signIn } from "next-auth/react";
|
||||
import type { AuthProvider } from "@/lib/auth/auth_platform";
|
||||
import type { LoginStatus } from "@/models/auth/login-status";
|
||||
import { authRepository } from "@/data/repositories/auth_repository";
|
||||
import type { IAuthRepository } from "@/data/repositories/interfaces";
|
||||
import { AuthStorage } from "@/data/storage/auth/auth_storage";
|
||||
import { deviceIdentifier } from "@/utils/device_identifier";
|
||||
import { Result } from "@/utils/result";
|
||||
@@ -40,11 +41,13 @@ async function readGuestId(): Promise<string | undefined> {
|
||||
// ============================================================
|
||||
// Actors(异步服务)
|
||||
// ============================================================
|
||||
// 仓库以接口类型注入:调用面只看接口,运行时仍是同一单例
|
||||
const authRepo: IAuthRepository = authRepository;
|
||||
|
||||
const emailLoginActor = fromPromise<LoginStatus, { email: string; password: string }>(
|
||||
async ({ input }) => {
|
||||
const guestId = await readGuestId();
|
||||
const result = await authRepository.emailLogin({ ...input, guestId });
|
||||
const result = await authRepo.emailLogin({ ...input, guestId });
|
||||
if (Result.isErr(result)) throw result.error;
|
||||
return "email" as LoginStatus;
|
||||
},
|
||||
@@ -56,11 +59,11 @@ const emailRegisterThenLoginActor = fromPromise<
|
||||
>(async ({ input }) => {
|
||||
const guestId = await readGuestId();
|
||||
|
||||
const registerResult = await authRepository.register({ ...input, guestId });
|
||||
const registerResult = await authRepo.register({ ...input, guestId });
|
||||
if (Result.isErr(registerResult)) throw registerResult.error;
|
||||
|
||||
// 注册后自动登录(对齐 Dart 行为)
|
||||
const loginResult = await authRepository.emailLogin({
|
||||
const loginResult = await authRepo.emailLogin({
|
||||
email: input.email,
|
||||
password: input.password,
|
||||
guestId,
|
||||
@@ -84,7 +87,7 @@ const oauthSignInActor = fromPromise<void, AuthProvider>(async ({ input }) => {
|
||||
const syncGoogleBackendActor = fromPromise<LoginStatus, { idToken: string }>(
|
||||
async ({ input }) => {
|
||||
const guestId = await readGuestId();
|
||||
const result = await authRepository.googleLogin({
|
||||
const result = await authRepo.googleLogin({
|
||||
idToken: input.idToken,
|
||||
guestId,
|
||||
});
|
||||
@@ -98,7 +101,7 @@ const syncFacebookBackendActor = fromPromise<
|
||||
{ accessToken: string }
|
||||
>(async ({ input }) => {
|
||||
const guestId = await readGuestId();
|
||||
const result = await authRepository.facebookLogin({
|
||||
const result = await authRepo.facebookLogin({
|
||||
accessToken: input.accessToken,
|
||||
guestId,
|
||||
});
|
||||
|
||||
@@ -18,6 +18,7 @@ import { setup, fromPromise, assign } from "xstate";
|
||||
|
||||
import type { UiMessage } from "@/models/chat/ui-message";
|
||||
import { chatRepository } from "@/data/repositories/chat_repository";
|
||||
import type { IChatRepository } from "@/data/repositories/interfaces";
|
||||
import { ChatStorage } from "@/data/storage/chat/chat_storage";
|
||||
import { AuthStorage } from "@/data/storage/auth/auth_storage";
|
||||
import { formatDate } from "@/utils/date";
|
||||
@@ -72,6 +73,9 @@ function mapTotalQuotaResult(
|
||||
return fallback;
|
||||
}
|
||||
|
||||
// 仓库以接口类型注入:调用面只看接口,运行时仍是同一单例
|
||||
const chatRepo: IChatRepository = chatRepository;
|
||||
|
||||
interface InitResult {
|
||||
localMessages: UiMessage[];
|
||||
isGuest: boolean;
|
||||
@@ -86,7 +90,7 @@ async function readInitData(): Promise<InitResult> {
|
||||
const [dailyResult, totalResult, localResult] = await Promise.all([
|
||||
chatStorage.getGuestDailyChatQuota(),
|
||||
chatStorage.getGuestTotalQuota(),
|
||||
isGuest ? chatRepository.getLocalMessages() : Promise.resolve(null),
|
||||
isGuest ? chatRepo.getLocalMessages() : Promise.resolve(null),
|
||||
]);
|
||||
|
||||
return {
|
||||
@@ -115,11 +119,11 @@ const sendMessageHttpActor = fromPromise<
|
||||
{ messages: UiMessage[] },
|
||||
{ content: string }
|
||||
>(async ({ input }) => {
|
||||
const result = await chatRepository.sendMessage(input.content);
|
||||
const result = await chatRepo.sendMessage(input.content);
|
||||
if (Result.isErr(result)) throw result.error;
|
||||
|
||||
// 拉取最新本地历史
|
||||
const local = await chatRepository.getLocalMessages();
|
||||
const local = await chatRepo.getLocalMessages();
|
||||
if (Result.isOk(local) && local.data) {
|
||||
return { messages: localMessagesToUi(local.data) };
|
||||
}
|
||||
@@ -130,7 +134,7 @@ const loadMoreHistoryActor = fromPromise<
|
||||
{ messages: UiMessage[]; hasMore: boolean; newOffset: number },
|
||||
{ offset: number }
|
||||
>(async ({ input }) => {
|
||||
const result = await chatRepository.getHistory(PAGE_SIZE, input.offset);
|
||||
const result = await chatRepo.getHistory(PAGE_SIZE, input.offset);
|
||||
if (Result.isErr(result)) throw result.error;
|
||||
|
||||
const page = localMessagesToUi(result.data.messages);
|
||||
|
||||
@@ -14,6 +14,10 @@ import { setup, fromPromise, assign } from "xstate";
|
||||
import type { UserView } from "@/models/user/user";
|
||||
import { userRepository } from "@/data/repositories/user_repository";
|
||||
import { authRepository } from "@/data/repositories/auth_repository";
|
||||
import type {
|
||||
IAuthRepository,
|
||||
IUserRepository,
|
||||
} from "@/data/repositories/interfaces";
|
||||
import { UserStorage } from "@/data/storage/user/user_storage";
|
||||
import { Result } from "@/utils/result";
|
||||
|
||||
@@ -81,10 +85,14 @@ async function readInitData(): Promise<InitData> {
|
||||
// ============================================================
|
||||
// Actors
|
||||
// ============================================================
|
||||
// 仓库以接口类型注入:调用面只看接口,运行时仍是同一单例
|
||||
const userRepo: IUserRepository = userRepository;
|
||||
const authRepo: IAuthRepository = authRepository;
|
||||
|
||||
const userInitActor = fromPromise<InitData>(async () => readInitData());
|
||||
|
||||
const userFetchActor = fromPromise<UserView | null>(async () => {
|
||||
const result = await userRepository.getCurrentUser();
|
||||
const result = await userRepo.getCurrentUser();
|
||||
if (Result.isErr(result)) return null;
|
||||
const view = toView(result.data.toJson());
|
||||
// 持久化到本地
|
||||
@@ -93,7 +101,7 @@ const userFetchActor = fromPromise<UserView | null>(async () => {
|
||||
});
|
||||
|
||||
const userLogoutActor = fromPromise<void>(async () => {
|
||||
await authRepository.logout();
|
||||
await authRepo.logout();
|
||||
await userStorage.clearUserData();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user