refactor(db): migrate BIPUsersDAO to use DatabaseFactory and SqlDialect

Replace hardcoded MySqlService/SqlServerService with DatabaseFactory,
enabling PostgreSQL support for user authentication and management.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Misaka
2026-04-05 11:20:17 +08:00
parent 13fb7bcf46
commit b5ba18b595

View File

@@ -8,10 +8,13 @@
* - Create, update, delete users * - Create, update, delete users
*/ */
import { MySqlService } from '../database/mysql' import {
import { SqlServerService } from '../database/sql-server' create,
disconnect as disconnectDb,
type IDatabaseService
} from '../database/index'
import { createDialect, type SqlDialect } from '../database/dialects'
import { ConfigManager } from '../config/config-manager' import { ConfigManager } from '../config/config-manager'
import sql from 'mssql'
import type { UserInfo } from '../../types/user.types' import type { UserInfo } from '../../types/user.types'
import { createLogger, logError } from '../logger' import { createLogger, logError } from '../logger'
@@ -21,10 +24,6 @@ const log = createLogger('BipUsersDao')
* Database configuration for BIPUsers table * Database configuration for BIPUsers table
*/ */
export const BIP_USERS_CONFIG = { export const BIP_USERS_CONFIG = {
/** Table name in SQL Server: [dbo].[BIPUsers] */
TABLE_NAME_SQLSERVER: '[dbo].[BIPUsers]',
/** Table name in MySQL: dbo_BIPUsers */
TABLE_NAME_MYSQL: 'dbo_BIPUsers',
/** Column names */ /** Column names */
COLUMNS: { COLUMNS: {
ID: 'ID', ID: 'ID',
@@ -44,71 +43,37 @@ export const BIP_USERS_CONFIG = {
* BIPUsers DAO Class * BIPUsers DAO Class
*/ */
export class BIPUsersDAO { export class BIPUsersDAO {
private mysqlService: MySqlService | null = null private dbService: IDatabaseService | null = null
private sqlServerService: SqlServerService | null = null private dialect: SqlDialect | null = null
private dbType: 'mysql' | 'sqlserver' | 'postgresql' = 'mysql'
private configManager: ConfigManager
/**
* Constructor - get database type from ConfigManager
*/
constructor() {
this.configManager = ConfigManager.getInstance()
this.dbType = this.configManager.getDatabaseType()
}
/** /**
* Get the appropriate table name based on database type * Get the appropriate table name based on database type
*/ */
private getTableName(): string { private getTableName(): string {
return this.dbType === 'sqlserver' return this.getDialect().quoteTableName('dbo', 'BIPUsers')
? BIP_USERS_CONFIG.TABLE_NAME_SQLSERVER
: BIP_USERS_CONFIG.TABLE_NAME_MYSQL
} }
/** /**
* Get database service instance (MySQL or SQL Server) * Get dialect instance
*/ */
private async getDatabaseService(): Promise<MySqlService | SqlServerService> { private getDialect(): SqlDialect {
const config = this.configManager.getConfig() if (!this.dialect) {
this.dialect = createDialect(this.dbService!.type)
if (this.dbType === 'sqlserver') { }
if (this.sqlServerService && this.sqlServerService.isConnected()) { return this.dialect
return this.sqlServerService
} }
const dbConfig = config.database.sqlserver /**
this.sqlServerService = new SqlServerService({ * Get database service instance via DatabaseFactory
server: dbConfig.server, */
port: dbConfig.port, private async getDatabaseService(): Promise<IDatabaseService> {
user: dbConfig.username, if (this.dbService && this.dbService.isConnected()) {
password: dbConfig.password, return this.dbService
database: dbConfig.database,
options: {
encrypt: false,
trustServerCertificate: dbConfig.trustServerCertificate
}
})
await this.sqlServerService.connect()
return this.sqlServerService
} else {
if (this.mysqlService && this.mysqlService.isConnected()) {
return this.mysqlService
} }
const dbConfig = config.database.mysql this.dbService = await create()
this.mysqlService = new MySqlService({ this.dialect = null // Reset dialect when service changes
host: dbConfig.host, return this.dbService
port: dbConfig.port,
user: dbConfig.username,
password: dbConfig.password,
database: dbConfig.database
})
await this.mysqlService.connect()
return this.mysqlService
}
} }
/** /**
@@ -121,18 +86,15 @@ export class BIPUsersDAO {
try { try {
const dbService = await this.getDatabaseService() const dbService = await this.getDatabaseService()
const tableName = this.getTableName() const tableName = this.getTableName()
const dialect = this.getDialect()
if (this.dbType === 'sqlserver') {
const sqlString = ` const sqlString = `
SELECT ID, UserName, UserType SELECT ID, UserName, UserType
FROM ${tableName} FROM ${tableName}
WHERE UserName = @username AND Password = @password WHERE UserName = ${dialect.param(0)} AND Password = ${dialect.param(1)}
` `
const result = await (dbService as SqlServerService).queryWithParams(sqlString, { const result = await dbService.query(sqlString, [username, password])
username: { value: username, type: sql.NVarChar(255) },
password: { value: password, type: sql.NVarChar(255) }
})
if (result.rows.length > 0) { if (result.rows.length > 0) {
const row = result.rows[0] const row = result.rows[0]
@@ -143,30 +105,11 @@ export class BIPUsersDAO {
} }
} }
return null return null
} else {
const sqlString = `
SELECT ID, UserName, UserType
FROM ${tableName}
WHERE UserName = ? AND Password = ?
`
const result = await (dbService as MySqlService).query(sqlString, [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'
}
}
return null
}
} catch (error) { } catch (error) {
logError(log, error, { logError(log, error, {
message: 'Authenticate failed', message: 'Authenticate failed',
operation: 'authenticate', operation: 'authenticate',
context: { username, dbType: this.dbType } context: { username, dbType: this.dbService?.type }
}) })
return null return null
} }
@@ -181,17 +124,15 @@ export class BIPUsersDAO {
try { try {
const dbService = await this.getDatabaseService() const dbService = await this.getDatabaseService()
const tableName = this.getTableName() const tableName = this.getTableName()
const dialect = this.getDialect()
if (this.dbType === 'sqlserver') {
const sqlString = ` const sqlString = `
SELECT ID, UserName, UserType SELECT ID, UserName, UserType
FROM ${tableName} FROM ${tableName}
WHERE ComputerName = @computerName WHERE ComputerName = ${dialect.param(0)}
` `
const result = await (dbService as SqlServerService).queryWithParams(sqlString, { const result = await dbService.query(sqlString, [computerName])
computerName: { value: computerName, type: sql.NVarChar(255) }
})
if (result.rows.length > 0) { if (result.rows.length > 0) {
const row = result.rows[0] const row = result.rows[0]
@@ -202,30 +143,11 @@ export class BIPUsersDAO {
} }
} }
return null return null
} else {
const sqlString = `
SELECT ID, UserName, UserType
FROM ${tableName}
WHERE ComputerName = ?
`
const result = await (dbService as MySqlService).query(sqlString, [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'
}
}
return null
}
} catch (error) { } catch (error) {
logError(log, error, { logError(log, error, {
message: 'Silent login failed', message: 'Silent login failed',
operation: 'authenticateByComputerName', operation: 'authenticateByComputerName',
context: { computerName, dbType: this.dbType } context: { computerName, dbType: this.dbService?.type }
}) })
return null return null
} }
@@ -246,10 +168,7 @@ export class BIPUsersDAO {
ORDER BY UserName ORDER BY UserName
` `
const result = const result = await dbService.query(sqlString)
this.dbType === 'sqlserver'
? await (dbService as SqlServerService).query(sqlString)
: await (dbService as MySqlService).query(sqlString)
return result.rows.map((row) => ({ return result.rows.map((row) => ({
id: row.ID as number, id: row.ID as number,
@@ -261,7 +180,7 @@ export class BIPUsersDAO {
logError(log, error, { logError(log, error, {
message: 'Get all users failed', message: 'Get all users failed',
operation: 'getAllUsers', operation: 'getAllUsers',
context: { dbType: this.dbType } context: { dbType: this.dbService?.type }
}) })
return [] return []
} }
@@ -284,72 +203,34 @@ export class BIPUsersDAO {
try { try {
const dbService = await this.getDatabaseService() const dbService = await this.getDatabaseService()
const tableName = this.getTableName() const tableName = this.getTableName()
const dialect = this.getDialect()
if (this.dbType === 'sqlserver') {
let sqlString: string let sqlString: string
let params: Record< let params: string[]
string,
{
value: unknown
type?: sql.ISqlType | sql.ISqlTypeFactoryWithLength | sql.ISqlTypeWithLength
}
>
if (computerName) { if (computerName) {
sqlString = ` sqlString = `
INSERT INTO ${tableName} INSERT INTO ${tableName}
(UserName, Password, UserType, ComputerName) (UserName, Password, UserType, ComputerName)
VALUES (@username, @password, @userType, @computerName) VALUES (${dialect.param(0)}, ${dialect.param(1)}, ${dialect.param(2)}, ${dialect.param(3)})
`
params = {
username: { value: username, type: sql.NVarChar(255) },
password: { value: password, type: sql.NVarChar(255) },
userType: { value: userType, type: sql.NVarChar(255) },
computerName: { value: computerName, type: sql.NVarChar(255) }
}
} else {
sqlString = `
INSERT INTO ${tableName}
(UserName, Password, UserType)
VALUES (@username, @password, @userType)
`
params = {
username: { value: username, type: sql.NVarChar(255) },
password: { value: password, type: sql.NVarChar(255) },
userType: { value: userType, type: sql.NVarChar(255) }
}
}
await (dbService as SqlServerService).queryWithParams(sqlString, params)
return true
} else {
let sqlString: string
let params: unknown[]
if (computerName) {
sqlString = `
INSERT INTO ${tableName}
(UserName, Password, UserType, ComputerName)
VALUES (?, ?, ?, ?)
` `
params = [username, password, userType, computerName] params = [username, password, userType, computerName]
} else { } else {
sqlString = ` sqlString = `
INSERT INTO ${tableName} INSERT INTO ${tableName}
(UserName, Password, UserType) (UserName, Password, UserType)
VALUES (?, ?, ?) VALUES (${dialect.param(0)}, ${dialect.param(1)}, ${dialect.param(2)})
` `
params = [username, password, userType] params = [username, password, userType]
} }
await (dbService as MySqlService).query(sqlString, params) await dbService.query(sqlString, params)
return true return true
}
} catch (error) { } catch (error) {
logError(log, error, { logError(log, error, {
message: 'Create user failed', message: 'Create user failed',
operation: 'createUser', operation: 'createUser',
context: { username, userType, dbType: this.dbType } context: { username, userType, dbType: this.dbService?.type }
}) })
return false return false
} }
@@ -365,34 +246,21 @@ export class BIPUsersDAO {
try { try {
const dbService = await this.getDatabaseService() const dbService = await this.getDatabaseService()
const tableName = this.getTableName() const tableName = this.getTableName()
const dialect = this.getDialect()
if (this.dbType === 'sqlserver') {
const sqlString = ` const sqlString = `
UPDATE ${tableName} UPDATE ${tableName}
SET UserType = @userType SET UserType = ${dialect.param(0)}
WHERE UserName = @username WHERE UserName = ${dialect.param(1)}
` `
await (dbService as SqlServerService).queryWithParams(sqlString, { await dbService.query(sqlString, [userType, username])
username: { value: username, type: sql.NVarChar(255) },
userType: { value: userType, type: sql.NVarChar(255) }
})
return true return true
} else {
const sqlString = `
UPDATE ${tableName}
SET UserType = ?
WHERE UserName = ?
`
await (dbService as MySqlService).query(sqlString, [userType, username])
return true
}
} catch (error) { } catch (error) {
logError(log, error, { logError(log, error, {
message: 'Update user type failed', message: 'Update user type failed',
operation: 'updateUserType', operation: 'updateUserType',
context: { username, userType, dbType: this.dbType } context: { username, userType, dbType: this.dbService?.type }
}) })
return false return false
} }
@@ -408,34 +276,21 @@ export class BIPUsersDAO {
try { try {
const dbService = await this.getDatabaseService() const dbService = await this.getDatabaseService()
const tableName = this.getTableName() const tableName = this.getTableName()
const dialect = this.getDialect()
if (this.dbType === 'sqlserver') {
const sqlString = ` const sqlString = `
UPDATE ${tableName} UPDATE ${tableName}
SET Password = @newPassword SET Password = ${dialect.param(0)}
WHERE UserName = @username WHERE UserName = ${dialect.param(1)}
` `
await (dbService as SqlServerService).queryWithParams(sqlString, { await dbService.query(sqlString, [newPassword, username])
username: { value: username, type: sql.NVarChar(255) },
newPassword: { value: newPassword, type: sql.NVarChar(255) }
})
return true return true
} else {
const sqlString = `
UPDATE ${tableName}
SET Password = ?
WHERE UserName = ?
`
await (dbService as MySqlService).query(sqlString, [newPassword, username])
return true
}
} catch (error) { } catch (error) {
logError(log, error, { logError(log, error, {
message: 'Update password failed', message: 'Update password failed',
operation: 'updatePassword', operation: 'updatePassword',
context: { username, dbType: this.dbType } context: { username, dbType: this.dbService?.type }
}) })
return false return false
} }
@@ -450,31 +305,20 @@ export class BIPUsersDAO {
try { try {
const dbService = await this.getDatabaseService() const dbService = await this.getDatabaseService()
const tableName = this.getTableName() const tableName = this.getTableName()
const dialect = this.getDialect()
if (this.dbType === 'sqlserver') {
const sqlString = ` const sqlString = `
DELETE FROM ${tableName} DELETE FROM ${tableName}
WHERE UserName = @username WHERE UserName = ${dialect.param(0)}
` `
await (dbService as SqlServerService).queryWithParams(sqlString, { await dbService.query(sqlString, [username])
username: { value: username, type: sql.NVarChar(255) }
})
return true return true
} else {
const sqlString = `
DELETE FROM ${tableName}
WHERE UserName = ?
`
await (dbService as MySqlService).query(sqlString, [username])
return true
}
} catch (error) { } catch (error) {
logError(log, error, { logError(log, error, {
message: 'Delete user failed', message: 'Delete user failed',
operation: 'deleteUser', operation: 'deleteUser',
context: { username, dbType: this.dbType } context: { username, dbType: this.dbService?.type }
}) })
return false return false
} }
@@ -489,33 +333,21 @@ export class BIPUsersDAO {
try { try {
const dbService = await this.getDatabaseService() const dbService = await this.getDatabaseService()
const tableName = this.getTableName() const tableName = this.getTableName()
const dialect = this.getDialect()
if (this.dbType === 'sqlserver') {
const sqlString = ` const sqlString = `
SELECT COUNT(*) as count SELECT COUNT(*) as count
FROM ${tableName} FROM ${tableName}
WHERE UserName = @username WHERE UserName = ${dialect.param(0)}
` `
const result = await (dbService as SqlServerService).queryWithParams(sqlString, { const result = await dbService.query(sqlString, [username])
username: { value: username, type: sql.NVarChar(255) }
})
return result.rows.length > 0 && (result.rows[0].count as number) > 0 return result.rows.length > 0 && (result.rows[0].count as number) > 0
} else {
const sqlString = `
SELECT COUNT(*) as count
FROM ${tableName}
WHERE UserName = ?
`
const result = await (dbService as MySqlService).query(sqlString, [username])
return result.rows.length > 0 && (result.rows[0].count as number) > 0
}
} catch (error) { } catch (error) {
logError(log, error, { logError(log, error, {
message: 'Check user exists failed', message: 'Check user exists failed',
operation: 'userExists', operation: 'userExists',
context: { username, dbType: this.dbType } context: { username, dbType: this.dbService?.type }
}) })
return false return false
} }
@@ -533,18 +365,16 @@ export class BIPUsersDAO {
try { try {
const dbService = await this.getDatabaseService() const dbService = await this.getDatabaseService()
const tableName = this.getTableName() const tableName = this.getTableName()
const dialect = this.getDialect()
const cols = BIP_USERS_CONFIG.COLUMNS const cols = BIP_USERS_CONFIG.COLUMNS
if (this.dbType === 'sqlserver') {
const sqlString = ` const sqlString = `
SELECT ${cols.ERP_USERNAME}, ${cols.ERP_PASSWORD} SELECT ${cols.ERP_USERNAME}, ${cols.ERP_PASSWORD}
FROM ${tableName} FROM ${tableName}
WHERE UserName = @username WHERE UserName = ${dialect.param(0)}
` `
const result = await (dbService as SqlServerService).queryWithParams(sqlString, { const result = await dbService.query(sqlString, [username])
username: { value: username, type: sql.NVarChar(255) }
})
if (result.rows.length > 0) { if (result.rows.length > 0) {
const row = result.rows[0] const row = result.rows[0]
@@ -554,29 +384,11 @@ export class BIPUsersDAO {
} }
} }
return null return null
} else {
const sqlString = `
SELECT ${cols.ERP_USERNAME}, ${cols.ERP_PASSWORD}
FROM ${tableName}
WHERE UserName = ?
`
const result = await (dbService as MySqlService).query(sqlString, [username])
if (result.rows.length > 0) {
const row = result.rows[0]
return {
username: (row[cols.ERP_USERNAME] as string) || '',
password: (row[cols.ERP_PASSWORD] as string) || ''
}
}
return null
}
} catch (error) { } catch (error) {
logError(log, error, { logError(log, error, {
message: 'Get user ERP credentials failed', message: 'Get user ERP credentials failed',
operation: 'getUserErpCredentials', operation: 'getUserErpCredentials',
context: { username, dbType: this.dbType } context: { username, dbType: this.dbService?.type }
}) })
return null return null
} }
@@ -597,38 +409,23 @@ export class BIPUsersDAO {
try { try {
const dbService = await this.getDatabaseService() const dbService = await this.getDatabaseService()
const tableName = this.getTableName() const tableName = this.getTableName()
const dialect = this.getDialect()
const cols = BIP_USERS_CONFIG.COLUMNS const cols = BIP_USERS_CONFIG.COLUMNS
if (this.dbType === 'sqlserver') {
const sqlString = ` const sqlString = `
UPDATE ${tableName} UPDATE ${tableName}
SET ${cols.ERP_USERNAME} = @erpUsername, SET ${cols.ERP_USERNAME} = ${dialect.param(0)},
${cols.ERP_PASSWORD} = @erpPassword ${cols.ERP_PASSWORD} = ${dialect.param(1)}
WHERE UserName = @username WHERE UserName = ${dialect.param(2)}
` `
await (dbService as SqlServerService).queryWithParams(sqlString, { await dbService.query(sqlString, [erpUsername, erpPassword, username])
username: { value: username, type: sql.NVarChar(255) },
erpUsername: { value: erpUsername, type: sql.NVarChar(255) },
erpPassword: { value: erpPassword, type: sql.NVarChar(255) }
})
return true return true
} else {
const sqlString = `
UPDATE ${tableName}
SET ${cols.ERP_USERNAME} = ?,
${cols.ERP_PASSWORD} = ?
WHERE UserName = ?
`
await (dbService as MySqlService).query(sqlString, [erpUsername, erpPassword, username])
return true
}
} catch (error) { } catch (error) {
logError(log, error, { logError(log, error, {
message: 'Update user ERP credentials failed', message: 'Update user ERP credentials failed',
operation: 'updateUserErpCredentials', operation: 'updateUserErpCredentials',
context: { username, dbType: this.dbType } context: { username, dbType: this.dbService?.type }
}) })
return false return false
} }
@@ -656,10 +453,7 @@ export class BIPUsersDAO {
ORDER BY ${cols.USERNAME} ORDER BY ${cols.USERNAME}
` `
const result = const result = await dbService.query(sqlString)
this.dbType === 'sqlserver'
? await (dbService as SqlServerService).query(sqlString)
: await (dbService as MySqlService).query(sqlString)
return result.rows.map((row) => ({ return result.rows.map((row) => ({
username: row[cols.USERNAME] as string, username: row[cols.USERNAME] as string,
@@ -670,7 +464,7 @@ export class BIPUsersDAO {
logError(log, error, { logError(log, error, {
message: 'Get all users ERP config failed', message: 'Get all users ERP config failed',
operation: 'getAllUsersErpConfig', operation: 'getAllUsersErpConfig',
context: { dbType: this.dbType } context: { dbType: this.dbService?.type }
}) })
return [] return []
} }
@@ -680,13 +474,10 @@ export class BIPUsersDAO {
* Disconnect from database * Disconnect from database
*/ */
async disconnect(): Promise<void> { async disconnect(): Promise<void> {
if (this.mysqlService) { if (this.dbService) {
await this.mysqlService.disconnect() await this.dbService.disconnect()
this.mysqlService = null this.dbService = null
} this.dialect = null
if (this.sqlServerService) {
await this.sqlServerService.disconnect()
this.sqlServerService = null
} }
} }
} }