优化接单平台工单查询性能
This commit is contained in:
@@ -10,7 +10,12 @@ import { fileURLToPath } from 'node:url'
|
||||
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url))
|
||||
const BACKEND_ROOT = path.resolve(SCRIPT_DIR, '..')
|
||||
|
||||
const checks: Array<{ name: string; file: string; patterns: string[] }> = [
|
||||
const checks: Array<{
|
||||
name: string
|
||||
file: string
|
||||
patterns?: string[]
|
||||
forbiddenPatterns?: string[]
|
||||
}> = [
|
||||
{
|
||||
name: '慢查询阈值配置',
|
||||
file: path.join(BACKEND_ROOT, 'src/config/env-overrides.ts'),
|
||||
@@ -35,6 +40,12 @@ const checks: Array<{ name: string; file: string; patterns: string[] }> = [
|
||||
file: path.join(BACKEND_ROOT, 'src/repositories/worker-platform/work-order-share-repo.ts'),
|
||||
patterns: ['workOrderIds: number[]', 'wos.work_order_id = ANY($2::bigint[])'],
|
||||
},
|
||||
{
|
||||
name: '接单工单宽详情分页禁止窗口总数',
|
||||
file: path.join(BACKEND_ROOT, 'src/repositories/worker-platform/work-order-query-repo.ts'),
|
||||
patterns: ['countWorkOrdersWithWhere', 'includeTotal'],
|
||||
forbiddenPatterns: ['COUNT(*) OVER' + '()', 'LIST_WORK_ORDER_SELECT'],
|
||||
},
|
||||
{
|
||||
name: '数据保留清理扫描索引',
|
||||
file: path.join(BACKEND_ROOT, 'src/db/migrations/067_data_retention_indexes.sql'),
|
||||
@@ -55,10 +66,27 @@ const checks: Array<{ name: string; file: string; patterns: string[] }> = [
|
||||
"WHERE event_type LIKE 'timeout_%'",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: '接单工单搜索表达式索引重建',
|
||||
file: path.join(BACKEND_ROOT, 'src/db/migrations/073_rebuild_work_order_search_index.sql'),
|
||||
patterns: [
|
||||
'DROP INDEX IF EXISTS idx_work_orders_search_text_trgm',
|
||||
'CREATE INDEX idx_work_orders_search_text_trgm',
|
||||
"material_json->'collect'->'fields'->>'gameId'",
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const failures: string[] = []
|
||||
|
||||
// 窗口总数会在 LIMIT/OFFSET 之前消费全部候选行;全后端 SQL 都禁止引入该写法。
|
||||
const countWindowPattern = /COUNT\s*\(\s*\*\s*\)\s*OVER\s*\(/i
|
||||
for (const file of listSourceFiles(path.join(BACKEND_ROOT, 'src'))) {
|
||||
if (countWindowPattern.test(fs.readFileSync(file, 'utf8'))) {
|
||||
failures.push(`禁止 COUNT(*) OVER() 窗口分页: ${file}`)
|
||||
}
|
||||
}
|
||||
|
||||
// 已应用的历史迁移必须保持字节不变,所有结构或数据修复都必须新增迁移文件。
|
||||
try {
|
||||
const diffOutputs = [
|
||||
@@ -89,11 +117,16 @@ for (const check of checks) {
|
||||
continue
|
||||
}
|
||||
const source = fs.readFileSync(check.file, 'utf8')
|
||||
for (const pattern of check.patterns) {
|
||||
for (const pattern of check.patterns || []) {
|
||||
if (!source.includes(pattern)) {
|
||||
failures.push(`${check.name}: 缺少约束 ${pattern} (${check.file})`)
|
||||
}
|
||||
}
|
||||
for (const pattern of check.forbiddenPatterns || []) {
|
||||
if (source.includes(pattern)) {
|
||||
failures.push(`${check.name}: 出现禁止 SQL ${pattern} (${check.file})`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failures.length > 0) {
|
||||
@@ -103,3 +136,16 @@ if (failures.length > 0) {
|
||||
} else {
|
||||
console.info('[sql-guard] SQL 约束检查通过')
|
||||
}
|
||||
|
||||
function listSourceFiles(directory: string): string[] {
|
||||
const files: string[] = []
|
||||
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
||||
const file = path.join(directory, entry.name)
|
||||
if (entry.isDirectory()) {
|
||||
files.push(...listSourceFiles(file))
|
||||
} else if (entry.isFile() && /\.(?:ts|sql)$/.test(entry.name)) {
|
||||
files.push(file)
|
||||
}
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user