2 Commits

Author SHA1 Message Date
Misaka
2ecd8db79a feat: implement production order DAO
- Add ProductionOrderDAO class for production order data access
- Implement queryProductionOrderNumbers() to query SQL Server for production order numbers
- Implement readProductionIds() to read production IDs from text files
- Includes proper error handling and logging

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-28 22:45:06 +08:00
Misaka
3acdbf4b1a feat: implement Excel converter service
- Add ExcelConverterService for processing Excel files
- Support Excel to JSON conversion with header extraction
- Implement merge functionality for multiple Excel files
- Use exceljs library for Excel file operations
- Integrate with LoggerService for operation tracking

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-28 22:43:47 +08:00
2 changed files with 170 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
import { DatabaseService } from '../services/database.service';
import { LoggerService } from '../services/logger.service';
export class ProductionOrderDAO {
constructor(private dbService: DatabaseService) {}
async queryProductionOrderNumbers(productionIds: string[]): Promise<string[]> {
try {
LoggerService.info(`Querying production orders for ${productionIds.length} IDs`);
const idsString = productionIds.map((id) => `'${id}'`).join(',');
const query = `
SELECT DISTINCT production_order_no
FROM production_orders
WHERE production_id IN (${idsString})
`;
const result = await this.dbService.executeSQLServerQuery(query);
const orderNumbers = result.rows.map((row: any) => row.production_order_no);
LoggerService.info(`Found ${orderNumbers.length} production orders`);
return orderNumbers;
} catch (error) {
LoggerService.error('Failed to query production orders', error);
throw error;
}
}
async readProductionIds(filePath: string): Promise<string[]> {
try {
const fs = await import('fs/promises');
const content = await fs.readFile(filePath, 'utf-8');
const ids = content
.split('\n')
.map((line) => line.trim())
.filter((line) => line.length > 0);
LoggerService.info(`Read ${ids.length} production IDs from file`);
return ids;
} catch (error) {
LoggerService.error('Failed to read production IDs file', error);
throw error;
}
}
}

View File

@@ -0,0 +1,125 @@
import ExcelJS from 'exceljs';
import path from 'path';
import { LoggerService } from './logger.service';
export class ExcelConverterService {
constructor(private verbose: boolean = true) {}
async convert(inputPath: string, outputPath?: string): Promise<any[]> {
try {
// Validate input file
const fs = await import('fs/promises');
// Check file exists
try {
await fs.access(inputPath, fs.constants.R_OK);
} catch {
throw new Error(`File not found: ${inputPath}`);
}
// Check file extension
if (!inputPath.match(/\.(xlsx|xls)$/i)) {
throw new Error(`Invalid file type. Expected .xlsx or .xls file: ${inputPath}`);
}
LoggerService.info(`Converting Excel file: ${inputPath}`);
const workbook = new ExcelJS.Workbook();
await workbook.xlsx.readFile(inputPath);
const worksheet = workbook.worksheets[0];
if (!worksheet) {
throw new Error('No worksheet found in Excel file');
}
const data: any[] = [];
const headers: string[] = [];
// Extract headers
const headerRow = worksheet.getRow(1);
headerRow.eachCell((cell, colNumber) => {
headers[colNumber - 1] = cell.text;
});
// Extract data rows
worksheet.eachRow((row, rowNumber) => {
if (rowNumber === 1) return; // Skip header row
const rowData: any = {};
row.eachCell((cell, colNumber) => {
const header = headers[colNumber - 1];
if (header) {
// Extract actual value from cell (handles formulas, rich text, etc.)
const value = cell.value;
const extractedValue = typeof value === 'object' && value !== null
? (value as any).result || (value as any).text || value
: value;
rowData[header] = extractedValue;
}
});
data.push(rowData);
});
// Validate we got data
if (data.length === 0) {
LoggerService.warn('Excel file contains no data rows');
}
LoggerService.info(`Extracted ${data.length} rows from Excel file`);
// Save to output path if provided
if (outputPath) {
await this.saveAsJson(data, outputPath);
}
return data;
} catch (error) {
LoggerService.error('Excel conversion failed', error);
throw error;
}
}
async mergeExcelFiles(filePaths: string[], outputPath: string): Promise<void> {
try {
LoggerService.info(`Merging ${filePaths.length} Excel files...`);
const allData: any[] = [];
for (const filePath of filePaths) {
const data = await this.convert(filePath);
allData.push(...data);
}
// Create new Excel workbook with merged data
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('Merged Data');
if (allData.length > 0) {
const headers = Object.keys(allData[0]);
worksheet.addRow(headers);
allData.forEach((row) => {
const values = headers.map((header) => row[header]);
worksheet.addRow(values);
});
}
await workbook.xlsx.writeFile(outputPath);
LoggerService.info(`Merged data saved to: ${outputPath}`);
} catch (error) {
LoggerService.error('Excel merge failed', error);
throw error;
}
}
private async saveAsJson(data: any[], outputPath: string): Promise<void> {
try {
const fs = await import('fs/promises');
await fs.writeFile(outputPath, JSON.stringify(data, null, 2), 'utf-8');
LoggerService.info(`Data saved to JSON: ${outputPath}`);
} catch (error) {
LoggerService.error(`Failed to save JSON to ${outputPath}`, error);
throw new Error(`Failed to save JSON output: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
}