refactor(chat): extract XState actors into chat-machine.actors.ts
- Split chat state machine actors (chatInit, sendMessageHttp, loadMoreHistory, chatWebSocket) into a dedicated module - Add comprehensive console logging across the chat state machine for full trace debugging - Comment out margin on text bubble CSS for layout adjustment - Add debug logging to chat input bar send handler - Clean up redundant comments in auth-screen component
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Chat 状态机:纯函数 + 数据加载
|
||||
*
|
||||
* 从 `chat-machine.ts` 抽出的"helpers"段:
|
||||
* - 翻页大小常量
|
||||
* - 仓库注入(接口类型 → 单例)
|
||||
* - DTO ↔ UiMessage 映射(**纯函数** —— 可单测)
|
||||
* - Result → number 映射(**纯函数** —— 可单测)
|
||||
* - 初始数据装配(**仅**游客有意义 —— `chatInitActor` 调用)
|
||||
*
|
||||
* 设计目标:
|
||||
* - helpers **无 XState 依赖**(可独立 import / 测试)
|
||||
* - actors 依赖 helpers(import)
|
||||
* - machine 依赖 actors(import)—— **不**直接依赖 helpers
|
||||
*/
|
||||
|
||||
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 { Result } from "@/utils/result";
|
||||
|
||||
// ============================================================// Constants
|
||||
// ============================================================
|
||||
/** 翻历史每页大小(**仅** `loadMoreHistoryActor` 用) */
|
||||
export const PAGE_SIZE = 20;
|
||||
|
||||
// ============================================================// Repository injection
|
||||
// ============================================================
|
||||
// 仓库以接口类型注入:调用面只看接口,运行时仍是同一单例
|
||||
export const chatRepo: IChatRepository = chatRepository;
|
||||
|
||||
// ============================================================// DTO ↔ UiMessage 映射
|
||||
// ============================================================
|
||||
/**
|
||||
* ChatMessage[] → UiMessage[](**纯函数**)
|
||||
* - 与 Dart `LocalMessage → ChatMessage` 等价
|
||||
* - 业务事实:`role === "assistant"` → `isFromAI: true`
|
||||
*/
|
||||
export function localMessagesToUi(
|
||||
records: ReadonlyArray<{
|
||||
content: string;
|
||||
role: string;
|
||||
createdAt: string;
|
||||
}>,
|
||||
): UiMessage[] {
|
||||
return records.map((m) => ({
|
||||
content: m.content,
|
||||
isFromAI: m.role === "assistant",
|
||||
date: m.createdAt,
|
||||
}));
|
||||
}
|
||||
|
||||
// ============================================================// Result → number 映射
|
||||
// ============================================================
|
||||
/** 日配额 Result → number(**纯函数**) */
|
||||
export function mapQuotaResult(
|
||||
r: Result<{ remaining: number } | null>,
|
||||
fallback: number,
|
||||
): number {
|
||||
if (r.success && r.data != null) return r.data.remaining;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
/** 总配额 Result → number(**纯函数**) */
|
||||
export function mapTotalQuotaResult(
|
||||
r: Result<number | null>,
|
||||
fallback: number,
|
||||
): number {
|
||||
if (r.success && r.data != null) return r.data;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
// ============================================================// Initial data
|
||||
// ============================================================
|
||||
export interface InitResult {
|
||||
localMessages: UiMessage[];
|
||||
isGuest: boolean;
|
||||
guestRemainingQuota: number;
|
||||
guestTotalQuota: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取初始数据(`chatInitActor` 调用)
|
||||
*
|
||||
* - `isGuest` 在 chat 业务层仅用于"是否加载本地历史" + "onDone 条件赋配额"
|
||||
* —— **不**是鉴权信号。鉴权判断已上提到 chat-screen 派生。
|
||||
* - 配额 fallback 0 —— 非游客路径用不到;onDone 会**条件**赋。
|
||||
*/
|
||||
export async function readInitData(): Promise<InitResult> {
|
||||
const chatStorage = ChatStorage.getInstance();
|
||||
const isGuest = !AuthStorage.getInstance().hasLoginToken();
|
||||
|
||||
const [dailyResult, totalResult, localResult] = await Promise.all([
|
||||
chatStorage.getGuestDailyChatQuota(),
|
||||
chatStorage.getGuestTotalQuota(),
|
||||
isGuest ? chatRepo.getLocalMessages() : Promise.resolve(null),
|
||||
]);
|
||||
|
||||
return {
|
||||
isGuest,
|
||||
guestRemainingQuota: mapQuotaResult(
|
||||
dailyResult as Result<{ remaining: number } | null>,
|
||||
0,
|
||||
),
|
||||
guestTotalQuota: mapTotalQuotaResult(
|
||||
totalResult as Result<number | null>,
|
||||
0,
|
||||
),
|
||||
localMessages:
|
||||
localResult && Result.isOk(localResult) && localResult.data
|
||||
? localMessagesToUi(localResult.data)
|
||||
: [],
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user