/** * 对话状态常量 */ export const ConversationStatus = { ACTIVE: 'ACTIVE', ENDED: 'ENDED', ARCHIVED: 'ARCHIVED', } as const; export type ConversationStatusType = (typeof ConversationStatus)[keyof typeof ConversationStatus]; /** * 咨询阶段常量 */ export const ConsultingStage = { GREETING: 'greeting', NEEDS_DISCOVERY: 'needs_discovery', INFO_COLLECTION: 'info_collection', ASSESSMENT: 'assessment', RECOMMENDATION: 'recommendation', OBJECTION_HANDLING: 'objection_handling', CONVERSION: 'conversion', HANDOFF: 'handoff', } as const; export type ConsultingStageType = (typeof ConsultingStage)[keyof typeof ConsultingStage]; /** * 咨询状态结构(存储为JSON) */ export interface ConsultingStateJson { strategyId: string; currentStageId: string; stageTurnCount: number; collectedInfo: Record; assessmentResult?: { recommendedPrograms: string[]; suitabilityScore: number; highlights: string[]; concerns: string[]; }; conversionPath?: string; stageHistory: Array<{ stageId: string; enteredAt: string; exitedAt?: string; turnsInStage: number; }>; } /** * Device info structure */ export interface DeviceInfo { ip?: string; userAgent?: string; fingerprint?: string; region?: string; } /** * Conversation Domain Entity */ export class ConversationEntity { readonly id: string; userId: string; status: ConversationStatusType; title: string | null; summary: string | null; category: string | null; messageCount: number; userMessageCount: number; assistantMessageCount: number; totalInputTokens: number; totalOutputTokens: number; rating: number | null; feedback: string | null; hasConverted: boolean; consultingStage: ConsultingStageType | null; consultingState: ConsultingStateJson | null; collectedInfo: Record | null; recommendedPrograms: string[] | null; conversionPath: string | null; deviceInfo: DeviceInfo | null; readonly createdAt: Date; updatedAt: Date; endedAt: Date | null; private constructor(props: { id: string; userId: string; status: ConversationStatusType; title: string | null; summary: string | null; category: string | null; messageCount: number; userMessageCount: number; assistantMessageCount: number; totalInputTokens: number; totalOutputTokens: number; rating: number | null; feedback: string | null; hasConverted: boolean; consultingStage: ConsultingStageType | null; consultingState: ConsultingStateJson | null; collectedInfo: Record | null; recommendedPrograms: string[] | null; conversionPath: string | null; deviceInfo: DeviceInfo | null; createdAt: Date; updatedAt: Date; endedAt: Date | null; }) { Object.assign(this, props); } static create(props: { id: string; userId: string; title?: string; category?: string; deviceInfo?: DeviceInfo; }): ConversationEntity { const now = new Date(); return new ConversationEntity({ id: props.id, userId: props.userId, status: ConversationStatus.ACTIVE, title: props.title || null, summary: null, category: props.category || null, messageCount: 0, userMessageCount: 0, assistantMessageCount: 0, totalInputTokens: 0, totalOutputTokens: 0, rating: null, feedback: null, hasConverted: false, consultingStage: ConsultingStage.GREETING, consultingState: null, collectedInfo: null, recommendedPrograms: null, conversionPath: null, deviceInfo: props.deviceInfo || null, createdAt: now, updatedAt: now, endedAt: null, }); } static fromPersistence(props: { id: string; userId: string; status: ConversationStatusType; title: string | null; summary: string | null; category: string | null; messageCount: number; userMessageCount: number; assistantMessageCount: number; totalInputTokens: number; totalOutputTokens: number; rating: number | null; feedback: string | null; hasConverted: boolean; consultingStage: ConsultingStageType | null; consultingState: ConsultingStateJson | null; collectedInfo: Record | null; recommendedPrograms: string[] | null; conversionPath: string | null; deviceInfo: DeviceInfo | null; createdAt: Date; updatedAt: Date; endedAt: Date | null; }): ConversationEntity { return new ConversationEntity(props); } incrementMessageCount(role: 'user' | 'assistant'): void { this.messageCount++; if (role === 'user') { this.userMessageCount++; } else { this.assistantMessageCount++; } this.updatedAt = new Date(); } addTokens(inputTokens: number, outputTokens: number): void { this.totalInputTokens += inputTokens; this.totalOutputTokens += outputTokens; this.updatedAt = new Date(); } updateConsultingStage(stage: ConsultingStageType): void { this.consultingStage = stage; this.updatedAt = new Date(); } updateConsultingState(state: ConsultingStateJson): void { this.consultingState = state; this.collectedInfo = state.collectedInfo; if (state.assessmentResult?.recommendedPrograms) { this.recommendedPrograms = state.assessmentResult.recommendedPrograms; } if (state.conversionPath) { this.conversionPath = state.conversionPath; } this.updatedAt = new Date(); } setRating(rating: number, feedback?: string): void { this.rating = rating; if (feedback) { this.feedback = feedback; } this.updatedAt = new Date(); } markAsConverted(): void { this.hasConverted = true; this.updatedAt = new Date(); } end(): void { this.status = ConversationStatus.ENDED; this.endedAt = new Date(); this.updatedAt = new Date(); } archive(): void { this.status = ConversationStatus.ARCHIVED; this.updatedAt = new Date(); } isActive(): boolean { return this.status === ConversationStatus.ACTIVE; } isEnded(): boolean { return this.status === ConversationStatus.ENDED; } }