fa694af723
Update implementation_plan.md with a detailed refactor plan that splits `chatInitActor` into two independent actors (`loadQuotaActor` + `loadHistoryActor`) and redesigns the history loading flow to follow local → network → save semantics so the UI sees local data first, then network data. Key changes outlined in the plan: - Add new events: `ChatQuotaLoaded`, `ChatHistoryLocalLoaded`, `ChatHistoryNetworkLoaded`, `ChatHistorySyncDone` - Remove dead-code `ChatInit` event - Extend `ChatState` with `quotaLoaded` and `historyLoaded` flags - Use an `always` barrier in `guestSession.initializing` and `userSession.initializing` so both tasks must complete before transitioning to `ready` - Guest init runs `loadQuota` + `loadHistory` in parallel; non-guest init runs `chatWebSocket` + `loadHistory` as independent tasks Also adds `implementation_plan` to .gitignore.
100 lines
2.3 KiB
TypeScript
100 lines
2.3 KiB
TypeScript
/**
|
|
* 游客每日聊天配额数据 DTO
|
|
*/
|
|
import {
|
|
GuestChatQuotaSchema,
|
|
type GuestChatQuotaInput,
|
|
type GuestChatQuotaData,
|
|
} from "@/data/schemas/chat/guest_chat_quota";
|
|
|
|
export class GuestChatQuota {
|
|
// 静态常量
|
|
static readonly maxQuotaPerDay = 40;
|
|
static readonly maxQuotaPerDayTest = 4;
|
|
static readonly quotaWarningThreshold = 20;
|
|
|
|
static readonly quotaWarningThresholdTest = 2;
|
|
static readonly defaultTotalQuota = 50;
|
|
static readonly defaultTotalQuotaDev = 5;
|
|
static readonly quotaExhausted = 0;
|
|
|
|
declare readonly remaining: number;
|
|
declare readonly date: string;
|
|
|
|
private constructor(input: GuestChatQuotaInput) {
|
|
const data = GuestChatQuotaSchema.parse(input);
|
|
Object.assign(this, data);
|
|
Object.freeze(this);
|
|
}
|
|
|
|
static from(input: GuestChatQuotaInput): GuestChatQuota {
|
|
return new GuestChatQuota(input);
|
|
}
|
|
|
|
static fromJson(json: unknown): GuestChatQuota {
|
|
return GuestChatQuota.from(json as GuestChatQuotaInput);
|
|
}
|
|
|
|
/**
|
|
* 配额耗尽状态值
|
|
*/
|
|
static get exhausted(): number {
|
|
return GuestChatQuota.quotaExhausted;
|
|
}
|
|
|
|
/**
|
|
* 是否处于开发环境
|
|
*/
|
|
static get isDevelopment(): boolean {
|
|
return process.env.NODE_ENV !== "production";
|
|
}
|
|
|
|
/**
|
|
* 获取当前环境的配额警告阈值
|
|
*/
|
|
static get warningThreshold(): number {
|
|
return GuestChatQuota.isDevelopment
|
|
? GuestChatQuota.quotaWarningThresholdTest
|
|
: GuestChatQuota.quotaWarningThreshold;
|
|
}
|
|
|
|
/**
|
|
* 获取当前环境的每日最大配额
|
|
*/
|
|
static get threshold(): number {
|
|
return GuestChatQuota.isDevelopment
|
|
? GuestChatQuota.maxQuotaPerDayTest
|
|
: GuestChatQuota.maxQuotaPerDay;
|
|
}
|
|
|
|
/**
|
|
* 获取当前环境的总配额默认值
|
|
*/
|
|
static get totalQuotaDefault(): number {
|
|
return GuestChatQuota.isDevelopment
|
|
? GuestChatQuota.defaultTotalQuotaDev
|
|
: GuestChatQuota.defaultTotalQuota;
|
|
}
|
|
|
|
/**
|
|
* 创建初始配额实例
|
|
*/
|
|
static initial(todayString: string): GuestChatQuota {
|
|
return new GuestChatQuota({
|
|
remaining: GuestChatQuota.threshold,
|
|
date: todayString,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 是否需要重置(跨天)
|
|
*/
|
|
needsReset(todayString: string): boolean {
|
|
return this.date !== todayString;
|
|
}
|
|
|
|
toJson(): GuestChatQuotaData {
|
|
return GuestChatQuotaSchema.parse(this);
|
|
}
|
|
}
|