Files
BIPMaterialManager/src/main/services/database/mysql.ts
Misaka 5760b56f70 feat: implement IPC handlers, database services and Extractor UI
- Add SqlServerService and MySqlService for database persistence
- Implement IPC handlers for file, extractor, cleaner, and database operations
- Define IPC API types and update preload script
- Create ExtractorPage UI with OrderNumberInput component
- Add unit and integration tests for MySQL and SQL Server
- Update vitest config with path aliases
2026-03-01 15:46:01 +08:00

124 lines
2.9 KiB
TypeScript

import mysql from 'mysql2/promise'
export interface MySqlConfig {
host: string
port: number
user: string
password: string
database: string
}
export interface MySqlQueryResult {
rows: Record<string, unknown>[]
columns: string[]
rowCount: number
}
export class MySqlService {
private connection: mysql.Connection | null = null
private config: MySqlConfig
constructor(config: MySqlConfig) {
this.config = config
}
/**
* Connect to MySQL database
*/
async connect(): Promise<void> {
if (this.connection) {
throw new Error('Already connected to MySQL')
}
try {
this.connection = await mysql.createConnection({
host: this.config.host,
port: this.config.port,
user: this.config.user,
password: this.config.password,
database: this.config.database
})
// Test connection
await this.connection.ping()
} catch (error) {
throw new Error(`Failed to connect to MySQL: ${(error as Error).message}`)
}
}
/**
* Disconnect from MySQL database
*/
async disconnect(): Promise<void> {
if (!this.connection) {
return
}
try {
await this.connection.end()
this.connection = null
} catch (error) {
throw new Error(`Failed to disconnect from MySQL: ${(error as Error).message}`)
}
}
/**
* Check if connected to MySQL database
*/
isConnected(): boolean {
return this.connection !== null
}
/**
* Execute a query and return results
*/
async query(sql: string, params?: any[]): Promise<MySqlQueryResult> {
if (!this.connection) {
throw new Error('Not connected to MySQL. Call connect() first.')
}
try {
const [rows, fields] = await this.connection.execute(sql, params)
// Convert rows to plain objects and extract column names
const columns = fields.map((field) => field.name)
const rowCount = Array.isArray(rows) ? rows.length : 0
// Type assertion for rows - mysql2 returns different types based on query
const typedRows = Array.isArray(rows) ? (rows as Record<string, unknown>[]) : []
return {
rows: typedRows,
columns,
rowCount
}
} catch (error) {
throw new Error(`MySQL query failed: ${(error as Error).message}`)
}
}
/**
* Execute multiple queries in a transaction
*/
async transaction(queries: { sql: string; params?: any[] }[]): Promise<void> {
if (!this.connection) {
throw new Error('Not connected to MySQL. Call connect() first.')
}
try {
await this.connection.beginTransaction()
for (const { sql, params } of queries) {
await this.connection.execute(sql, params)
}
await this.connection.commit()
} catch (error) {
if (this.connection) {
await this.connection.rollback()
}
throw new Error(`MySQL transaction failed: ${(error as Error).message}`)
}
}
}