import-processor.ts 4.6 KB

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