/** * Use Case: RequestAvatarGenerationUseCase * * Initiates the avatar generation process by validating the face photo * and creating a generation request. */ import type { IAvatarGenerationRepository } from '../../domain/repositories/IAvatarGenerationRepository'; import type { FaceValidationPort } from '../ports/FaceValidationPort'; import type { AvatarGenerationPort } from '../ports/AvatarGenerationPort'; import { AvatarGenerationRequest, type RacingSuitColor, type AvatarStyle } from '../../domain/entities/AvatarGenerationRequest'; export interface RequestAvatarGenerationCommand { userId: string; facePhotoData: string; // Base64 encoded image data suitColor: RacingSuitColor; style?: AvatarStyle; } export interface RequestAvatarGenerationResult { requestId: string; status: 'validating' | 'generating' | 'completed' | 'failed'; avatarUrls?: string[]; errorMessage?: string; } export class RequestAvatarGenerationUseCase { constructor( private readonly avatarRepository: IAvatarGenerationRepository, private readonly faceValidation: FaceValidationPort, private readonly avatarGeneration: AvatarGenerationPort, ) {} async execute(command: RequestAvatarGenerationCommand): Promise { // Create the generation request const requestId = this.generateId(); const request = AvatarGenerationRequest.create({ id: requestId, userId: command.userId, facePhotoUrl: `data:image/jpeg;base64,${command.facePhotoData}`, suitColor: command.suitColor, style: command.style, }); // Mark as validating request.markAsValidating(); await this.avatarRepository.save(request); // Validate the face photo const validationResult = await this.faceValidation.validateFacePhoto(command.facePhotoData); if (!validationResult.isValid) { request.fail(validationResult.errorMessage || 'Face validation failed'); await this.avatarRepository.save(request); return { requestId, status: 'failed', errorMessage: validationResult.errorMessage || 'Please upload a clear photo of your face', }; } if (!validationResult.hasFace) { request.fail('No face detected in the image'); await this.avatarRepository.save(request); return { requestId, status: 'failed', errorMessage: 'No face detected. Please upload a photo that clearly shows your face.', }; } if (validationResult.faceCount > 1) { request.fail('Multiple faces detected'); await this.avatarRepository.save(request); return { requestId, status: 'failed', errorMessage: 'Multiple faces detected. Please upload a photo with only your face.', }; } // Mark as generating request.markAsGenerating(); await this.avatarRepository.save(request); // Generate avatars const generationResult = await this.avatarGeneration.generateAvatars({ facePhotoUrl: request.facePhotoUrl, prompt: request.buildPrompt(), suitColor: request.suitColor, style: request.style, count: 3, // Generate 3 options }); if (!generationResult.success) { request.fail(generationResult.errorMessage || 'Avatar generation failed'); await this.avatarRepository.save(request); return { requestId, status: 'failed', errorMessage: generationResult.errorMessage || 'Failed to generate avatars. Please try again.', }; } // Complete with generated avatars const avatarUrls = generationResult.avatars.map(a => a.url); request.completeWithAvatars(avatarUrls); await this.avatarRepository.save(request); return { requestId, status: 'completed', avatarUrls, }; } private generateId(): string { if (typeof crypto !== 'undefined' && crypto.randomUUID) { return crypto.randomUUID(); } return 'avatar-' + Date.now().toString(36) + Math.random().toString(36).substr(2, 9); } }