feat: add SQL Server support and refactor database layer for multi-database compatibility

- Add SqlServerService integration alongside existing MySQL support
- Refactor DAOs (DiscreteMaterialPlanDAO, MaterialsToBeDeletedDAO, BipUsersDAO) to support both MySQL and SQL Server
- Update validation handler to dynamically select database service based on DB_TYPE environment variable
- Add connection pooling and proper connection management for SQL Server
- Update package-lock.json dependency peer flags

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Misaka_Company
2026-03-02 11:10:59 +08:00
parent bbdd1e18f8
commit 05d856309f
6 changed files with 1207 additions and 473 deletions

33
package-lock.json generated
View File

@@ -99,6 +99,7 @@
"resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.1.tgz",
"integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==",
"license": "MIT",
"peer": true,
"dependencies": {
"@azure/abort-controller": "^2.1.2",
"@azure/core-auth": "^1.10.0",
@@ -160,6 +161,7 @@
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.2.tgz",
"integrity": "sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@azure/abort-controller": "^2.1.2",
"@azure/core-auth": "^1.10.0",
@@ -351,6 +353,7 @@
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@@ -1099,7 +1102,6 @@
"dev": true,
"license": "BSD-2-Clause",
"optional": true,
"peer": true,
"dependencies": {
"cross-dirname": "^0.1.0",
"debug": "^4.3.4",
@@ -1121,7 +1123,6 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
@@ -1138,7 +1139,6 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"universalify": "^2.0.0"
},
@@ -1153,7 +1153,6 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">= 10.0.0"
}
@@ -3008,6 +3007,7 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.13.tgz",
"integrity": "sha512-akNQMv0wW5uyRpD2v2IEyRSZiR+BeGuoB6L310EgGObO44HSMNT8z1xzio28V8qOrgYaopIDNA18YgdXd+qTiw==",
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~6.21.0"
}
@@ -3030,6 +3030,7 @@
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -3132,6 +3133,7 @@
"integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.56.1",
"@typescript-eslint/types": "8.56.1",
@@ -3565,6 +3567,7 @@
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -3597,6 +3600,7 @@
"integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -4329,6 +4333,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -4984,8 +4989,7 @@
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
"optional": true
},
"node_modules/cross-spawn": {
"version": "7.0.6",
@@ -5334,6 +5338,7 @@
"integrity": "sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"app-builder-lib": "26.8.1",
"builder-util": "26.8.1",
@@ -5562,6 +5567,7 @@
"integrity": "sha512-Rz5QvP1pTqoU1DPRrG3EeX2oWBtS3uRmd6Z/wzZsb2e/iIUsrT+XcBaAhFr4FW48gDc8uP2wYVyY5Aamha/5Zg==",
"hasInstallScript": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@electron/get": "^2.0.0",
"@types/node": "^22.7.7",
@@ -5750,7 +5756,6 @@
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@electron/asar": "^3.2.1",
"debug": "^4.1.1",
@@ -5771,7 +5776,6 @@
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"graceful-fs": "^4.1.2",
"jsonfile": "^4.0.0",
@@ -6099,6 +6103,7 @@
"integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -6159,6 +6164,7 @@
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"eslint-config-prettier": "bin/cli.js"
},
@@ -9870,6 +9876,7 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -9951,6 +9958,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -9974,7 +9982,6 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"commander": "^9.4.0"
},
@@ -9992,7 +9999,6 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": "^12.20.0 || >=14"
}
@@ -10013,6 +10019,7 @@
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
@@ -10145,6 +10152,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -11380,7 +11388,6 @@
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"mkdirp": "^0.5.1",
"rimraf": "~2.6.2"
@@ -11668,6 +11675,7 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -11902,6 +11910,7 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.27.0",
"fdir": "^6.5.0",
@@ -12448,6 +12457,7 @@
"integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@vitest/expect": "4.0.18",
"@vitest/mocker": "4.0.18",
@@ -12845,6 +12855,7 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}

View File

@@ -9,6 +9,7 @@
import { ipcMain } from 'electron'
import { MySqlService } from '../services/database/mysql'
import { SqlServerService } from '../services/database/sql-server'
import { MaterialsToBeDeletedDAO } from '../services/database/materials-to-be-deleted-dao'
import { DiscreteMaterialPlanDAO } from '../services/database/discrete-material-plan-dao'
import type {
@@ -49,9 +50,26 @@ export function clearSharedProductionIds(): void {
}
/**
* Get MySQL service for validation operations
* Get database service for validation operations (MySQL or SQL Server)
*/
async function getValidationMySqlService(): Promise<MySqlService> {
async function getValidationDatabaseService(): Promise<MySqlService | SqlServerService> {
const dbType = process.env.DB_TYPE?.toLowerCase()
if (dbType === 'sqlserver' || dbType === 'mssql') {
const sqlServerService = new SqlServerService({
server: process.env.DB_SERVER || 'localhost',
port: parseInt(process.env.DB_SQLSERVER_PORT || '1433', 10),
user: process.env.DB_USERNAME || 'sa',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME || '',
options: {
encrypt: process.env.DB_TRUST_SERVER_CERTIFICATE === 'yes',
trustServerCertificate: process.env.DB_TRUST_SERVER_CERTIFICATE === 'yes'
}
})
await sqlServerService.connect()
return sqlServerService
} else {
const mysqlService = new MySqlService({
host: process.env.DB_MYSQL_HOST || 'localhost',
port: parseInt(process.env.DB_MYSQL_PORT || '3306', 10),
@@ -59,10 +77,27 @@ async function getValidationMySqlService(): Promise<MySqlService> {
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME || ''
})
await mysqlService.connect()
return mysqlService
}
}
/**
* Get table name based on database type
*/
function getTableName(mysqlTableName: string): string {
const dbType = process.env.DB_TYPE?.toLowerCase()
if (dbType === 'sqlserver' || dbType === 'mssql') {
// Convert MySQL table name to SQL Server format
// e.g., dbo_MaterialsToBeDeleted -> [dbo].[MaterialsToBeDeleted]
if (mysqlTableName.startsWith('dbo_')) {
const tableName = mysqlTableName.substring(4)
return `[dbo].[${tableName}]`
}
return `[dbo].[${mysqlTableName}]`
}
return mysqlTableName
}
/**
* Read Production IDs from file
@@ -96,10 +131,12 @@ function identifyInputType(input: string): 'production_id' | 'order_number' | 'u
*/
async function getSourceNumbersFromInputs(
inputs: string[],
mysqlService: MySqlService
dbService: MySqlService | SqlServerService
): Promise<string[]> {
const productionIds: string[] = []
const orderNumbers: string[] = []
const dbType = process.env.DB_TYPE?.toLowerCase()
const isSqlServer = dbType === 'sqlserver' || dbType === 'mssql'
for (const item of inputs) {
const type = identifyInputType(item)
@@ -114,18 +151,39 @@ async function getSourceNumbersFromInputs(
// Table name in MySQL: productionContractData_26年压力表合同数据
// Column name: 生产订单号 (SourceNumber)
if (productionIds.length > 0) {
if (isSqlServer) {
const sql = require('mssql')
const placeholders = productionIds.map((_, idx) => `@p${idx}`).join(',')
const params: Record<string, { value: string; type: any }> = {}
productionIds.forEach((id, idx) => {
params[`p${idx}`] = { value: id, type: sql.NVarChar }
})
const contractSql = `
SELECT DISTINCT 生产订单号
FROM productionContractData_26年压力表合同数据
WHERE 总排号 IN (${placeholders})
`
const contractResult = await (dbService as SqlServerService).queryWithParams(contractSql, params)
const dbOrderNumbers = contractResult.rows.map(
row => row. as string
)
orderNumbers.push(...dbOrderNumbers)
} else {
const placeholders = productionIds.map(() => '?').join(',')
const contractSql = `
SELECT DISTINCT 生产订单号
FROM productionContractData_26年压力表合同数据
WHERE 总排号 IN (${placeholders})
`
const contractResult = await mysqlService.query(contractSql, productionIds)
const contractResult = await (dbService as MySqlService).query(contractSql, productionIds)
const dbOrderNumbers = contractResult.rows.map(
row => row. as string
)
orderNumbers.push(...dbOrderNumbers)
}
}
// Deduplicate
return [...new Set(orderNumbers)]
@@ -146,13 +204,15 @@ export function registerValidationHandlers(): void {
_event,
request: ValidationRequest
): Promise<ValidationResponse> => {
let mysqlService: MySqlService | null = null
let dbService: MySqlService | SqlServerService | null = null
try {
console.log('[Validation] Starting validation:', request)
// Connect to MySQL
mysqlService = await getValidationMySqlService()
// Connect to database
dbService = await getValidationDatabaseService()
const dbType = process.env.DB_TYPE?.toLowerCase()
const isSqlServer = dbType === 'sqlserver' || dbType === 'mssql'
let sourceNumbers: string[] | null = null
@@ -175,7 +235,7 @@ export function registerValidationHandlers(): void {
}
}
sourceNumbers = await getSourceNumbersFromInputs(sharedIds, mysqlService)
sourceNumbers = await getSourceNumbersFromInputs(sharedIds, dbService)
console.log(
`[Validation] Got ${sourceNumbers.length} source numbers from shared Production IDs`
)
@@ -185,7 +245,7 @@ export function registerValidationHandlers(): void {
console.log(
`[Validation] Read ${inputs.length} inputs from file`
)
sourceNumbers = await getSourceNumbersFromInputs(inputs, mysqlService)
sourceNumbers = await getSourceNumbersFromInputs(inputs, dbService)
console.log(
`[Validation] Got ${sourceNumbers.length} source numbers`
)
@@ -194,8 +254,6 @@ export function registerValidationHandlers(): void {
// Get material records from DiscreteMaterialPlanData
const materialDao = new DiscreteMaterialPlanDAO()
// Inject the mysqlService instance
;(materialDao as any).mysqlService = mysqlService
let materialRecords: any[] = []
@@ -222,24 +280,32 @@ export function registerValidationHandlers(): void {
}
// Get type keywords from MaterialsTypeToBeDeleted
const typeKeywordTableName = getTableName('dbo_MaterialsTypeToBeDeleted')
const typeKeywordSql = `
SELECT MaterialName, ManagerName
FROM dbo_MaterialsTypeToBeDeleted
FROM ${typeKeywordTableName}
WHERE MaterialName IS NOT NULL
`
const typeKeywordResult = await mysqlService.query(typeKeywordSql)
const typeKeywordResult = isSqlServer
? await (dbService as SqlServerService).query(typeKeywordSql)
: await (dbService as MySqlService).query(typeKeywordSql)
const typeKeywords = typeKeywordResult.rows.map(row => ({
materialName: row.MaterialName as string,
managerName: row.ManagerName as string
}))
// Get marked material codes from MaterialsToBeDeleted
const markedTableName = getTableName('dbo_MaterialsToBeDeleted')
const markedSql = `
SELECT MaterialCode, ManagerName
FROM dbo_MaterialsToBeDeleted
FROM ${markedTableName}
WHERE MaterialCode IS NOT NULL AND ManagerName IS NOT NULL
`
const markedResult = await mysqlService.query(markedSql)
const markedResult = isSqlServer
? await (dbService as SqlServerService).query(markedSql)
: await (dbService as MySqlService).query(markedSql)
const markedCodesDict = new Map<string, string>()
for (const row of markedResult.rows) {
markedCodesDict.set(
@@ -306,11 +372,11 @@ export function registerValidationHandlers(): void {
error: `Validation failed: ${message}`
}
} finally {
if (mysqlService) {
if (dbService) {
try {
await mysqlService.disconnect()
await dbService.disconnect()
} catch (closeError) {
console.warn('[Validation] Error disconnecting MySQL:', closeError)
console.warn('[Validation] Error disconnecting database:', closeError)
}
}
}
@@ -328,13 +394,8 @@ export function registerValidationHandlers(): void {
_event,
request: MaterialUpsertBatchRequest
): Promise<MaterialOperationResponse> => {
let mysqlService: MySqlService | null = null
try {
mysqlService = await getValidationMySqlService()
const dao = new MaterialsToBeDeletedDAO()
;(dao as any).mysqlService = mysqlService
const stats = await dao.upsertBatch(request.materials)
return {
@@ -348,14 +409,6 @@ export function registerValidationHandlers(): void {
success: false,
error: `Upsert failed: ${message}`
}
} finally {
if (mysqlService) {
try {
await mysqlService.disconnect()
} catch (closeError) {
console.warn('[Materials] Error disconnecting MySQL:', closeError)
}
}
}
}
)
@@ -369,13 +422,8 @@ export function registerValidationHandlers(): void {
_event,
request: MaterialDeleteRequest
): Promise<MaterialOperationResponse> => {
let mysqlService: MySqlService | null = null
try {
mysqlService = await getValidationMySqlService()
const dao = new MaterialsToBeDeletedDAO()
;(dao as any).mysqlService = mysqlService
const count = await dao.deleteByMaterialCodes(request.materialCodes)
return {
@@ -389,14 +437,6 @@ export function registerValidationHandlers(): void {
success: false,
error: `Delete failed: ${message}`
}
} finally {
if (mysqlService) {
try {
await mysqlService.disconnect()
} catch (closeError) {
console.warn('[Materials] Error disconnecting MySQL:', closeError)
}
}
}
}
)
@@ -407,26 +447,13 @@ export function registerValidationHandlers(): void {
ipcMain.handle(
'materials:getManagers',
async (_event): Promise<{ managers: string[] }> => {
let mysqlService: MySqlService | null = null
try {
mysqlService = await getValidationMySqlService()
const dao = new MaterialsToBeDeletedDAO()
;(dao as any).mysqlService = mysqlService
const managers = await dao.getManagers()
return { managers }
} catch (error) {
console.error('[Materials] Get managers error:', error)
return { managers: [] }
} finally {
if (mysqlService) {
try {
await mysqlService.disconnect()
} catch (closeError) {
console.warn('[Materials] Error disconnecting MySQL:', closeError)
}
}
}
}
)
@@ -440,13 +467,14 @@ export function registerValidationHandlers(): void {
_event,
managerName: string
): Promise<{ materials: MaterialRecordSummary[] }> => {
let mysqlService: MySqlService | null = null
let dbService: MySqlService | SqlServerService | null = null
try {
mysqlService = await getValidationMySqlService()
const dao = new MaterialsToBeDeletedDAO()
;(dao as any).mysqlService = mysqlService
dbService = await getValidationDatabaseService()
const dbType = process.env.DB_TYPE?.toLowerCase()
const isSqlServer = dbType === 'sqlserver' || dbType === 'mssql'
const dao = new MaterialsToBeDeletedDAO()
const materials = await dao.getMaterialsByManager(managerName)
// Get material codes set for quick lookup
@@ -454,17 +482,32 @@ export function registerValidationHandlers(): void {
// Enrich with material details from DiscreteMaterialPlanData
const enrichedMaterials: MaterialRecordSummary[] = []
const detailTableName = getTableName('dbo_DiscreteMaterialPlanData')
for (const mat of materials) {
let detailResult: any
if (isSqlServer) {
const sql = require('mssql')
const detailSql = `
SELECT TOP 1 MaterialName, Specification, Model
FROM ${detailTableName}
WHERE MaterialCode = @materialCode
`
detailResult = await (dbService as SqlServerService).queryWithParams(detailSql, {
materialCode: { value: mat.materialCode, type: sql.NVarChar }
})
} else {
const detailSql = `
SELECT MaterialName, Specification, Model
FROM dbo_DiscreteMaterialPlanData
FROM ${detailTableName}
WHERE MaterialCode = ?
LIMIT 1
`
const detailResult = await mysqlService.query(detailSql, [
detailResult = await (dbService as MySqlService).query(detailSql, [
mat.materialCode
])
}
enrichedMaterials.push({
materialCode: mat.materialCode,
@@ -490,11 +533,11 @@ export function registerValidationHandlers(): void {
console.error('[Materials] Get by manager error:', error)
return { materials: [] }
} finally {
if (mysqlService) {
if (dbService) {
try {
await mysqlService.disconnect()
await dbService.disconnect()
} catch (closeError) {
console.warn('[Materials] Error disconnecting MySQL:', closeError)
console.warn('[Materials] Error disconnecting database:', closeError)
}
}
}
@@ -507,28 +550,44 @@ export function registerValidationHandlers(): void {
ipcMain.handle(
'materials:getAll',
async (_event): Promise<{ materials: MaterialRecordSummary[] }> => {
let mysqlService: MySqlService | null = null
let dbService: MySqlService | SqlServerService | null = null
try {
mysqlService = await getValidationMySqlService()
const dao = new MaterialsToBeDeletedDAO()
;(dao as any).mysqlService = mysqlService
dbService = await getValidationDatabaseService()
const dbType = process.env.DB_TYPE?.toLowerCase()
const isSqlServer = dbType === 'sqlserver' || dbType === 'mssql'
const dao = new MaterialsToBeDeletedDAO()
const materials = await dao.getAllRecords()
const markedCodes = await dao.getAllMaterialCodes()
const enrichedMaterials: MaterialRecordSummary[] = []
const detailTableName = getTableName('dbo_DiscreteMaterialPlanData')
for (const mat of materials) {
let detailResult: any
if (isSqlServer) {
const sql = require('mssql')
const detailSql = `
SELECT TOP 1 MaterialName, Specification, Model
FROM ${detailTableName}
WHERE MaterialCode = @materialCode
`
detailResult = await (dbService as SqlServerService).queryWithParams(detailSql, {
materialCode: { value: mat.materialCode, type: sql.NVarChar }
})
} else {
const detailSql = `
SELECT MaterialName, Specification, Model
FROM dbo_DiscreteMaterialPlanData
FROM ${detailTableName}
WHERE MaterialCode = ?
LIMIT 1
`
const detailResult = await mysqlService.query(detailSql, [
detailResult = await (dbService as MySqlService).query(detailSql, [
mat.materialCode
])
}
enrichedMaterials.push({
materialCode: mat.materialCode,
@@ -554,11 +613,11 @@ export function registerValidationHandlers(): void {
console.error('[Materials] Get all error:', error)
return { materials: [] }
} finally {
if (mysqlService) {
if (dbService) {
try {
await mysqlService.disconnect()
await dbService.disconnect()
} catch (closeError) {
console.warn('[Materials] Error disconnecting MySQL:', closeError)
console.warn('[Materials] Error disconnecting database:', closeError)
}
}
}
@@ -571,26 +630,13 @@ export function registerValidationHandlers(): void {
ipcMain.handle(
'materials:getStatistics',
async (_event): Promise<{ stats: any }> => {
let mysqlService: MySqlService | null = null
try {
mysqlService = await getValidationMySqlService()
const dao = new MaterialsToBeDeletedDAO()
;(dao as any).mysqlService = mysqlService
const stats = await dao.getStatistics()
return { stats }
} catch (error) {
console.error('[Materials] Get statistics error:', error)
return { stats: null }
} finally {
if (mysqlService) {
try {
await mysqlService.disconnect()
} catch (closeError) {
console.warn('[Materials] Error disconnecting MySQL:', closeError)
}
}
}
}
)
@@ -628,7 +674,7 @@ export function registerValidationHandlers(): void {
materialCodes?: string[]
error?: string
}> => {
let mysqlService: MySqlService | null = null
let dbService: MySqlService | SqlServerService | null = null
const sessionManager = (await import('../services/user/session-manager')).SessionManager.getInstance()
try {
@@ -643,11 +689,13 @@ export function registerValidationHandlers(): void {
const isAdmin = userInfo.userType === 'Admin'
const username = userInfo.username
const dbType = process.env.DB_TYPE?.toLowerCase()
const isSqlServer = dbType === 'sqlserver' || dbType === 'mssql'
console.log(`[CleanerData] User: ${username}, isAdmin: ${isAdmin}`)
// Connect to MySQL
mysqlService = await getValidationMySqlService()
// Connect to database
dbService = await getValidationDatabaseService()
// 1. Get order numbers from shared Production IDs
const sharedIds = getSharedProductionIds()
@@ -655,36 +703,55 @@ export function registerValidationHandlers(): void {
if (sharedIds.length > 0) {
console.log(`[CleanerData] Using ${sharedIds.length} shared Production IDs`)
orderNumbers = await getSourceNumbersFromInputs(sharedIds, mysqlService)
orderNumbers = await getSourceNumbersFromInputs(sharedIds, dbService)
console.log(`[CleanerData] Got ${orderNumbers.length} order numbers`)
}
// 2. Get material codes from MaterialsToBeDeleted table
let materialCodes: string[] = []
const markedTableName = getTableName('dbo_MaterialsToBeDeleted')
if (isAdmin) {
// Admin sees all materials
const allCodesSql = `
SELECT MaterialCode
FROM dbo_MaterialsToBeDeleted
FROM ${markedTableName}
WHERE MaterialCode IS NOT NULL
`
const result = await mysqlService.query(allCodesSql)
const result = isSqlServer
? await (dbService as SqlServerService).query(allCodesSql)
: await (dbService as MySqlService).query(allCodesSql)
materialCodes = result.rows
.map(row => row.MaterialCode as string)
.filter(Boolean)
console.log(`[CleanerData] Admin user: got ${materialCodes.length} materials`)
} else {
// Regular users only see their own materials
if (isSqlServer) {
const sql = require('mssql')
const userMaterialsSql = `
SELECT MaterialCode
FROM dbo_MaterialsToBeDeleted
WHERE ManagerName = ? AND MaterialCode IS NOT NULL
FROM ${markedTableName}
WHERE ManagerName = @username AND MaterialCode IS NOT NULL
`
const result = await mysqlService.query(userMaterialsSql, [username])
const result = await (dbService as SqlServerService).queryWithParams(userMaterialsSql, {
username: { value: username, type: sql.NVarChar }
})
materialCodes = result.rows
.map(row => row.MaterialCode as string)
.filter(Boolean)
} else {
const userMaterialsSql = `
SELECT MaterialCode
FROM ${markedTableName}
WHERE ManagerName = ? AND MaterialCode IS NOT NULL
`
const result = await (dbService as MySqlService).query(userMaterialsSql, [username])
materialCodes = result.rows
.map(row => row.MaterialCode as string)
.filter(Boolean)
}
console.log(`[CleanerData] Regular user: got ${materialCodes.length} materials`)
}
@@ -701,11 +768,11 @@ export function registerValidationHandlers(): void {
error: `获取清理数据失败:${message}`
}
} finally {
if (mysqlService) {
if (dbService) {
try {
await mysqlService.disconnect()
await dbService.disconnect()
} catch (closeError) {
console.warn('[CleanerData] Error disconnecting MySQL:', closeError)
console.warn('[CleanerData] Error disconnecting database:', closeError)
}
}
}

View File

@@ -9,6 +9,8 @@
*/
import { MySqlService } from './mysql'
import { SqlServerService } from './sql-server'
import sql from 'mssql'
/**
* Material plan record interface
@@ -50,6 +52,7 @@ export interface MaterialPlanRecord {
* Configuration for DiscreteMaterialPlanData table
*/
export const DISCRETE_MATERIAL_PLAN_CONFIG = {
TABLE_NAME_SQLSERVER: '[dbo].[DiscreteMaterialPlanData]',
TABLE_NAME_MYSQL: 'dbo_DiscreteMaterialPlanData',
COLUMNS: {
ID: 'ID',
@@ -90,11 +93,54 @@ export const DISCRETE_MATERIAL_PLAN_CONFIG = {
*/
export class DiscreteMaterialPlanDAO {
private mysqlService: MySqlService | null = null
private sqlServerService: SqlServerService | null = null
private dbType: 'mysql' | 'sqlserver' = 'mysql'
/**
* Get MySQL service instance
* Constructor - determine database type from environment
*/
private async getMySqlService(): Promise<MySqlService> {
constructor() {
const dbType = process.env.DB_TYPE?.toLowerCase()
if (dbType === 'sqlserver' || dbType === 'mssql') {
this.dbType = 'sqlserver'
} else {
this.dbType = 'mysql'
}
}
/**
* Get the appropriate table name based on database type
*/
private getTableName(): string {
return this.dbType === 'sqlserver'
? DISCRETE_MATERIAL_PLAN_CONFIG.TABLE_NAME_SQLSERVER
: DISCRETE_MATERIAL_PLAN_CONFIG.TABLE_NAME_MYSQL
}
/**
* Get database service instance (MySQL or SQL Server)
*/
private async getDatabaseService(): Promise<MySqlService | SqlServerService> {
if (this.dbType === 'sqlserver') {
if (this.sqlServerService && this.sqlServerService.isConnected()) {
return this.sqlServerService
}
this.sqlServerService = new SqlServerService({
server: process.env.DB_SERVER || 'localhost',
port: parseInt(process.env.DB_SQLSERVER_PORT || '1433', 10),
user: process.env.DB_USERNAME || 'sa',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME || '',
options: {
encrypt: process.env.DB_TRUST_SERVER_CERTIFICATE === 'yes',
trustServerCertificate: process.env.DB_TRUST_SERVER_CERTIFICATE === 'yes'
}
})
await this.sqlServerService.connect()
return this.sqlServerService
} else {
if (this.mysqlService && this.mysqlService.isConnected()) {
return this.mysqlService
}
@@ -110,6 +156,7 @@ export class DiscreteMaterialPlanDAO {
await this.mysqlService.connect()
return this.mysqlService
}
}
// ==================== QUERY ALL ====================
@@ -119,11 +166,15 @@ export class DiscreteMaterialPlanDAO {
*/
async queryAll(): Promise<any[]> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const sql = `SELECT * FROM ${DISCRETE_MATERIAL_PLAN_CONFIG.TABLE_NAME_MYSQL}`
const sql = `SELECT * FROM ${tableName}`
const result = this.dbType === 'sqlserver'
? await (dbService as SqlServerService).query(sql)
: await (dbService as MySqlService).query(sql)
const result = await mysqlService.query(sql)
return result.rows
} catch (error) {
console.error('[DiscreteMaterialPlanDAO] Query all error:', error)
@@ -139,7 +190,8 @@ export class DiscreteMaterialPlanDAO {
*/
async queryAllDistinctByMaterialCode(): Promise<any[]> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const sql = `
WITH RankedRecords AS (
@@ -149,7 +201,7 @@ export class DiscreteMaterialPlanDAO {
PARTITION BY MaterialCode
ORDER BY CreateDate ASC, SequenceNumber ASC
) AS rn
FROM ${DISCRETE_MATERIAL_PLAN_CONFIG.TABLE_NAME_MYSQL}
FROM ${tableName}
WHERE MaterialCode IS NOT NULL
)
SELECT
@@ -163,7 +215,10 @@ export class DiscreteMaterialPlanDAO {
WHERE rn = 1
`
const result = await mysqlService.query(sql)
const result = this.dbType === 'sqlserver'
? await (dbService as SqlServerService).query(sql)
: await (dbService as MySqlService).query(sql)
return result.rows
} catch (error) {
console.error('[DiscreteMaterialPlanDAO] Query all distinct by material code error:', error)
@@ -184,23 +239,43 @@ export class DiscreteMaterialPlanDAO {
}
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const batchSize = 2000
const allResults: any[] = []
for (let i = 0; i < sourceNumbers.length; i += batchSize) {
const batch = sourceNumbers.slice(i, i + batchSize)
if (this.dbType === 'sqlserver') {
const placeholders = batch.map((_, idx) => `@p${idx}`).join(',')
const params: Record<string, { value: string; type: sql.ISqlType }> = {}
batch.forEach((num, idx) => {
params[`p${idx}`] = { value: num, type: sql.NVarChar }
})
const sql = `
SELECT *
FROM ${tableName}
WHERE SourceNumber IN (${placeholders})
`
const result = await (dbService as SqlServerService).queryWithParams(sql, params)
allResults.push(...result.rows)
} else {
const placeholders = batch.map(() => '?').join(',')
const sql = `
SELECT *
FROM ${DISCRETE_MATERIAL_PLAN_CONFIG.TABLE_NAME_MYSQL}
FROM ${tableName}
WHERE SourceNumber IN (${placeholders})
`
const result = await mysqlService.query(sql, batch)
const result = await (dbService as MySqlService).query(sql, batch)
allResults.push(...result.rows)
}
}
return allResults
} catch (error) {
@@ -222,13 +297,21 @@ export class DiscreteMaterialPlanDAO {
}
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const batchSize = 2000
const allResults: any[] = []
for (let i = 0; i < sourceNumbers.length; i += batchSize) {
const batch = sourceNumbers.slice(i, i + batchSize)
const placeholders = batch.map(() => '?').join(',')
if (this.dbType === 'sqlserver') {
const placeholders = batch.map((_, idx) => `@p${idx}`).join(',')
const params: Record<string, { value: string; type: sql.ISqlType }> = {}
batch.forEach((num, idx) => {
params[`p${idx}`] = { value: num, type: sql.NVarChar }
})
const sql = `
WITH RankedRecords AS (
@@ -238,7 +321,7 @@ export class DiscreteMaterialPlanDAO {
PARTITION BY MaterialCode
ORDER BY CreateDate ASC, SequenceNumber ASC
) AS rn
FROM ${DISCRETE_MATERIAL_PLAN_CONFIG.TABLE_NAME_MYSQL}
FROM ${tableName}
WHERE SourceNumber IN (${placeholders})
AND MaterialCode IS NOT NULL
)
@@ -253,8 +336,37 @@ export class DiscreteMaterialPlanDAO {
WHERE rn = 1
`
const result = await mysqlService.query(sql, batch)
const result = await (dbService as SqlServerService).queryWithParams(sql, params)
allResults.push(...result.rows)
} else {
const placeholders = batch.map(() => '?').join(',')
const sql = `
WITH RankedRecords AS (
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY MaterialCode
ORDER BY CreateDate ASC, SequenceNumber ASC
) AS rn
FROM ${tableName}
WHERE SourceNumber IN (${placeholders})
AND MaterialCode IS NOT NULL
)
SELECT
Factory, MaterialStatus, PlanNumber, SourceNumber, MaterialType,
ProductCode, ProductName, ProductUnit, ProductPlanQuantity,
UseDepartment, Remark, Creator, CreateDate, Approver, ApproveDate,
SequenceNumber, MaterialCode, MaterialName, Specification, Model,
DrawingNumber, MaterialQuality, PlanQuantity, Unit, RequiredDate,
Warehouse, UnitUsage, CumulativeOutputQuantity, BOMVersion
FROM RankedRecords
WHERE rn = 1
`
const result = await (dbService as MySqlService).query(sql, batch)
allResults.push(...result.rows)
}
}
return allResults
@@ -271,16 +383,32 @@ export class DiscreteMaterialPlanDAO {
*/
async queryBySourceNumber(sourceNumber: string): Promise<any[]> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
if (this.dbType === 'sqlserver') {
const sql = `
SELECT *
FROM ${DISCRETE_MATERIAL_PLAN_CONFIG.TABLE_NAME_MYSQL}
FROM ${tableName}
WHERE SourceNumber = @sourceNumber
`
const result = await (dbService as SqlServerService).queryWithParams(sql, {
sourceNumber: { value: sourceNumber, type: sql.NVarChar }
})
return result.rows
} else {
const sql = `
SELECT *
FROM ${tableName}
WHERE SourceNumber = ?
`
const result = await mysqlService.query(sql, [sourceNumber])
const result = await (dbService as MySqlService).query(sql, [sourceNumber])
return result.rows
}
} catch (error) {
console.error('[DiscreteMaterialPlanDAO] Query by source number error:', error)
return []
@@ -296,16 +424,32 @@ export class DiscreteMaterialPlanDAO {
*/
async queryByPlanNumber(planNumber: string): Promise<any[]> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
if (this.dbType === 'sqlserver') {
const sql = `
SELECT *
FROM ${DISCRETE_MATERIAL_PLAN_CONFIG.TABLE_NAME_MYSQL}
FROM ${tableName}
WHERE PlanNumber = @planNumber
`
const result = await (dbService as SqlServerService).queryWithParams(sql, {
planNumber: { value: planNumber, type: sql.NVarChar }
})
return result.rows
} else {
const sql = `
SELECT *
FROM ${tableName}
WHERE PlanNumber = ?
`
const result = await mysqlService.query(sql, [planNumber])
const result = await (dbService as MySqlService).query(sql, [planNumber])
return result.rows
}
} catch (error) {
console.error('[DiscreteMaterialPlanDAO] Query by plan number error:', error)
return []
@@ -323,17 +467,37 @@ export class DiscreteMaterialPlanDAO {
}
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
if (this.dbType === 'sqlserver') {
const placeholders = planNumbers.map((_, idx) => `@p${idx}`).join(',')
const params: Record<string, { value: string; type: sql.ISqlType }> = {}
planNumbers.forEach((num, idx) => {
params[`p${idx}`] = { value: num, type: sql.NVarChar }
})
const sql = `
SELECT *
FROM ${tableName}
WHERE PlanNumber IN (${placeholders})
`
const result = await (dbService as SqlServerService).queryWithParams(sql, params)
return result.rows
} else {
const placeholders = planNumbers.map(() => '?').join(',')
const sql = `
SELECT *
FROM ${DISCRETE_MATERIAL_PLAN_CONFIG.TABLE_NAME_MYSQL}
FROM ${tableName}
WHERE PlanNumber IN (${placeholders})
`
const result = await mysqlService.query(sql, planNumbers)
const result = await (dbService as MySqlService).query(sql, planNumbers)
return result.rows
}
} catch (error) {
console.error('[DiscreteMaterialPlanDAO] Query by plan numbers error:', error)
return []
@@ -348,11 +512,15 @@ export class DiscreteMaterialPlanDAO {
*/
async countAll(): Promise<number> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const sql = `SELECT COUNT(*) as count FROM ${DISCRETE_MATERIAL_PLAN_CONFIG.TABLE_NAME_MYSQL}`
const sql = `SELECT COUNT(*) as count FROM ${tableName}`
const result = this.dbType === 'sqlserver'
? await (dbService as SqlServerService).query(sql)
: await (dbService as MySqlService).query(sql)
const result = await mysqlService.query(sql)
return result.rows.length > 0 ? (result.rows[0].count as number) : 0
} catch (error) {
console.error('[DiscreteMaterialPlanDAO] Count all error:', error)
@@ -367,16 +535,32 @@ export class DiscreteMaterialPlanDAO {
*/
async countByPlanNumber(planNumber: string): Promise<number> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
if (this.dbType === 'sqlserver') {
const sql = `
SELECT COUNT(*) as count
FROM ${DISCRETE_MATERIAL_PLAN_CONFIG.TABLE_NAME_MYSQL}
FROM ${tableName}
WHERE PlanNumber = @planNumber
`
const result = await (dbService as SqlServerService).queryWithParams(sql, {
planNumber: { value: planNumber, type: sql.NVarChar }
})
return result.rows.length > 0 ? (result.rows[0].count as number) : 0
} else {
const sql = `
SELECT COUNT(*) as count
FROM ${tableName}
WHERE PlanNumber = ?
`
const result = await mysqlService.query(sql, [planNumber])
const result = await (dbService as MySqlService).query(sql, [planNumber])
return result.rows.length > 0 ? (result.rows[0].count as number) : 0
}
} catch (error) {
console.error('[DiscreteMaterialPlanDAO] Count by plan number error:', error)
return 0
@@ -390,32 +574,59 @@ export class DiscreteMaterialPlanDAO {
*/
async getUniqueMaterialNames(sourceNumbers?: string[]): Promise<string[]> {
try {
const mysqlService = await this.getMySqlService()
let sql: string
let params: any[] = []
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
if (sourceNumbers && sourceNumbers.length > 0) {
const placeholders = sourceNumbers.map(() => '?').join(',')
sql = `
if (this.dbType === 'sqlserver') {
const placeholders = sourceNumbers.map((_, idx) => `@p${idx}`).join(',')
const params: Record<string, { value: string; type: sql.ISqlType }> = {}
sourceNumbers.forEach((num, idx) => {
params[`p${idx}`] = { value: num, type: sql.NVarChar }
})
const sql = `
SELECT DISTINCT MaterialName
FROM ${DISCRETE_MATERIAL_PLAN_CONFIG.TABLE_NAME_MYSQL}
FROM ${tableName}
WHERE SourceNumber IN (${placeholders})
AND MaterialName IS NOT NULL
`
params = sourceNumbers
} else {
sql = `
SELECT DISTINCT MaterialName
FROM ${DISCRETE_MATERIAL_PLAN_CONFIG.TABLE_NAME_MYSQL}
WHERE MaterialName IS NOT NULL
`
}
const result = await mysqlService.query(sql, params)
const result = await (dbService as SqlServerService).queryWithParams(sql, params)
return result.rows
.map(row => row.MaterialName as string)
.filter(Boolean)
} else {
const placeholders = sourceNumbers.map(() => '?').join(',')
const sql = `
SELECT DISTINCT MaterialName
FROM ${tableName}
WHERE SourceNumber IN (${placeholders})
AND MaterialName IS NOT NULL
`
const result = await (dbService as MySqlService).query(sql, sourceNumbers)
return result.rows
.map(row => row.MaterialName as string)
.filter(Boolean)
}
} else {
const sql = `
SELECT DISTINCT MaterialName
FROM ${tableName}
WHERE MaterialName IS NOT NULL
`
const result = this.dbType === 'sqlserver'
? await (dbService as SqlServerService).query(sql)
: await (dbService as MySqlService).query(sql)
return result.rows
.map(row => row.MaterialName as string)
.filter(Boolean)
}
} catch (error) {
console.error('[DiscreteMaterialPlanDAO] Get unique material names error:', error)
return []
@@ -428,7 +639,8 @@ export class DiscreteMaterialPlanDAO {
*/
async getStatistics(): Promise<any> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const sql = `
SELECT
@@ -437,10 +649,13 @@ export class DiscreteMaterialPlanDAO {
COUNT(DISTINCT SourceNumber) as uniqueOrders,
MIN(CreateDate) as earliestRecord,
MAX(CreateDate) as latestRecord
FROM ${DISCRETE_MATERIAL_PLAN_CONFIG.TABLE_NAME_MYSQL}
FROM ${tableName}
`
const result = await mysqlService.query(sql)
const result = this.dbType === 'sqlserver'
? await (dbService as SqlServerService).query(sql)
: await (dbService as MySqlService).query(sql)
return result.rows.length > 0 ? result.rows[0] : {}
} catch (error) {
console.error('[DiscreteMaterialPlanDAO] Get statistics error:', error)
@@ -456,5 +671,9 @@ export class DiscreteMaterialPlanDAO {
await this.mysqlService.disconnect()
this.mysqlService = null
}
if (this.sqlServerService) {
await this.sqlServerService.disconnect()
this.sqlServerService = null
}
}
}

View File

@@ -9,6 +9,8 @@
*/
import { MySqlService } from './mysql'
import { SqlServerService } from './sql-server'
import sql from 'mssql'
/**
* Material record interface
@@ -41,6 +43,7 @@ export interface MaterialStats {
* Configuration for MaterialsToBeDeleted table
*/
export const MATERIALS_TO_BE_DELETED_CONFIG = {
TABLE_NAME_SQLSERVER: '[dbo].[MaterialsToBeDeleted]',
TABLE_NAME_MYSQL: 'dbo_MaterialsToBeDeleted',
COLUMNS: {
ID: 'ID',
@@ -54,11 +57,54 @@ export const MATERIALS_TO_BE_DELETED_CONFIG = {
*/
export class MaterialsToBeDeletedDAO {
private mysqlService: MySqlService | null = null
private sqlServerService: SqlServerService | null = null
private dbType: 'mysql' | 'sqlserver' = 'mysql'
/**
* Get MySQL service instance
* Constructor - determine database type from environment
*/
private async getMySqlService(): Promise<MySqlService> {
constructor() {
const dbType = process.env.DB_TYPE?.toLowerCase()
if (dbType === 'sqlserver' || dbType === 'mssql') {
this.dbType = 'sqlserver'
} else {
this.dbType = 'mysql'
}
}
/**
* Get the appropriate table name based on database type
*/
private getTableName(): string {
return this.dbType === 'sqlserver'
? MATERIALS_TO_BE_DELETED_CONFIG.TABLE_NAME_SQLSERVER
: MATERIALS_TO_BE_DELETED_CONFIG.TABLE_NAME_MYSQL
}
/**
* Get database service instance (MySQL or SQL Server)
*/
private async getDatabaseService(): Promise<MySqlService | SqlServerService> {
if (this.dbType === 'sqlserver') {
if (this.sqlServerService && this.sqlServerService.isConnected()) {
return this.sqlServerService
}
this.sqlServerService = new SqlServerService({
server: process.env.DB_SERVER || 'localhost',
port: parseInt(process.env.DB_SQLSERVER_PORT || '1433', 10),
user: process.env.DB_USERNAME || 'sa',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME || '',
options: {
encrypt: process.env.DB_TRUST_SERVER_CERTIFICATE === 'yes',
trustServerCertificate: process.env.DB_TRUST_SERVER_CERTIFICATE === 'yes'
}
})
await this.sqlServerService.connect()
return this.sqlServerService
} else {
if (this.mysqlService && this.mysqlService.isConnected()) {
return this.mysqlService
}
@@ -74,6 +120,7 @@ export class MaterialsToBeDeletedDAO {
await this.mysqlService.connect()
return this.mysqlService
}
}
// ==================== UPSERT (MERGE) ====================
@@ -90,15 +137,34 @@ export class MaterialsToBeDeletedDAO {
}
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const code = materialCode.trim()
const manager = managerName?.trim() || null
if (this.dbType === 'sqlserver') {
const sql = `
INSERT INTO ${MATERIALS_TO_BE_DELETED_CONFIG.TABLE_NAME_MYSQL} (MaterialCode, ManagerName)
MERGE ${tableName} AS target
USING (VALUES (@materialCode, @managerName)) AS source (MaterialCode, ManagerName)
ON target.MaterialCode = source.MaterialCode
WHEN MATCHED THEN UPDATE SET ManagerName = source.ManagerName
WHEN NOT MATCHED THEN INSERT (MaterialCode, ManagerName) VALUES (source.MaterialCode, source.ManagerName);
`
await (dbService as SqlServerService).queryWithParams(sql, {
materialCode: { value: code, type: sql.NVarChar },
managerName: { value: manager, type: sql.NVarChar }
})
} else {
const sql = `
INSERT INTO ${tableName} (MaterialCode, ManagerName)
VALUES (?, ?)
ON DUPLICATE KEY UPDATE ManagerName = VALUES(ManagerName)
`
await mysqlService.query(sql, [materialCode.trim(), managerName?.trim() || null])
await (dbService as MySqlService).query(sql, [code, manager])
}
return true
} catch (error) {
console.error('[MaterialsToBeDeletedDAO] Upsert material error:', error)
@@ -123,7 +189,8 @@ export class MaterialsToBeDeletedDAO {
}
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
for (const material of materials) {
const materialCode = material.materialCode?.trim()
@@ -135,13 +202,29 @@ export class MaterialsToBeDeletedDAO {
}
try {
if (this.dbType === 'sqlserver') {
const sql = `
INSERT INTO ${MATERIALS_TO_BE_DELETED_CONFIG.TABLE_NAME_MYSQL} (MaterialCode, ManagerName)
MERGE ${tableName} AS target
USING (VALUES (@materialCode, @managerName)) AS source (MaterialCode, ManagerName)
ON target.MaterialCode = source.MaterialCode
WHEN MATCHED THEN UPDATE SET ManagerName = source.ManagerName
WHEN NOT MATCHED THEN INSERT (MaterialCode, ManagerName) VALUES (source.MaterialCode, source.ManagerName);
`
await (dbService as SqlServerService).queryWithParams(sql, {
materialCode: { value: materialCode, type: sql.NVarChar },
managerName: { value: managerName || null, type: sql.NVarChar }
})
} else {
const sql = `
INSERT INTO ${tableName} (MaterialCode, ManagerName)
VALUES (?, ?)
ON DUPLICATE KEY UPDATE ManagerName = VALUES(ManagerName)
`
await mysqlService.query(sql, [materialCode, managerName || null])
await (dbService as MySqlService).query(sql, [materialCode, managerName || null])
}
stats.success++
} catch (error) {
console.error('[MaterialsToBeDeletedDAO] Error upserting material:', materialCode, error)
@@ -164,15 +247,19 @@ export class MaterialsToBeDeletedDAO {
*/
async getAllMaterialCodes(): Promise<Set<string>> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const sql = `
SELECT MaterialCode
FROM ${MATERIALS_TO_BE_DELETED_CONFIG.TABLE_NAME_MYSQL}
FROM ${tableName}
WHERE MaterialCode IS NOT NULL
`
const result = await mysqlService.query(sql)
const result = this.dbType === 'sqlserver'
? await (dbService as SqlServerService).query(sql)
: await (dbService as MySqlService).query(sql)
return new Set(result.rows.map(row => row.MaterialCode as string).filter(Boolean))
} catch (error) {
console.error('[MaterialsToBeDeletedDAO] Get all material codes error:', error)
@@ -186,16 +273,20 @@ export class MaterialsToBeDeletedDAO {
*/
async getAllRecords(): Promise<MaterialRecord[]> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const sql = `
SELECT ID, MaterialCode, ManagerName
FROM ${MATERIALS_TO_BE_DELETED_CONFIG.TABLE_NAME_MYSQL}
FROM ${tableName}
WHERE MaterialCode IS NOT NULL
ORDER BY ManagerName, MaterialCode
`
const result = await mysqlService.query(sql)
const result = this.dbType === 'sqlserver'
? await (dbService as SqlServerService).query(sql)
: await (dbService as MySqlService).query(sql)
return result.rows.map(row => ({
id: row.ID as number,
materialCode: row.MaterialCode as string,
@@ -214,21 +305,42 @@ export class MaterialsToBeDeletedDAO {
*/
async getMaterialsByManager(managerName: string): Promise<MaterialRecord[]> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
if (this.dbType === 'sqlserver') {
const sql = `
SELECT ID, MaterialCode, ManagerName
FROM ${MATERIALS_TO_BE_DELETED_CONFIG.TABLE_NAME_MYSQL}
WHERE ManagerName = ? AND MaterialCode IS NOT NULL
FROM ${tableName}
WHERE ManagerName = @managerName AND MaterialCode IS NOT NULL
ORDER BY MaterialCode
`
const result = await mysqlService.query(sql, [managerName])
const result = await (dbService as SqlServerService).queryWithParams(sql, {
managerName: { value: managerName, type: sql.NVarChar }
})
return result.rows.map(row => ({
id: row.ID as number,
materialCode: row.MaterialCode as string,
managerName: row.ManagerName as string
}))
} else {
const sql = `
SELECT ID, MaterialCode, ManagerName
FROM ${tableName}
WHERE ManagerName = ? AND MaterialCode IS NOT NULL
ORDER BY MaterialCode
`
const result = await (dbService as MySqlService).query(sql, [managerName])
return result.rows.map(row => ({
id: row.ID as number,
materialCode: row.MaterialCode as string,
managerName: row.ManagerName as string
}))
}
} catch (error) {
console.error('[MaterialsToBeDeletedDAO] Get materials by manager error:', error)
return []
@@ -241,16 +353,20 @@ export class MaterialsToBeDeletedDAO {
*/
async getManagers(): Promise<string[]> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const sql = `
SELECT DISTINCT ManagerName
FROM ${MATERIALS_TO_BE_DELETED_CONFIG.TABLE_NAME_MYSQL}
FROM ${tableName}
WHERE ManagerName IS NOT NULL
ORDER BY ManagerName
`
const result = await mysqlService.query(sql)
const result = this.dbType === 'sqlserver'
? await (dbService as SqlServerService).query(sql)
: await (dbService as MySqlService).query(sql)
return result.rows.map(row => row.ManagerName as string).filter(Boolean)
} catch (error) {
console.error('[MaterialsToBeDeletedDAO] Get managers error:', error)
@@ -265,15 +381,21 @@ export class MaterialsToBeDeletedDAO {
*/
async getRecordByMaterialCode(materialCode: string): Promise<MaterialRecord | null> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const code = materialCode.trim()
if (this.dbType === 'sqlserver') {
const sql = `
SELECT ID, MaterialCode, ManagerName
FROM ${MATERIALS_TO_BE_DELETED_CONFIG.TABLE_NAME_MYSQL}
WHERE MaterialCode = ?
FROM ${tableName}
WHERE MaterialCode = @materialCode
`
const result = await mysqlService.query(sql, [materialCode.trim()])
const result = await (dbService as SqlServerService).queryWithParams(sql, {
materialCode: { value: code, type: sql.NVarChar }
})
if (result.rows.length === 0) {
return null
}
@@ -284,6 +406,26 @@ export class MaterialsToBeDeletedDAO {
materialCode: row.MaterialCode as string,
managerName: row.ManagerName as string
}
} else {
const sql = `
SELECT ID, MaterialCode, ManagerName
FROM ${tableName}
WHERE MaterialCode = ?
`
const result = await (dbService as MySqlService).query(sql, [code])
if (result.rows.length === 0) {
return null
}
const row = result.rows[0]
return {
id: row.ID as number,
materialCode: row.MaterialCode as string,
managerName: row.ManagerName as string
}
}
} catch (error) {
console.error('[MaterialsToBeDeletedDAO] Get record by material code error:', error)
return null
@@ -299,15 +441,31 @@ export class MaterialsToBeDeletedDAO {
*/
async deleteByMaterialCode(materialCode: string): Promise<boolean> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const code = materialCode.trim()
if (this.dbType === 'sqlserver') {
const sql = `
DELETE FROM ${MATERIALS_TO_BE_DELETED_CONFIG.TABLE_NAME_MYSQL}
DELETE FROM ${tableName}
WHERE MaterialCode = @materialCode
`
const result = await (dbService as SqlServerService).queryWithParams(sql, {
materialCode: { value: code, type: sql.NVarChar }
})
return result.rowCount > 0
} else {
const sql = `
DELETE FROM ${tableName}
WHERE MaterialCode = ?
`
const result = await mysqlService.query(sql, [materialCode.trim()])
const result = await (dbService as MySqlService).query(sql, [code])
return result.rowCount > 0
}
} catch (error) {
console.error('[MaterialsToBeDeletedDAO] Delete by material code error:', error)
return false
@@ -321,15 +479,30 @@ export class MaterialsToBeDeletedDAO {
*/
async deleteByManager(managerName: string): Promise<number> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
if (this.dbType === 'sqlserver') {
const sql = `
DELETE FROM ${MATERIALS_TO_BE_DELETED_CONFIG.TABLE_NAME_MYSQL}
DELETE FROM ${tableName}
WHERE ManagerName = @managerName
`
const result = await (dbService as SqlServerService).queryWithParams(sql, {
managerName: { value: managerName, type: sql.NVarChar }
})
return result.rowCount
} else {
const sql = `
DELETE FROM ${tableName}
WHERE ManagerName = ?
`
const result = await mysqlService.query(sql, [managerName])
const result = await (dbService as MySqlService).query(sql, [managerName])
return result.rowCount
}
} catch (error) {
console.error('[MaterialsToBeDeletedDAO] Delete by manager error:', error)
return 0
@@ -342,11 +515,15 @@ export class MaterialsToBeDeletedDAO {
*/
async deleteAllMaterials(): Promise<number> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const sql = `DELETE FROM ${MATERIALS_TO_BE_DELETED_CONFIG.TABLE_NAME_MYSQL}`
const sql = `DELETE FROM ${tableName}`
const result = this.dbType === 'sqlserver'
? await (dbService as SqlServerService).query(sql)
: await (dbService as MySqlService).query(sql)
const result = await mysqlService.query(sql)
return result.rowCount
} catch (error) {
console.error('[MaterialsToBeDeletedDAO] Delete all materials error:', error)
@@ -368,19 +545,38 @@ export class MaterialsToBeDeletedDAO {
const batchSize = 1000
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
for (let i = 0; i < materialCodes.length; i += batchSize) {
const batch = materialCodes.slice(i, i + batchSize)
const placeholders = batch.map(() => '?').join(',')
if (this.dbType === 'sqlserver') {
const placeholders = batch.map((_, idx) => `@p${idx}`).join(',')
const params: Record<string, { value: string; type: sql.ISqlType }> = {}
batch.forEach((code, idx) => {
params[`p${idx}`] = { value: code.trim(), type: sql.NVarChar }
})
const sql = `
DELETE FROM ${MATERIALS_TO_BE_DELETED_CONFIG.TABLE_NAME_MYSQL}
DELETE FROM ${tableName}
WHERE MaterialCode IN (${placeholders})
`
const result = await mysqlService.query(sql, batch)
const result = await (dbService as SqlServerService).queryWithParams(sql, params)
totalDeleted += result.rowCount
} else {
const placeholders = batch.map(() => '?').join(',')
const sql = `
DELETE FROM ${tableName}
WHERE MaterialCode IN (${placeholders})
`
const result = await (dbService as MySqlService).query(sql, batch)
totalDeleted += result.rowCount
}
}
} catch (error) {
console.error('[MaterialsToBeDeletedDAO] Delete by material codes error:', error)
@@ -398,16 +594,33 @@ export class MaterialsToBeDeletedDAO {
*/
async materialExists(materialCode: string): Promise<boolean> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const code = materialCode.trim()
if (this.dbType === 'sqlserver') {
const sql = `
SELECT COUNT(*) as count
FROM ${MATERIALS_TO_BE_DELETED_CONFIG.TABLE_NAME_MYSQL}
FROM ${tableName}
WHERE MaterialCode = @materialCode
`
const result = await (dbService as SqlServerService).queryWithParams(sql, {
materialCode: { value: code, type: sql.NVarChar }
})
return result.rows.length > 0 && (result.rows[0].count as number) > 0
} else {
const sql = `
SELECT COUNT(*) as count
FROM ${tableName}
WHERE MaterialCode = ?
`
const result = await mysqlService.query(sql, [materialCode.trim()])
const result = await (dbService as MySqlService).query(sql, [code])
return result.rows.length > 0 && (result.rows[0].count as number) > 0
}
} catch (error) {
console.error('[MaterialsToBeDeletedDAO] Material exists error:', error)
return false
@@ -420,11 +633,15 @@ export class MaterialsToBeDeletedDAO {
*/
async countAll(): Promise<number> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const sql = `SELECT COUNT(*) as count FROM ${MATERIALS_TO_BE_DELETED_CONFIG.TABLE_NAME_MYSQL}`
const sql = `SELECT COUNT(*) as count FROM ${tableName}`
const result = this.dbType === 'sqlserver'
? await (dbService as SqlServerService).query(sql)
: await (dbService as MySqlService).query(sql)
const result = await mysqlService.query(sql)
return result.rows.length > 0 ? (result.rows[0].count as number) : 0
} catch (error) {
console.error('[MaterialsToBeDeletedDAO] Count all error:', error)
@@ -439,16 +656,32 @@ export class MaterialsToBeDeletedDAO {
*/
async countByManager(managerName: string): Promise<number> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
if (this.dbType === 'sqlserver') {
const sql = `
SELECT COUNT(*) as count
FROM ${MATERIALS_TO_BE_DELETED_CONFIG.TABLE_NAME_MYSQL}
FROM ${tableName}
WHERE ManagerName = @managerName
`
const result = await (dbService as SqlServerService).queryWithParams(sql, {
managerName: { value: managerName, type: sql.NVarChar }
})
return result.rows.length > 0 ? (result.rows[0].count as number) : 0
} else {
const sql = `
SELECT COUNT(*) as count
FROM ${tableName}
WHERE ManagerName = ?
`
const result = await mysqlService.query(sql, [managerName])
const result = await (dbService as MySqlService).query(sql, [managerName])
return result.rows.length > 0 ? (result.rows[0].count as number) : 0
}
} catch (error) {
console.error('[MaterialsToBeDeletedDAO] Count by manager error:', error)
return 0
@@ -461,30 +694,37 @@ export class MaterialsToBeDeletedDAO {
*/
async getStatistics(): Promise<MaterialStats> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
// Get total and unique managers
const statsSql = `
SELECT
COUNT(*) as totalMaterials,
COUNT(DISTINCT ManagerName) as uniqueManagers
FROM ${MATERIALS_TO_BE_DELETED_CONFIG.TABLE_NAME_MYSQL}
FROM ${tableName}
WHERE MaterialCode IS NOT NULL
`
const statsResult = await mysqlService.query(statsSql)
const statsResult = this.dbType === 'sqlserver'
? await (dbService as SqlServerService).query(statsSql)
: await (dbService as MySqlService).query(statsSql)
const stats = statsResult.rows[0] || {}
// Get materials per manager
const managerSql = `
SELECT ManagerName, COUNT(*) as count
FROM ${MATERIALS_TO_BE_DELETED_CONFIG.TABLE_NAME_MYSQL}
FROM ${tableName}
WHERE ManagerName IS NOT NULL
GROUP BY ManagerName
ORDER BY count DESC
`
const managerResult = await mysqlService.query(managerSql)
const managerResult = this.dbType === 'sqlserver'
? await (dbService as SqlServerService).query(managerSql)
: await (dbService as MySqlService).query(managerSql)
const materialsPerManager = managerResult.rows.map(row => ({
[row.ManagerName as string]: row.count as number
}))
@@ -512,5 +752,9 @@ export class MaterialsToBeDeletedDAO {
await this.mysqlService.disconnect()
this.mysqlService = null
}
if (this.sqlServerService) {
await this.sqlServerService.disconnect()
this.sqlServerService = null
}
}
}

View File

@@ -98,8 +98,9 @@ export class SqlServerService {
const result = await request.query(sqlString)
// Convert recordset to array of objects
const columns = result.recordset.columns?.map((col) => col.name) || []
const rows = result.recordset as Record<string, unknown>[]
// Extract column names from the first row if available
const columns = rows.length > 0 ? Object.keys(rows[0]) : []
return {
rows,
@@ -137,8 +138,9 @@ export class SqlServerService {
const result = await request.query(sqlString)
// Convert recordset to array of objects
const columns = result.recordset.columns?.map((col) => col.name) || []
const rows = result.recordset as Record<string, unknown>[]
// Extract column names from the first row if available
const columns = rows.length > 0 ? Object.keys(rows[0]) : []
return {
rows,

View File

@@ -9,6 +9,8 @@
*/
import { MySqlService } from '../database/mysql'
import { SqlServerService } from '../database/sql-server'
import sql from 'mssql'
import type { UserInfo } from '../../types/user.types'
/**
@@ -35,11 +37,54 @@ export const BIP_USERS_CONFIG = {
*/
export class BIPUsersDAO {
private mysqlService: MySqlService | null = null
private sqlServerService: SqlServerService | null = null
private dbType: 'mysql' | 'sqlserver' = 'mysql'
/**
* Get MySQL service instance
* Constructor - determine database type from environment
*/
private async getMySqlService(): Promise<MySqlService> {
constructor() {
const dbType = process.env.DB_TYPE?.toLowerCase()
if (dbType === 'sqlserver' || dbType === 'mssql') {
this.dbType = 'sqlserver'
} else {
this.dbType = 'mysql'
}
}
/**
* Get the appropriate table name based on database type
*/
private getTableName(): string {
return this.dbType === 'sqlserver'
? BIP_USERS_CONFIG.TABLE_NAME_SQLSERVER
: BIP_USERS_CONFIG.TABLE_NAME_MYSQL
}
/**
* Get database service instance (MySQL or SQL Server)
*/
private async getDatabaseService(): Promise<MySqlService | SqlServerService> {
if (this.dbType === 'sqlserver') {
if (this.sqlServerService && this.sqlServerService.isConnected()) {
return this.sqlServerService
}
this.sqlServerService = new SqlServerService({
server: process.env.DB_SERVER || 'localhost',
port: parseInt(process.env.DB_SQLSERVER_PORT || '1433', 10),
user: process.env.DB_USERNAME || 'sa',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME || '',
options: {
encrypt: process.env.DB_TRUST_SERVER_CERTIFICATE === 'yes',
trustServerCertificate: process.env.DB_TRUST_SERVER_CERTIFICATE === 'yes'
}
})
await this.sqlServerService.connect()
return this.sqlServerService
} else {
if (this.mysqlService && this.mysqlService.isConnected()) {
return this.mysqlService
}
@@ -55,6 +100,7 @@ export class BIPUsersDAO {
await this.mysqlService.connect()
return this.mysqlService
}
}
/**
* Authenticate a user with username and password
@@ -64,15 +110,20 @@ export class BIPUsersDAO {
*/
async authenticate(username: string, password: string): Promise<UserInfo | null> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
if (this.dbType === 'sqlserver') {
const sql = `
SELECT ID, UserName, UserType
FROM ${BIP_USERS_CONFIG.TABLE_NAME_MYSQL}
WHERE UserName = ? AND Password = ?
FROM ${tableName}
WHERE UserName = @username AND Password = @password
`
const result = await mysqlService.query(sql, [username, password])
const result = await (dbService as SqlServerService).queryWithParams(sql, {
username: { value: username, type: sql.NVarChar },
password: { value: password, type: sql.NVarChar }
})
if (result.rows.length > 0) {
const row = result.rows[0]
@@ -83,6 +134,25 @@ export class BIPUsersDAO {
}
}
return null
} else {
const sql = `
SELECT ID, UserName, UserType
FROM ${tableName}
WHERE UserName = ? AND Password = ?
`
const result = await (dbService as MySqlService).query(sql, [username, password])
if (result.rows.length > 0) {
const row = result.rows[0]
return {
id: row.ID as number,
username: row.UserName as string,
userType: row.UserType as 'Admin' | 'User' | 'Guest'
}
}
return null
}
} catch (error) {
console.error('[BIPUsersDAO] Authenticate error:', error)
return null
@@ -96,15 +166,19 @@ export class BIPUsersDAO {
*/
async authenticateByComputerName(computerName: string): Promise<UserInfo | null> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
if (this.dbType === 'sqlserver') {
const sql = `
SELECT ID, UserName, UserType
FROM ${BIP_USERS_CONFIG.TABLE_NAME_MYSQL}
WHERE ComputerNmae = ?
FROM ${tableName}
WHERE ComputerNmae = @computerName
`
const result = await mysqlService.query(sql, [computerName])
const result = await (dbService as SqlServerService).queryWithParams(sql, {
computerName: { value: computerName, type: sql.NVarChar }
})
if (result.rows.length > 0) {
const row = result.rows[0]
@@ -115,6 +189,25 @@ export class BIPUsersDAO {
}
}
return null
} else {
const sql = `
SELECT ID, UserName, UserType
FROM ${tableName}
WHERE ComputerNmae = ?
`
const result = await (dbService as MySqlService).query(sql, [computerName])
if (result.rows.length > 0) {
const row = result.rows[0]
return {
id: row.ID as number,
username: row.UserName as string,
userType: row.UserType as 'Admin' | 'User' | 'Guest'
}
}
return null
}
} catch (error) {
console.error('[BIPUsersDAO] Authenticate by computer name error:', error)
return null
@@ -127,15 +220,18 @@ export class BIPUsersDAO {
*/
async getAllUsers(): Promise<UserInfo[]> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const sql = `
SELECT ID, UserName, UserType, CreateTime
FROM ${BIP_USERS_CONFIG.TABLE_NAME_MYSQL}
FROM ${tableName}
ORDER BY UserName
`
const result = await mysqlService.query(sql)
const result = this.dbType === 'sqlserver'
? await (dbService as SqlServerService).query(sql)
: await (dbService as MySqlService).query(sql)
return result.rows.map(row => ({
id: row.ID as number,
@@ -164,29 +260,63 @@ export class BIPUsersDAO {
computerName: string = ''
): Promise<boolean> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
if (this.dbType === 'sqlserver') {
let sql: string
let params: Record<string, { value: unknown; type?: sql.ISqlType }>
if (computerName) {
sql = `
INSERT INTO ${tableName}
(UserName, Password, UserType, ComputerNmae)
VALUES (@username, @password, @userType, @computerName)
`
params = {
username: { value: username, type: sql.NVarChar },
password: { value: password, type: sql.NVarChar },
userType: { value: userType, type: sql.NVarChar },
computerName: { value: computerName, type: sql.NVarChar }
}
} else {
sql = `
INSERT INTO ${tableName}
(UserName, Password, UserType)
VALUES (@username, @password, @userType)
`
params = {
username: { value: username, type: sql.NVarChar },
password: { value: password, type: sql.NVarChar },
userType: { value: userType, type: sql.NVarChar }
}
}
await (dbService as SqlServerService).queryWithParams(sql, params)
return true
} else {
let sql: string
let params: any[]
if (computerName) {
sql = `
INSERT INTO ${BIP_USERS_CONFIG.TABLE_NAME_MYSQL}
INSERT INTO ${tableName}
(UserName, Password, UserType, ComputerNmae)
VALUES (?, ?, ?, ?)
`
params = [username, password, userType, computerName]
} else {
sql = `
INSERT INTO ${BIP_USERS_CONFIG.TABLE_NAME_MYSQL}
INSERT INTO ${tableName}
(UserName, Password, UserType)
VALUES (?, ?, ?)
`
params = [username, password, userType]
}
await mysqlService.query(sql, params)
await (dbService as MySqlService).query(sql, params)
return true
}
} catch (error) {
console.error('[BIPUsersDAO] Create user error:', error)
return false
@@ -201,16 +331,31 @@ export class BIPUsersDAO {
*/
async updateUserType(username: string, userType: string): Promise<boolean> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
if (this.dbType === 'sqlserver') {
const sql = `
UPDATE ${BIP_USERS_CONFIG.TABLE_NAME_MYSQL}
UPDATE ${tableName}
SET UserType = @userType
WHERE UserName = @username
`
await (dbService as SqlServerService).queryWithParams(sql, {
username: { value: username, type: sql.NVarChar },
userType: { value: userType, type: sql.NVarChar }
})
return true
} else {
const sql = `
UPDATE ${tableName}
SET UserType = ?
WHERE UserName = ?
`
await mysqlService.query(sql, [userType, username])
await (dbService as MySqlService).query(sql, [userType, username])
return true
}
} catch (error) {
console.error('[BIPUsersDAO] Update user type error:', error)
return false
@@ -225,16 +370,31 @@ export class BIPUsersDAO {
*/
async updatePassword(username: string, newPassword: string): Promise<boolean> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
if (this.dbType === 'sqlserver') {
const sql = `
UPDATE ${BIP_USERS_CONFIG.TABLE_NAME_MYSQL}
UPDATE ${tableName}
SET Password = @newPassword
WHERE UserName = @username
`
await (dbService as SqlServerService).queryWithParams(sql, {
username: { value: username, type: sql.NVarChar },
newPassword: { value: newPassword, type: sql.NVarChar }
})
return true
} else {
const sql = `
UPDATE ${tableName}
SET Password = ?
WHERE UserName = ?
`
await mysqlService.query(sql, [newPassword, username])
await (dbService as MySqlService).query(sql, [newPassword, username])
return true
}
} catch (error) {
console.error('[BIPUsersDAO] Update password error:', error)
return false
@@ -248,15 +408,28 @@ export class BIPUsersDAO {
*/
async deleteUser(username: string): Promise<boolean> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
if (this.dbType === 'sqlserver') {
const sql = `
DELETE FROM ${BIP_USERS_CONFIG.TABLE_NAME_MYSQL}
DELETE FROM ${tableName}
WHERE UserName = @username
`
await (dbService as SqlServerService).queryWithParams(sql, {
username: { value: username, type: sql.NVarChar }
})
return true
} else {
const sql = `
DELETE FROM ${tableName}
WHERE UserName = ?
`
await mysqlService.query(sql, [username])
await (dbService as MySqlService).query(sql, [username])
return true
}
} catch (error) {
console.error('[BIPUsersDAO] Delete user error:', error)
return false
@@ -270,16 +443,30 @@ export class BIPUsersDAO {
*/
async userExists(username: string): Promise<boolean> {
try {
const mysqlService = await this.getMySqlService()
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
if (this.dbType === 'sqlserver') {
const sql = `
SELECT COUNT(*) as count
FROM ${BIP_USERS_CONFIG.TABLE_NAME_MYSQL}
FROM ${tableName}
WHERE UserName = @username
`
const result = await (dbService as SqlServerService).queryWithParams(sql, {
username: { value: username, type: sql.NVarChar }
})
return result.rows.length > 0 && (result.rows[0].count as number) > 0
} else {
const sql = `
SELECT COUNT(*) as count
FROM ${tableName}
WHERE UserName = ?
`
const result = await mysqlService.query(sql, [username])
const result = await (dbService as MySqlService).query(sql, [username])
return result.rows.length > 0 && (result.rows[0].count as number) > 0
}
} catch (error) {
console.error('[BIPUsersDAO] User exists error:', error)
return false
@@ -294,5 +481,9 @@ export class BIPUsersDAO {
await this.mysqlService.disconnect()
this.mysqlService = null
}
if (this.sqlServerService) {
await this.sqlServerService.disconnect()
this.sqlServerService = null
}
}
}