import { Logger } from '../utils/Logger'; import { FingerprintManager } from './FingerprintManager'; interface ShipInfo { orderId: string; platform: string; shopId: string; trackingNumber: string; carrier: string; items: Array<{ productId: string; skuId: string; quantity: number; }>; shippingAddress: { name: string; phone: string; address: string; city: string; state: string; zipCode: string; country: string; }; } interface ShipResult { success: boolean; orderId: string; trackingNumber?: string; carrier?: string; status: 'shipped' | 'failed' | 'pending'; message: string; timestamp: string; traceId: string; } interface ShipTask { taskId: string; orderId: string; shopId: string; platform: string; status: 'pending' | 'processing' | 'completed' | 'failed'; retryCount: number; maxRetries: number; createdAt: string; updatedAt: string; traceId: string; } export class AutoShipService { private logger = new Logger('AutoShipService'); private fingerprintManager: FingerprintManager; private tasks: Map = new Map(); private readonly MAX_RETRIES = 3; private readonly RETRY_DELAY_MS = 5000; constructor(fingerprintManager: FingerprintManager) { this.fingerprintManager = fingerprintManager; } async createShipTask(shipInfo: ShipInfo, traceId?: string): Promise { const tid = traceId || this.generateTraceId(); const taskId = `SHIP-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; this.logger.info('Creating ship task', { taskId, orderId: shipInfo.orderId, platform: shipInfo.platform, traceId: tid, }); const task: ShipTask = { taskId, orderId: shipInfo.orderId, shopId: shipInfo.shopId, platform: shipInfo.platform, status: 'pending', retryCount: 0, maxRetries: this.MAX_RETRIES, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), traceId: tid, }; this.tasks.set(taskId, task); await this.reportTaskCreated(task); return task; } async processShipTask(taskId: string, shipInfo: ShipInfo): Promise { const task = this.tasks.get(taskId); if (!task) { throw new Error(`Task not found: ${taskId}`); } task.status = 'processing'; task.updatedAt = new Date().toISOString(); this.logger.info('Processing ship task', { taskId, orderId: task.orderId, platform: task.platform, traceId: task.traceId, }); try { const context = await this.fingerprintManager.createIsolatedContext( task.shopId, undefined, task.traceId ); if (!context.success || !context.context) { throw new Error(`Failed to create isolated context: ${context.error}`); } const result = await this.executeShipOperation(task, shipInfo, context.context); if (result.success) { task.status = 'completed'; this.logger.info('Ship task completed', { taskId, orderId: task.orderId, trackingNumber: result.trackingNumber, traceId: task.traceId, }); } else if (task.retryCount < task.maxRetries) { task.retryCount++; task.status = 'pending'; this.logger.warn('Ship task failed, will retry', { taskId, orderId: task.orderId, retryCount: task.retryCount, error: result.message, traceId: task.traceId, }); setTimeout(() => { this.processShipTask(taskId, shipInfo); }, this.RETRY_DELAY_MS * task.retryCount); } else { task.status = 'failed'; this.logger.error('Ship task failed after max retries', { taskId, orderId: task.orderId, error: result.message, traceId: task.traceId, }); } task.updatedAt = new Date().toISOString(); await this.reportTaskStatus(task, result); return result; } catch (error: any) { task.status = 'failed'; task.updatedAt = new Date().toISOString(); this.logger.error('Ship task processing error', { taskId, orderId: task.orderId, error: error.message, traceId: task.traceId, }); const errorResult: ShipResult = { success: false, orderId: task.orderId, status: 'failed', message: error.message, timestamp: new Date().toISOString(), traceId: task.traceId, }; await this.reportTaskStatus(task, errorResult); return errorResult; } } private async executeShipOperation( task: ShipTask, shipInfo: ShipInfo, context: any ): Promise { this.logger.info('Executing ship operation', { taskId: task.taskId, orderId: task.orderId, platform: task.platform, traceId: task.traceId, }); try { switch (task.platform.toLowerCase()) { case 'tiktok': return await this.shipTikTokOrder(task, shipInfo, context); case 'temu': return await this.shipTemuOrder(task, shipInfo, context); case '1688': return await this.ship1688Order(task, shipInfo, context); default: throw new Error(`Unsupported platform: ${task.platform}`); } } catch (error: any) { return { success: false, orderId: task.orderId, status: 'failed', message: `Ship operation failed: ${error.message}`, timestamp: new Date().toISOString(), traceId: task.traceId, }; } } private async shipTikTokOrder( task: ShipTask, shipInfo: ShipInfo, context: any ): Promise { this.logger.info('Shipping TikTok order', { taskId: task.taskId, orderId: task.orderId, traceId: task.traceId, }); await this.simulateDelay(2000, 4000); const success = Math.random() > 0.1; if (success) { return { success: true, orderId: task.orderId, trackingNumber: shipInfo.trackingNumber, carrier: shipInfo.carrier, status: 'shipped', message: 'Order shipped successfully on TikTok', timestamp: new Date().toISOString(), traceId: task.traceId, }; } else { return { success: false, orderId: task.orderId, status: 'failed', message: 'Failed to ship order on TikTok: Platform validation error', timestamp: new Date().toISOString(), traceId: task.traceId, }; } } private async shipTemuOrder( task: ShipTask, shipInfo: ShipInfo, context: any ): Promise { this.logger.info('Shipping Temu order', { taskId: task.taskId, orderId: task.orderId, traceId: task.traceId, }); await this.simulateDelay(1500, 3000); const success = Math.random() > 0.15; if (success) { return { success: true, orderId: task.orderId, trackingNumber: shipInfo.trackingNumber, carrier: shipInfo.carrier, status: 'shipped', message: 'Order shipped successfully on Temu', timestamp: new Date().toISOString(), traceId: task.traceId, }; } else { return { success: false, orderId: task.orderId, status: 'failed', message: 'Failed to ship order on Temu: Order status not eligible for shipping', timestamp: new Date().toISOString(), traceId: task.traceId, }; } } private async ship1688Order( task: ShipTask, shipInfo: ShipInfo, context: any ): Promise { this.logger.info('Shipping 1688 order', { taskId: task.taskId, orderId: task.orderId, traceId: task.traceId, }); await this.simulateDelay(2500, 5000); const success = Math.random() > 0.2; if (success) { return { success: true, orderId: task.orderId, trackingNumber: shipInfo.trackingNumber, carrier: shipInfo.carrier, status: 'shipped', message: 'Order shipped successfully on 1688', timestamp: new Date().toISOString(), traceId: task.traceId, }; } else { return { success: false, orderId: task.orderId, status: 'failed', message: 'Failed to ship order on 1688: Authentication required', timestamp: new Date().toISOString(), traceId: task.traceId, }; } } async batchProcessShipTasks(tasks: Array<{ taskId: string; shipInfo: ShipInfo }>): Promise { this.logger.info('Starting batch ship processing', { count: tasks.length }); const results: ShipResult[] = []; for (const { taskId, shipInfo } of tasks) { try { const result = await this.processShipTask(taskId, shipInfo); results.push(result); } catch (error: any) { results.push({ success: false, orderId: shipInfo.orderId, status: 'failed', message: `Batch processing error: ${error.message}`, timestamp: new Date().toISOString(), traceId: shipInfo.orderId, }); } await this.simulateDelay(1000, 2000); } this.logger.info('Batch ship processing completed', { total: tasks.length, success: results.filter(r => r.success).length, failed: results.filter(r => !r.success).length, }); return results; } getTaskStatus(taskId: string): ShipTask | undefined { return this.tasks.get(taskId); } getTasksByShop(shopId: string): ShipTask[] { return Array.from(this.tasks.values()).filter(t => t.shopId === shopId); } getTasksByStatus(status: ShipTask['status']): ShipTask[] { return Array.from(this.tasks.values()).filter(t => t.status === status); } private async reportTaskCreated(task: ShipTask): Promise { this.logger.info('Reporting task created to backend', { taskId: task.taskId, orderId: task.orderId, traceId: task.traceId, }); } private async reportTaskStatus(task: ShipTask, result: ShipResult): Promise { this.logger.info('Reporting task status to backend', { taskId: task.taskId, orderId: task.orderId, status: task.status, traceId: task.traceId, }); try { const response = await fetch('http://localhost:3000/api/plugin/ship-status', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ taskId: task.taskId, orderId: task.orderId, shopId: task.shopId, platform: task.platform, status: task.status, trackingNumber: result.trackingNumber, carrier: result.carrier, message: result.message, timestamp: result.timestamp, traceId: task.traceId, }), }); if (!response.ok) { this.logger.warn('Failed to report task status', { taskId: task.taskId, statusCode: response.status, }); } } catch (error: any) { this.logger.warn('Error reporting task status', { taskId: task.taskId, error: error.message, }); } } private async simulateDelay(minMs: number, maxMs: number): Promise { const delay = Math.floor(Math.random() * (maxMs - minMs + 1)) + minMs; return new Promise(resolve => setTimeout(resolve, delay)); } private generateTraceId(): string { return `trace-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; } }