import-processor.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  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 = ImportProgressServer.getInstance();
  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. }
  31. });
  32. if (!importRecord) {
  33. throw new Error('Import not found');
  34. }
  35. if (!importRecord.fileId) {
  36. throw new Error('No file attached to import');
  37. }
  38. // Get the actual file
  39. const fileRecord = await this.prisma.file.findUnique({
  40. where: { id: importRecord.fileId }
  41. });
  42. if (!fileRecord) {
  43. throw new Error('File not found');
  44. }
  45. // Initialize progress tracking
  46. const progress: ImportProgress = {
  47. importId,
  48. status: 'processing',
  49. currentSection: '',
  50. currentRow: 0,
  51. totalRows: 0,
  52. errors: [],
  53. processedSections: 0,
  54. totalSections: importRecord.layout?.sections?.length ?? 0
  55. };
  56. // Read Excel file
  57. const sections = await this.reader.readExcelFile(
  58. fileRecord.data,
  59. importRecord.layout,
  60. (sectionProgress: ImportProgress) => {
  61. this.progressServer.broadcastProgress(importId, sectionProgress);
  62. }
  63. );
  64. // Process each section
  65. const processedSections: ProcessedSection[] = [];
  66. let totalInserted = 0;
  67. for (let i = 0; i < sections.length; i++) {
  68. const section = sections[i];
  69. if (section) {
  70. progress.currentSection = section.name ?? '';
  71. progress.processedSections = i + 1;
  72. this.progressServer.broadcastProgress(importId, progress);
  73. try {
  74. const insertedRows = await this.inserter.insertSectionData(
  75. section,
  76. importId,
  77. (rows: number) => {
  78. progress.currentRow = rows;
  79. this.progressServer.broadcastProgress(importId, progress);
  80. }
  81. );
  82. processedSections.push({
  83. sectionData: section,
  84. insertedRows
  85. });
  86. totalInserted += insertedRows;
  87. } catch (error: unknown) {
  88. const errorMessage = `Error processing section ${section.name ?? 'unknown'}: ${error instanceof Error ? error.message : 'Unknown error'}`;
  89. progress.errors.push(errorMessage);
  90. this.progressServer.broadcastProgress(importId, progress);
  91. }
  92. }
  93. }
  94. // Import processing completed
  95. progress.status = 'completed';
  96. this.progressServer.broadcastProgress(importId, progress);
  97. return {
  98. success: true,
  99. totalInserted,
  100. sections: processedSections
  101. };
  102. } catch (error) {
  103. // Import processing failed
  104. const progress: ImportProgress = {
  105. importId,
  106. status: 'failed',
  107. currentSection: '',
  108. currentRow: 0,
  109. totalRows: 0,
  110. errors: [error instanceof Error ? error.message : 'Unknown error'],
  111. processedSections: 0,
  112. totalSections: 0
  113. };
  114. this.progressServer.broadcastProgress(importId, progress);
  115. return {
  116. success: false,
  117. totalInserted: 0,
  118. sections: [],
  119. errors: [error instanceof Error ? error.message : 'Unknown error']
  120. };
  121. }
  122. }
  123. async validateImport(importId: number): Promise<{ valid: boolean; errors: string[] }> {
  124. const errors: string[] = [];
  125. try {
  126. const importRecord = await this.prisma.import.findUnique({
  127. where: { id: importId },
  128. include: {
  129. layout: true
  130. }
  131. });
  132. if (!importRecord) {
  133. errors.push('Import record not found');
  134. return { valid: false, errors };
  135. }
  136. if (!importRecord.fileId) {
  137. errors.push('No file attached to import');
  138. }
  139. if (!importRecord.layout) {
  140. errors.push('No layout configuration found');
  141. }
  142. return { valid: errors.length === 0, errors };
  143. } catch (error) {
  144. errors.push(`Validation error: ${error instanceof Error ? error.message : 'Unknown error'}`);
  145. return { valid: false, errors };
  146. }
  147. }
  148. }