import-processor.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import { PrismaClient } from '@prisma/client';
  2. import { ExcelReaderService } from './excel-reader';
  3. import { BulkInserter } from './bulk-inserter';
  4. import { ImportProgressServer } from './websocket-server';
  5. import { ImportProgress, ImportResult, ProcessedSection } from './types';
  6. export class ImportProcessor {
  7. private prisma: PrismaClient;
  8. private reader: ExcelReaderService;
  9. private inserter: BulkInserter;
  10. private progressServer: ImportProgressServer;
  11. constructor() {
  12. this.prisma = new PrismaClient();
  13. this.reader = new ExcelReaderService();
  14. this.inserter = new BulkInserter();
  15. this.progressServer = new ImportProgressServer();
  16. }
  17. async processImport(importId: number): Promise<ImportResult> {
  18. try {
  19. // Get import record with layout configuration
  20. const importRecord = await this.prisma.import.findUnique({
  21. where: { id: importId },
  22. include: {
  23. layout: {
  24. include: {
  25. sections: {
  26. include: { fields: true }
  27. }
  28. }
  29. },
  30. file: true
  31. }
  32. });
  33. if (!importRecord || !importRecord.file) {
  34. throw new Error('Import not found or no file attached');
  35. }
  36. // Update import status to processing
  37. await this.prisma.import.update({
  38. where: { id: importId },
  39. data: { status: 'PROCESSING' }
  40. });
  41. // Initialize progress tracking
  42. const progress: ImportProgress = {
  43. importId,
  44. status: 'processing',
  45. currentSection: '',
  46. currentRow: 0,
  47. totalRows: 0,
  48. errors: [],
  49. processedSections: 0,
  50. totalSections: importRecord.layout?.sections?.length || 0
  51. };
  52. // Read Excel file
  53. const sections = await this.reader.readExcelFile(
  54. importRecord.file.data,
  55. importRecord.layout,
  56. (sectionProgress) => {
  57. this.progressServer.broadcastProgress(importId, sectionProgress);
  58. }
  59. );
  60. // Process each section
  61. const processedSections: ProcessedSection[] = [];
  62. let totalInserted = 0;
  63. for (let i = 0; i < sections.length; i++) {
  64. const section = sections[i];
  65. progress.currentSection = section.name;
  66. progress.processedSections = i + 1;
  67. this.progressServer.broadcastProgress(importId, progress);
  68. try {
  69. const insertedRows = await this.inserter.insertSectionData(
  70. section,
  71. importId,
  72. (rows) => {
  73. progress.currentRow = rows;
  74. this.progressServer.broadcastProgress(importId, progress);
  75. }
  76. );
  77. processedSections.push({
  78. sectionData: section,
  79. insertedRows
  80. });
  81. totalInserted += insertedRows;
  82. } catch (error) {
  83. const errorMessage = `Error processing section ${section.name}: ${error instanceof Error ? error.message : 'Unknown error'}`;
  84. progress.errors.push(errorMessage);
  85. this.progressServer.broadcastProgress(importId, progress);
  86. }
  87. }
  88. // Update import status to completed
  89. await this.prisma.import.update({
  90. where: { id: importId },
  91. data: {
  92. status: 'COMPLETED',
  93. processedAt: new Date()
  94. }
  95. });
  96. progress.status = 'completed';
  97. this.progressServer.broadcastProgress(importId, progress);
  98. return {
  99. success: true,
  100. totalInserted,
  101. sections: processedSections
  102. };
  103. } catch (error) {
  104. // Update import status to failed
  105. await this.prisma.import.update({
  106. where: { id: importId },
  107. data: {
  108. status: 'FAILED',
  109. error: error instanceof Error ? error.message : 'Unknown error',
  110. processedAt: new Date()
  111. }
  112. });
  113. const progress: ImportProgress = {
  114. importId,
  115. status: 'failed',
  116. currentSection: '',
  117. currentRow: 0,
  118. totalRows: 0,
  119. errors: [error instanceof Error ? error.message : 'Unknown error'],
  120. processedSections: 0,
  121. totalSections: 0
  122. };
  123. this.progressServer.broadcastProgress(importId, progress);
  124. return {
  125. success: false,
  126. totalInserted: 0,
  127. sections: [],
  128. errors: [error instanceof Error ? error.message : 'Unknown error']
  129. };
  130. }
  131. }
  132. async validateImport(importId: number): Promise<{ valid: boolean; errors: string[] }> {
  133. const errors: string[] = [];
  134. try {
  135. const importRecord = await this.prisma.import.findUnique({
  136. where: { id: importId },
  137. include: { file: true, layout: true }
  138. });
  139. if (!importRecord) {
  140. errors.push('Import record not found');
  141. return { valid: false, errors };
  142. }
  143. if (!importRecord.file) {
  144. errors.push('No file attached to import');
  145. }
  146. if (!importRecord.layout) {
  147. errors.push('No layout configuration found');
  148. }
  149. return { valid: errors.length === 0, errors };
  150. } catch (error) {
  151. errors.push(`Validation error: ${error instanceof Error ? error.message : 'Unknown error'}`);
  152. return { valid: false, errors };
  153. }
  154. }
  155. }