refactor(db): use SqlDialect in ExtractorOperationHistoryDAO

Replace all isSqlServer checks, buildPlaceholders, and hardcoded table names
with the SqlDialect abstraction. The dialect now handles parameter placeholders,
table name quoting, current timestamp functions, and pagination across all
supported database types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Misaka
2026-04-05 10:25:38 +08:00
parent 9556891dea
commit e97ec63433

View File

@@ -10,6 +10,7 @@
*/
import { create, type IDatabaseService } from './index'
import { createDialect, type SqlDialect } from './dialects'
import { createLogger, run, getRequestId, trackDuration } from '../logger'
import type {
OperationHistoryRecord,
@@ -36,8 +37,6 @@ function formatDateTime(value: unknown): string {
* Configuration for ExtractorOperationHistory table
*/
export const EXTRACTOR_OPERATION_HISTORY_CONFIG = {
TABLE_NAME_SQLSERVER: '[dbo].[ExtractorOperationHistory]',
TABLE_NAME_MYSQL: 'dbo_ExtractorOperationHistory',
COLUMNS: {
ID: 'ID',
BATCH_ID: 'BatchId',
@@ -57,15 +56,20 @@ export const EXTRACTOR_OPERATION_HISTORY_CONFIG = {
*/
export class ExtractorOperationHistoryDAO {
private dbService: IDatabaseService | null = null
private dialect: SqlDialect | null = null
private getDialect(): SqlDialect {
if (!this.dialect) {
this.dialect = createDialect(this.dbService!.type)
}
return this.dialect
}
/**
* Get the appropriate table name based on database type
*/
private getTableName(): string {
const isSqlServer = this.dbService?.type === 'sqlserver'
return isSqlServer
? EXTRACTOR_OPERATION_HISTORY_CONFIG.TABLE_NAME_SQLSERVER
: EXTRACTOR_OPERATION_HISTORY_CONFIG.TABLE_NAME_MYSQL
return this.getDialect().quoteTableName('dbo', 'ExtractorOperationHistory')
}
/**
@@ -80,14 +84,6 @@ export class ExtractorOperationHistoryDAO {
return this.dbService
}
/**
* Build placeholders for IN clause based on database type
*/
private buildPlaceholders(count: number, isSqlServer: boolean): string {
return isSqlServer
? Array.from({ length: count }, (_, idx) => `@p${idx}`).join(',')
: Array.from({ length: count }, () => '?').join(',')
}
// ==================== INSERT ====================
@@ -119,7 +115,7 @@ export class ExtractorOperationHistoryDAO {
try {
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const isSqlServer = dbService.type === 'sqlserver'
const dialect = this.getDialect()
log.info('Batch records insertion started', {
tableName,
@@ -133,49 +129,26 @@ export class ExtractorOperationHistoryDAO {
for (const record of records) {
try {
if (isSqlServer) {
const sqlString = `
INSERT INTO ${tableName}
(BatchId, UserId, Username, ProductionId, OrderNumber, OperationTime, Status)
VALUES
(@p0, @p1, @p2, @p3, @p4, GETDATE(), 'pending')
`
await trackDuration(
async () =>
await dbService.query(sqlString, [
batchId,
userId,
username,
record.productionId || null,
record.orderNumber
]),
{
operationName: 'ExtractorOperationHistoryDAO.insertBatchRecords',
context: { tableName, operationType: 'INSERT', batchId }
}
)
} else {
const sqlString = `
INSERT INTO ${tableName}
(BatchId, UserId, Username, ProductionId, OrderNumber, OperationTime, Status)
VALUES
(?, ?, ?, ?, ?, NOW(), 'pending')
`
await trackDuration(
async () =>
await dbService.query(sqlString, [
batchId,
userId,
username,
record.productionId || null,
record.orderNumber
]),
{
operationName: 'ExtractorOperationHistoryDAO.insertBatchRecords',
context: { tableName, operationType: 'INSERT', batchId }
}
)
}
const sqlString = `
INSERT INTO ${tableName}
(BatchId, UserId, Username, ProductionId, OrderNumber, OperationTime, Status)
VALUES
(${dialect.param(0)}, ${dialect.param(1)}, ${dialect.param(2)}, ${dialect.param(3)}, ${dialect.param(4)}, ${dialect.currentTimestamp()}, 'pending')
`
await trackDuration(
async () =>
await dbService.query(sqlString, [
batchId,
userId,
username,
record.productionId || null,
record.orderNumber
]),
{
operationName: 'ExtractorOperationHistoryDAO.insertBatchRecords',
context: { tableName, operationType: 'INSERT', batchId }
}
)
} catch (error) {
log.error('Error inserting individual record', {
tableName,
@@ -221,12 +194,12 @@ export class ExtractorOperationHistoryDAO {
try {
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const isSqlServer = dbService.type === 'sqlserver'
const dialect = this.getDialect()
const sqlString = `
UPDATE ${tableName}
SET Status = ${isSqlServer ? '@p0' : '?'}
WHERE BatchId = ${isSqlServer ? '@p1' : '?'}
SET Status = ${dialect.param(0)}
WHERE BatchId = ${dialect.param(1)}
`
const params = [status, batchId]
@@ -274,7 +247,7 @@ export class ExtractorOperationHistoryDAO {
try {
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const isSqlServer = dbService.type === 'sqlserver'
const dialect = this.getDialect()
let sqlString: string
let params: (string | number | null)[]
@@ -282,20 +255,20 @@ export class ExtractorOperationHistoryDAO {
if (recordCount !== undefined) {
sqlString = `
UPDATE ${tableName}
SET Status = ${isSqlServer ? '@p0' : '?'},
ErrorMessage = ${isSqlServer ? '@p1' : '?'},
RecordCount = ${isSqlServer ? '@p2' : '?'}
WHERE BatchId = ${isSqlServer ? '@p3' : '?'}
AND OrderNumber = ${isSqlServer ? '@p4' : '?'}
SET Status = ${dialect.param(0)},
ErrorMessage = ${dialect.param(1)},
RecordCount = ${dialect.param(2)}
WHERE BatchId = ${dialect.param(3)}
AND OrderNumber = ${dialect.param(4)}
`
params = [status, errorMessage || null, recordCount, batchId, orderNumber]
} else {
sqlString = `
UPDATE ${tableName}
SET Status = ${isSqlServer ? '@p0' : '?'},
ErrorMessage = ${isSqlServer ? '@p1' : '?'}
WHERE BatchId = ${isSqlServer ? '@p2' : '?'}
AND OrderNumber = ${isSqlServer ? '@p3' : '?'}
SET Status = ${dialect.param(0)},
ErrorMessage = ${dialect.param(1)}
WHERE BatchId = ${dialect.param(2)}
AND OrderNumber = ${dialect.param(3)}
`
params = [status, errorMessage || null, batchId, orderNumber]
}
@@ -331,7 +304,7 @@ export class ExtractorOperationHistoryDAO {
try {
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const isSqlServer = dbService.type === 'sqlserver'
const dialect = this.getDialect()
let sqlString = `
SELECT
@@ -350,11 +323,10 @@ export class ExtractorOperationHistoryDAO {
const params: (number | string)[] = []
if (userId !== undefined) {
sqlString += isSqlServer ? ` WHERE UserId = @p0 ` : ` WHERE UserId = ? `
sqlString += ` WHERE UserId = ${dialect.param(params.length)} `
params.push(userId)
} else if (options?.usernames && options.usernames.length > 0) {
const placeholders = this.buildPlaceholders(options.usernames.length, isSqlServer)
sqlString += ` WHERE Username IN (${placeholders}) `
sqlString += ` WHERE Username IN (${dialect.params(options.usernames.length)}) `
params.push(...options.usernames)
}
@@ -367,24 +339,19 @@ export class ExtractorOperationHistoryDAO {
const safeLimit = Math.floor(options.limit)
const safeOffset = options.offset !== undefined ? Math.floor(options.offset) : undefined
if (isSqlServer) {
const offsetIndex = params.length
const result = dialect.paginate({
sql: sqlString,
limit: safeLimit,
offset: safeOffset,
paramIndex: params.length
})
sqlString = result.sql
if (dialect.dbType === 'sqlserver') {
if (safeOffset !== undefined) {
params.push(safeOffset)
}
params.push(safeLimit)
if (safeOffset !== undefined) {
sqlString += ` OFFSET @p${offsetIndex} ROWS FETCH NEXT @p${offsetIndex + 1} ROWS ONLY`
} else {
sqlString += ` OFFSET 0 ROWS FETCH NEXT @p${offsetIndex} ROWS ONLY`
}
} else {
if (safeOffset !== undefined) {
sqlString += ` LIMIT ${safeLimit} OFFSET ${safeOffset}`
} else {
sqlString += ` LIMIT ${safeLimit}`
}
}
}
@@ -425,9 +392,9 @@ export class ExtractorOperationHistoryDAO {
try {
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const isSqlServer = dbService.type === 'sqlserver'
const dialect = this.getDialect()
const placeholder = isSqlServer ? '@p0' : '?'
const placeholder = dialect.param(0)
const sqlString = `
SELECT
ID,
@@ -483,9 +450,9 @@ export class ExtractorOperationHistoryDAO {
try {
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const isSqlServer = dbService.type === 'sqlserver'
const dialect = this.getDialect()
const placeholder = isSqlServer ? '@p0' : '?'
const placeholder = dialect.param(0)
const sqlString = `
SELECT
BatchId,
@@ -554,7 +521,7 @@ export class ExtractorOperationHistoryDAO {
try {
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const isSqlServer = dbService.type === 'sqlserver'
const dialect = this.getDialect()
// First check if the batch exists and if the user has permission
const batchStats = await this.getBatchStats(batchId)
@@ -569,7 +536,7 @@ export class ExtractorOperationHistoryDAO {
}
// Delete the batch
const placeholder = isSqlServer ? '@p0' : '?'
const placeholder = dialect.param(0)
const sqlString = `
DELETE FROM ${tableName}
WHERE BatchId = ${placeholder}
@@ -612,9 +579,9 @@ export class ExtractorOperationHistoryDAO {
try {
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const isSqlServer = dbService.type === 'sqlserver'
const dialect = this.getDialect()
const placeholder = isSqlServer ? '@p0' : '?'
const placeholder = dialect.param(0)
const sqlString = `
DELETE FROM ${tableName}
WHERE UserId = ${placeholder}
@@ -648,9 +615,9 @@ export class ExtractorOperationHistoryDAO {
try {
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const isSqlServer = dbService.type === 'sqlserver'
const dialect = this.getDialect()
const placeholder = isSqlServer ? '@p0' : '?'
const placeholder = dialect.param(0)
const sqlString = `
SELECT COUNT(*) as count
FROM ${tableName}
@@ -684,7 +651,7 @@ export class ExtractorOperationHistoryDAO {
try {
const dbService = await this.getDatabaseService()
const tableName = this.getTableName()
const isSqlServer = dbService.type === 'sqlserver'
const dialect = this.getDialect()
let sqlString = `
SELECT COUNT(DISTINCT BatchId) as count
@@ -694,11 +661,10 @@ export class ExtractorOperationHistoryDAO {
const params: (number | string)[] = []
if (userId !== undefined) {
sqlString += isSqlServer ? ` WHERE UserId = @p0 ` : ` WHERE UserId = ? `
sqlString += ` WHERE UserId = ${dialect.param(params.length)} `
params.push(userId)
} else if (usernames && usernames.length > 0) {
const placeholders = this.buildPlaceholders(usernames.length, isSqlServer)
sqlString += ` WHERE Username IN (${placeholders}) `
sqlString += ` WHERE Username IN (${dialect.params(usernames.length)}) `
params.push(...usernames)
}
@@ -725,6 +691,7 @@ export class ExtractorOperationHistoryDAO {
if (this.dbService) {
await this.dbService.disconnect()
this.dbService = null
this.dialect = null
}
}
}