refactor: abstract database layer to support MySQL and SQL Server
- Add IDatabaseService interface with unified query(transaction) methods - Create DatabaseFactory for centralized database service creation - Modify MySqlService and SqlServerService to implement IDatabaseService - Unify SqlServerService.query() to accept array params (internally converts to @p0, @p1...) - Refactor OrderNumberResolver to use IDatabaseService - Refactor DiscreteMaterialPlanDAO to use DatabaseFactory - Refactor MaterialsToBeDeletedDAO to use DatabaseFactory - Update IPC handlers to use DatabaseFactory.create() - Add database.types.ts with shared type definitions This enables switching between MySQL and SQL Server via DB_TYPE env variable. Co-Authored-By: Claude (glm-5) <noreply@anthropic.com>
This commit is contained in:
@@ -214,7 +214,18 @@ export function registerDatabaseHandlers(): void {
|
||||
}
|
||||
|
||||
log.debug('Executing SQL Server query', { windowId, sql: sqlString.substring(0, 100) })
|
||||
return await service.query(sqlString, params)
|
||||
|
||||
// Use queryWithParams for named parameters, or query for no params
|
||||
if (params && Object.keys(params).length > 0) {
|
||||
// Convert to the format expected by queryWithParams
|
||||
const typedParams: Record<string, { value: unknown }> = {}
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
typedParams[key] = { value }
|
||||
}
|
||||
return await service.queryWithParams(sqlString, typedParams)
|
||||
} else {
|
||||
return await service.query(sqlString)
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'SQL Server query failed'
|
||||
log.error('SQL Server query failed', { error: message })
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ipcMain } from 'electron'
|
||||
import { ErpAuthService } from '../services/erp/erp-auth'
|
||||
import { ExtractorService } from '../services/erp/extractor'
|
||||
import { OrderNumberResolver } from '../services/erp/order-resolver'
|
||||
import { MySqlService } from '../services/database/mysql'
|
||||
import { create, type IDatabaseService } from '../services/database'
|
||||
import { createLogger } from '../services/logger'
|
||||
import { withErrorHandling, type IpcResult } from './index'
|
||||
import { ErpConnectionError, ValidationError, DatabaseQueryError } from '../types/errors'
|
||||
@@ -19,7 +19,7 @@ export function registerExtractorHandlers(): void {
|
||||
async (_event, input: ExtractorInput): Promise<IpcResult<ExtractorResult>> => {
|
||||
return withErrorHandling(async () => {
|
||||
let authService: ErpAuthService | null = null
|
||||
let mysqlService: MySqlService | null = null
|
||||
let dbService: IDatabaseService | null = null
|
||||
|
||||
try {
|
||||
// Check environment variables
|
||||
@@ -39,29 +39,20 @@ export function registerExtractorHandlers(): void {
|
||||
)
|
||||
}
|
||||
|
||||
// Resolve order numbers (convert productionIDs to 生产订单号)
|
||||
const mysqlConfig = {
|
||||
host: process.env.DB_MYSQL_HOST || 'localhost',
|
||||
port: parseInt(process.env.DB_MYSQL_PORT || '3306', 10),
|
||||
user: process.env.DB_USERNAME || 'root',
|
||||
password: process.env.DB_PASSWORD || '',
|
||||
database: process.env.DB_NAME || ''
|
||||
}
|
||||
|
||||
log.info('Connecting to MySQL for order resolution...')
|
||||
mysqlService = new MySqlService(mysqlConfig)
|
||||
|
||||
// Create database service using factory
|
||||
log.info('Connecting to database for order resolution...')
|
||||
try {
|
||||
await mysqlService.connect()
|
||||
dbService = await create()
|
||||
} catch (error) {
|
||||
throw new DatabaseQueryError(
|
||||
'MySQL 连接失败',
|
||||
'数据库连接失败',
|
||||
'DB_CONNECTION_FAILED',
|
||||
error instanceof Error ? error : undefined
|
||||
)
|
||||
}
|
||||
|
||||
const resolver = new OrderNumberResolver(mysqlService)
|
||||
// Resolve order numbers (convert productionIDs to 生产订单号)
|
||||
const resolver = new OrderNumberResolver(dbService)
|
||||
const mappings = await resolver.resolve(input.orderNumbers)
|
||||
|
||||
// Get valid order numbers and warnings
|
||||
@@ -136,13 +127,13 @@ export function registerExtractorHandlers(): void {
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up: disconnect MySQL
|
||||
if (mysqlService) {
|
||||
// Clean up: disconnect database
|
||||
if (dbService) {
|
||||
try {
|
||||
await mysqlService.disconnect()
|
||||
log.debug('MySQL disconnected')
|
||||
await dbService.disconnect()
|
||||
log.debug('Database disconnected')
|
||||
} catch (closeError) {
|
||||
log.warn('Error disconnecting MySQL', {
|
||||
log.warn('Error disconnecting database', {
|
||||
error: closeError instanceof Error ? closeError.message : String(closeError)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -7,10 +7,9 @@
|
||||
*/
|
||||
|
||||
import { ipcMain } from 'electron'
|
||||
import { MySqlService } from '../services/database/mysql'
|
||||
import { create, type IDatabaseService } from '../services/database'
|
||||
import { OrderNumberResolver } from '../services/erp/order-resolver'
|
||||
import { createLogger } from '../services/logger'
|
||||
import { DatabaseQueryError } from '../types/errors'
|
||||
import type { OrderMapping, ResolutionStats } from '../services/erp/order-resolver'
|
||||
|
||||
const log = createLogger('ResolverHandler')
|
||||
@@ -21,14 +20,6 @@ const log = createLogger('ResolverHandler')
|
||||
export interface ResolverInput {
|
||||
/** List of order numbers/productionIDs to resolve */
|
||||
inputs: string[]
|
||||
/** MySQL configuration (optional, uses default if not provided) */
|
||||
mysqlConfig?: {
|
||||
host: string
|
||||
port: number
|
||||
user: string
|
||||
password: string
|
||||
database: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,25 +51,15 @@ export function registerResolverHandlers(): void {
|
||||
ipcMain.handle(
|
||||
'resolver:resolve',
|
||||
async (_event, input: ResolverInput): Promise<ResolverResponse> => {
|
||||
let mysqlService: MySqlService | null = null
|
||||
let dbService: IDatabaseService | null = null
|
||||
|
||||
try {
|
||||
// Use provided config or environment variables
|
||||
const mysqlConfig = input.mysqlConfig || {
|
||||
host: process.env.DB_MYSQL_HOST || 'localhost',
|
||||
port: parseInt(process.env.DB_MYSQL_PORT || '3306', 10),
|
||||
user: process.env.DB_USERNAME || 'root',
|
||||
password: process.env.DB_PASSWORD || '',
|
||||
database: process.env.DB_NAME || ''
|
||||
}
|
||||
|
||||
// Create MySQL service
|
||||
log.info('Connecting to MySQL for resolution', { inputCount: input.inputs.length })
|
||||
mysqlService = new MySqlService(mysqlConfig)
|
||||
await mysqlService.connect()
|
||||
// Create database service using factory
|
||||
log.info('Connecting to database for resolution', { inputCount: input.inputs.length })
|
||||
dbService = await create()
|
||||
|
||||
// Create resolver and resolve inputs
|
||||
const resolver = new OrderNumberResolver(mysqlService)
|
||||
const resolver = new OrderNumberResolver(dbService)
|
||||
const mappings = await resolver.resolve(input.inputs)
|
||||
|
||||
// Get valid order numbers and warnings
|
||||
@@ -107,13 +88,13 @@ export function registerResolverHandlers(): void {
|
||||
error: `解析失败:${message}`
|
||||
}
|
||||
} finally {
|
||||
// Clean up MySQL connection
|
||||
if (mysqlService) {
|
||||
// Clean up database connection
|
||||
if (dbService) {
|
||||
try {
|
||||
await mysqlService.disconnect()
|
||||
log.debug('MySQL disconnected')
|
||||
await dbService.disconnect()
|
||||
log.debug('Database disconnected')
|
||||
} catch (closeError) {
|
||||
log.warn('Error disconnecting MySQL', {
|
||||
log.warn('Error disconnecting database', {
|
||||
error: closeError instanceof Error ? closeError.message : String(closeError)
|
||||
})
|
||||
}
|
||||
@@ -136,9 +117,11 @@ export function registerResolverHandlers(): void {
|
||||
error?: string
|
||||
}> => {
|
||||
try {
|
||||
// Create a mock resolver without database connection
|
||||
const resolver = new OrderNumberResolver({
|
||||
isConnected: () => false
|
||||
} as MySqlService)
|
||||
isConnected: () => false,
|
||||
type: 'mysql'
|
||||
} as IDatabaseService)
|
||||
|
||||
const results = inputs.map((input) => ({
|
||||
input,
|
||||
|
||||
Reference in New Issue
Block a user