Files
cozsweet-frontend-nextjs/src/data/dto/chat/guest_chat_quota.ts
T
admin fa694af723 docs(plan): add chat state machine refactor design
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.
2026-06-15 17:05:32 +08:00

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);
}
}