refactor: auto-init guest chat quota on first read, clean up logs

- Remove unused `quotaWarningThreshold` and `warningThreshold` getter from `GuestChatQuota` DTO
- Apply get-or-init pattern in `ChatStorage.getGuestDailyChatQuota` and `getGuestTotalQuota`: when no existing value, initialize with DTO defaults (`threshold` = max per day, `totalQuotaDefault`) and return the seeded data
- Fall back to original `null` result if initialization write fails
- Consolidate logging strategy: drop per-actor trace logs in `chat-machine.ts`, keep business-decision logs in 3 assign actions, move all actor ENTRY/API/DONE chains to `chat-machine.actors.ts` and helper load logs to `chat-machine.helpers.ts`
This commit is contained in:
2026-06-15 17:39:50 +08:00
parent fa694af723
commit 31f2433c4b
4 changed files with 88 additions and 170 deletions
-11
View File
@@ -11,9 +11,7 @@ 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;
@@ -49,15 +47,6 @@ export class GuestChatQuota {
return process.env.NODE_ENV !== "production";
}
/**
* 获取当前环境的配额警告阈值
*/
static get warningThreshold(): number {
return GuestChatQuota.isDevelopment
? GuestChatQuota.quotaWarningThresholdTest
: GuestChatQuota.quotaWarningThreshold;
}
/**
* 获取当前环境的每日最大配额
*/
+40 -3
View File
@@ -18,9 +18,11 @@ import {
GuestChatQuotaSchema,
type GuestChatQuotaData,
} from "@/data/schemas/chat/guest_chat_quota";
import { GuestChatQuota as GuestChatQuotaDto } from "@/data/dto/chat/guest_chat_quota";
import { z } from "zod";
import { type Result as ResultT } from "@/utils/result";
import { type Result as ResultT, Result } from "@/utils/result";
import { formatDate } from "@/utils/date";
import { SpAsyncUtil } from "@/utils/storage";
import { StorageKeys } from "../storage_keys";
import type { IChatStorage } from "./ichat_storage";
@@ -41,7 +43,27 @@ export class ChatStorage implements IChatStorage {
// ---- guest daily chat quota ----
getGuestDailyChatQuota(): Promise<ResultT<GuestChatQuotaData | null>> {
return SpAsyncUtil.getJson(StorageKeys.guestChatQuota, GuestChatQuotaSchema);
// get-or-init**有**返**有****无**则**用** DTO 默认**值**`threshold` = max per day**初**始**化**并**返**回**
return SpAsyncUtil.getJson(
StorageKeys.guestChatQuota,
GuestChatQuotaSchema,
).then(async (existing) => {
if (existing.success && existing.data != null) {
return existing;
}
const today = formatDate(new Date());
const initResult = await this.setGuestDailyChatQuota(
GuestChatQuotaDto.threshold,
today,
);
if (Result.isErr(initResult)) {
return existing; // **初**始**化**失**败** → **返**原**始** null
}
return {
success: true,
data: { remaining: GuestChatQuotaDto.threshold, date: today },
};
});
}
setGuestDailyChatQuota(remaining: number, date: string): Promise<ResultT<void>> {
@@ -56,7 +78,22 @@ export class ChatStorage implements IChatStorage {
// ---- guest total quota ----
getGuestTotalQuota(): Promise<ResultT<number | null>> {
return SpAsyncUtil.getJson(StorageKeys.guestTotalQuotaRemaining, z.number());
// get-or-init**有**返**有****无**则**用** DTO 默认**值**`totalQuotaDefault`**初**始**化**并**返**回**
return SpAsyncUtil.getJson(
StorageKeys.guestTotalQuotaRemaining,
z.number(),
).then(async (existing) => {
if (existing.success && existing.data != null) {
return existing;
}
const initResult = await this.setGuestTotalQuota(
GuestChatQuotaDto.totalQuotaDefault,
);
if (Result.isErr(initResult)) {
return existing;
}
return { success: true, data: GuestChatQuotaDto.totalQuotaDefault };
});
}
setGuestTotalQuota(remaining: number): Promise<ResultT<void>> {