style: apply Prettier formatting across codebase

Apply consistent code formatting using Prettier to improve code readability
and maintain style consistency throughout the project.

Co-Authored-By: Claude (glm-5) <noreply@anthropic.com>
This commit is contained in:
Misaka_Company
2026-03-04 14:24:16 +08:00
parent 88c8c256e2
commit c61776a1ff
6 changed files with 115 additions and 107 deletions

View File

@@ -1,9 +1,11 @@
# Implementation Plan: Auto-import Extracted Data to Database # Implementation Plan: Auto-import Extracted Data to Database
## Overview ## Overview
Implement automatic database import after ERP data extraction completes. The merged Excel file will be read and written to the `dbo_DiscreteMaterialPlanData` table. Implement automatic database import after ERP data extraction completes. The merged Excel file will be read and written to the `dbo_DiscreteMaterialPlanData` table.
## Requirements ## Requirements
- **Trigger**: Automatic after extraction completes - **Trigger**: Automatic after extraction completes
- **Delete Strategy**: Batch delete by `SourceNumber` before insert - **Delete Strategy**: Batch delete by `SourceNumber` before insert
- **Batch Insert**: 1000 records per batch - **Batch Insert**: 1000 records per batch
@@ -33,60 +35,67 @@ ExtractorService
## Field Mapping ## Field Mapping
| Excel Header | Database Column | Notes | | Excel Header | Database Column | Notes |
|-------------|-----------------|-------| | ------------ | ------------------------ | ---------------- |
| 工厂 | Factory | | | 工厂 | Factory | |
| 备料状态 | MaterialStatus | | | 备料状态 | MaterialStatus | |
| 备料计划单号 | PlanNumber | | | 备料计划单号 | PlanNumber | |
| 来源单号 | SourceNumber | **Deletion key** | | 来源单号 | SourceNumber | **Deletion key** |
| 备料类型 | MaterialType | | | 备料类型 | MaterialType | |
| 产品编码 | ProductCode | | | 产品编码 | ProductCode | |
| 产品名称 | ProductName | | | 产品名称 | ProductName | |
| 产品计划数量 | ProductPlanQuantity | decimal | | 产品计划数量 | ProductPlanQuantity | decimal |
| 产品单位 | ProductUnit | | | 产品单位 | ProductUnit | |
| 用料部门 | UseDepartment | | | 用料部门 | UseDepartment | |
| 备注 | Remark | | | 备注 | Remark | |
| 制单人 | Creator | | | 制单人 | Creator | |
| 制单日期 | CreateDate | date | | 制单日期 | CreateDate | date |
| 审批人 | Approver | | | 审批人 | Approver | |
| 审批日期 | ApproveDate | date | | 审批日期 | ApproveDate | date |
| 序号 | SequenceNumber | int | | 序号 | SequenceNumber | int |
| 材料编码 | MaterialCode | | | 材料编码 | MaterialCode | |
| 材料名称 | MaterialName | | | 材料名称 | MaterialName | |
| 规格 | Specification | | | 规格 | Specification | |
| 型号 | Model | | | 型号 | Model | |
| 图号 | DrawingNumber | | | 图号 | DrawingNumber | |
| 物料材质 | MaterialQuality | | | 物料材质 | MaterialQuality | |
| 计划数量 | PlanQuantity | decimal | | 计划数量 | PlanQuantity | decimal |
| 单位 | Unit | | | 单位 | Unit | |
| 需用日期 | RequiredDate | date | | 需用日期 | RequiredDate | date |
| 发料仓库 | Warehouse | | | 发料仓库 | Warehouse | |
| 单位用量 | UnitUsage | decimal | | 单位用量 | UnitUsage | decimal |
| 累计出库数量 | CumulativeOutputQuantity | decimal | | 累计出库数量 | CumulativeOutputQuantity | decimal |
| 打印人 | ❌ SKIP | Not in DB | | 打印人 | ❌ SKIP | Not in DB |
| 打印日期 | ❌ SKIP | Not in DB | | 打印日期 | ❌ SKIP | Not in DB |
| - | BOMVersion | SKIP (no source) | | - | BOMVersion | SKIP (no source) |
## Files to Create/Modify ## Files to Create/Modify
### 1. NEW: `src/main/services/database/data-importer.ts` ### 1. NEW: `src/main/services/database/data-importer.ts`
Main import service with: Main import service with:
- `importFromExcel(filePath)` - Main entry point - `importFromExcel(filePath)` - Main entry point
- `readExcelFile(filePath)` - Parse Excel using ExcelJS - `readExcelFile(filePath)` - Parse Excel using ExcelJS
- Map Excel columns to database fields - Map Excel columns to database fields
- Return records and unique SourceNumbers - Return records and unique SourceNumbers
### 2. MODIFY: `src/main/services/database/discrete-material-plan-dao.ts` ### 2. MODIFY: `src/main/services/database/discrete-material-plan-dao.ts`
Add methods: Add methods:
- `deleteBySourceNumbers(sourceNumbers: string[])` - Batch delete - `deleteBySourceNumbers(sourceNumbers: string[])` - Batch delete
- `batchInsert(records: MaterialPlanRecord[], batchSize: number)` - Batch insert - `batchInsert(records: MaterialPlanRecord[], batchSize: number)` - Batch insert
### 3. MODIFY: `src/main/services/erp/extractor.ts` ### 3. MODIFY: `src/main/services/erp/extractor.ts`
- After successful merge, call `importToDatabase(mergedFile)` - After successful merge, call `importToDatabase(mergedFile)`
- Add import results to `ExtractorResult` - Add import results to `ExtractorResult`
### 4. MODIFY: `src/main/types/extractor.types.ts` ### 4. MODIFY: `src/main/types/extractor.types.ts`
Add types: Add types:
```typescript ```typescript
export interface ImportResult { export interface ImportResult {
success: boolean success: boolean
@@ -102,6 +111,7 @@ export interface ExtractorResult {
``` ```
### 5. MODIFY: `src/renderer/src/pages/ExtractorPage.tsx` ### 5. MODIFY: `src/renderer/src/pages/ExtractorPage.tsx`
- Display import results - Display import results
- Show records deleted/imported counts - Show records deleted/imported counts
@@ -114,6 +124,7 @@ export interface ExtractorResult {
5. Update UI 5. Update UI
## Testing Plan ## Testing Plan
1. Unit test DAO methods 1. Unit test DAO methods
2. Integration test with sample Excel file 2. Integration test with sample Excel file
3. E2E test extraction → import flow 3. E2E test extraction → import flow

View File

@@ -4,7 +4,7 @@
* This script modifies the ID column to be AUTO_INCREMENT while preserving data * This script modifies the ID column to be AUTO_INCREMENT while preserving data
*/ */
const mysql = require('mysql2/promise'); const mysql = require('mysql2/promise')
async function main() { async function main() {
const config = { const config = {
@@ -13,17 +13,17 @@ async function main() {
user: 'remote_user', user: 'remote_user',
password: '3.1415926Beeke', password: '3.1415926Beeke',
database: 'BLD_DB' database: 'BLD_DB'
}; }
let connection; let connection
try { try {
console.log('Connecting to MySQL...'); console.log('Connecting to MySQL...')
connection = await mysql.createConnection(config); connection = await mysql.createConnection(config)
console.log('Connected successfully!\n'); console.log('Connected successfully!\n')
// Step 1: Check current table structure // Step 1: Check current table structure
console.log('=== Step 1: Current table structure ==='); console.log('=== Step 1: Current table structure ===')
const [columns] = await connection.execute(` const [columns] = await connection.execute(`
SELECT SELECT
COLUMN_NAME, COLUMN_NAME,
@@ -39,40 +39,38 @@ async function main() {
AND TABLE_SCHEMA = DATABASE() AND TABLE_SCHEMA = DATABASE()
ORDER BY ORDER BY
ORDINAL_POSITION ORDINAL_POSITION
`); `)
console.table(columns); console.table(columns)
// Step 2: Count records before modification // Step 2: Count records before modification
console.log('\n=== Step 2: Record count before modification ==='); console.log('\n=== Step 2: Record count before modification ===')
const [countBefore] = await connection.execute( const [countBefore] = await connection.execute(
'SELECT COUNT(*) AS total FROM dbo_MaterialsTypeToBeDeleted' 'SELECT COUNT(*) AS total FROM dbo_MaterialsTypeToBeDeleted'
); )
console.log(`Total records: ${countBefore[0].total}`); console.log(`Total records: ${countBefore[0].total}`)
// Step 3: Show sample data // Step 3: Show sample data
console.log('\n=== Step 3: Sample data ==='); console.log('\n=== Step 3: Sample data ===')
const [sample] = await connection.execute( const [sample] = await connection.execute('SELECT * FROM dbo_MaterialsTypeToBeDeleted LIMIT 5')
'SELECT * FROM dbo_MaterialsTypeToBeDeleted LIMIT 5' console.table(sample)
);
console.table(sample);
// Step 4: Check if ID is already AUTO_INCREMENT // Step 4: Check if ID is already AUTO_INCREMENT
const idColumn = columns.find((col) => col.COLUMN_NAME === 'ID'); const idColumn = columns.find((col) => col.COLUMN_NAME === 'ID')
if (idColumn && idColumn.EXTRA.includes('auto_increment')) { if (idColumn && idColumn.EXTRA.includes('auto_increment')) {
console.log('\n=== ID is already AUTO_INCREMENT! No modification needed. ==='); console.log('\n=== ID is already AUTO_INCREMENT! No modification needed. ===')
return; return
} }
// Step 5: Modify the ID column // Step 5: Modify the ID column
console.log('\n=== Step 4: Modifying ID column to AUTO_INCREMENT ==='); console.log('\n=== Step 4: Modifying ID column to AUTO_INCREMENT ===')
await connection.execute(` await connection.execute(`
ALTER TABLE dbo_MaterialsTypeToBeDeleted ALTER TABLE dbo_MaterialsTypeToBeDeleted
MODIFY COLUMN ID INT NOT NULL AUTO_INCREMENT MODIFY COLUMN ID INT NOT NULL AUTO_INCREMENT
`); `)
console.log('Modification completed successfully!\n'); console.log('Modification completed successfully!\n')
// Step 6: Verify the change // Step 6: Verify the change
console.log('=== Step 5: Verify modification ==='); console.log('=== Step 5: Verify modification ===')
const [columnsAfter] = await connection.execute(` const [columnsAfter] = await connection.execute(`
SELECT SELECT
COLUMN_NAME, COLUMN_NAME,
@@ -86,32 +84,32 @@ async function main() {
TABLE_NAME = 'dbo_MaterialsTypeToBeDeleted' TABLE_NAME = 'dbo_MaterialsTypeToBeDeleted'
AND TABLE_SCHEMA = DATABASE() AND TABLE_SCHEMA = DATABASE()
AND COLUMN_NAME = 'ID' AND COLUMN_NAME = 'ID'
`); `)
console.table(columnsAfter); console.table(columnsAfter)
// Step 7: Verify data is still intact // Step 7: Verify data is still intact
console.log('\n=== Step 6: Verify data integrity ==='); console.log('\n=== Step 6: Verify data integrity ===')
const [countAfter] = await connection.execute( const [countAfter] = await connection.execute(
'SELECT COUNT(*) AS total FROM dbo_MaterialsTypeToBeDeleted' 'SELECT COUNT(*) AS total FROM dbo_MaterialsTypeToBeDeleted'
); )
console.log(`Total records after modification: ${countAfter[0].total}`); console.log(`Total records after modification: ${countAfter[0].total}`)
if (countBefore[0].total === countAfter[0].total) { if (countBefore[0].total === countAfter[0].total) {
console.log('\n✅ SUCCESS: All data preserved, AUTO_INCREMENT added to ID column!'); console.log('\n✅ SUCCESS: All data preserved, AUTO_INCREMENT added to ID column!')
} else { } else {
console.log('\n⚠ WARNING: Record count changed! Please check data.'); console.log('\n⚠ WARNING: Record count changed! Please check data.')
} }
} catch (error) { } catch (error) {
console.error('\n❌ Error:', error.message); console.error('\n❌ Error:', error.message)
if (error.code) { if (error.code) {
console.error('Error code:', error.code); console.error('Error code:', error.code)
} }
} finally { } finally {
if (connection) { if (connection) {
await connection.end(); await connection.end()
console.log('\nConnection closed.'); console.log('\nConnection closed.')
} }
} }
} }
main(); main()

View File

@@ -19,34 +19,34 @@ const log = createLogger('DataImportService')
* Excel column header to database field mapping * Excel column header to database field mapping
*/ */
const EXCEL_TO_DB_MAPPING: Record<string, keyof MaterialPlanRecord> = { const EXCEL_TO_DB_MAPPING: Record<string, keyof MaterialPlanRecord> = {
'工厂': 'factory', : 'factory',
'备料状态': 'materialStatus', : 'materialStatus',
'备料计划单号': 'planNumber', : 'planNumber',
'来源单号': 'sourceNumber', : 'sourceNumber',
'备料类型': 'materialType', : 'materialType',
'产品编码': 'productCode', : 'productCode',
'产品名称': 'productName', : 'productName',
'产品计划数量': 'productPlanQuantity', : 'productPlanQuantity',
'产品单位': 'productUnit', : 'productUnit',
'用料部门': 'useDepartment', : 'useDepartment',
'备注': 'remark', : 'remark',
'制单人': 'creator', : 'creator',
'制单日期': 'createDate', : 'createDate',
'审批人': 'approver', : 'approver',
'审批日期': 'approveDate', : 'approveDate',
'序号': 'sequenceNumber', : 'sequenceNumber',
'材料编码': 'materialCode', : 'materialCode',
'材料名称': 'materialName', : 'materialName',
'规格': 'specification', : 'specification',
'型号': 'model', : 'model',
'图号': 'drawingNumber', : 'drawingNumber',
'物料材质': 'materialQuality', : 'materialQuality',
'计划数量': 'planQuantity', : 'planQuantity',
'单位': 'unit', : 'unit',
'需用日期': 'requiredDate', : 'requiredDate',
'发料仓库': 'warehouse', : 'warehouse',
'单位用量': 'unitUsage', : 'unitUsage',
'累计出库数量': 'cumulativeOutputQuantity' : 'cumulativeOutputQuantity'
// Note: '打印人', '打印日期' are skipped (not in DB) // Note: '打印人', '打印日期' are skipped (not in DB)
// Note: 'BOMVersion' is skipped (not in Excel) // Note: 'BOMVersion' is skipped (not in Excel)
} }
@@ -275,11 +275,7 @@ export class DataImportService {
} }
// Handle date fields // Handle date fields
const dateFields: (keyof MaterialPlanRecord)[] = [ const dateFields: (keyof MaterialPlanRecord)[] = ['createDate', 'approveDate', 'requiredDate']
'createDate',
'approveDate',
'requiredDate'
]
if (dateFields.includes(fieldName)) { if (dateFields.includes(fieldName)) {
// ExcelJS returns date as Date object if recognized // ExcelJS returns date as Date object if recognized
@@ -301,4 +297,4 @@ export class DataImportService {
*/ */
export function createDataImportService(): DataImportService { export function createDataImportService(): DataImportService {
return new DataImportService() return new DataImportService()
} }

View File

@@ -201,4 +201,4 @@ export class ExtractorCore {
} }
return batches return batches
} }
} }

View File

@@ -227,7 +227,10 @@ const ExtractorPage: React.FC = () => {
{result?.importResult && ( {result?.importResult && (
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 flex flex-col gap-4"> <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 flex flex-col gap-4">
<h3 className="font-semibold text-lg border-b pb-2 flex items-center gap-2"> <h3 className="font-semibold text-lg border-b pb-2 flex items-center gap-2">
<Database size={20} className={result.importResult.success ? 'text-emerald-600' : 'text-red-500'} /> <Database
size={20}
className={result.importResult.success ? 'text-emerald-600' : 'text-red-500'}
/>
<span className={result.importResult.success ? 'text-emerald-600' : 'text-red-500'}> <span className={result.importResult.success ? 'text-emerald-600' : 'text-red-500'}>
</span> </span>

View File

@@ -116,4 +116,4 @@ describe('DataImportService', () => {
expect(result.uniqueSourceNumbers).toBeGreaterThanOrEqual(0) expect(result.uniqueSourceNumbers).toBeGreaterThanOrEqual(0)
}) })
}) })
}) })