refactor: 移除运行时数据库初始化与分区维护

- 删除了服务启动阶段的数据库初始化逻辑,包括创建数据库、表和分区的相关代码。
- 移除了定时分区维护任务,确保服务职责更清晰。
- 更新了数据库分区策略,明确分区由外部脚本管理,服务不再自动创建缺失分区。
- 修改了相关文档,确保数据库结构与分区维护的责任转移到 `SQL_Script/` 目录下的外部脚本。
- 更新了需求和场景,确保符合新的设计规范。
This commit is contained in:
2026-03-04 11:52:12 +08:00
parent 3d80ad8710
commit 33c9bf0e07
20 changed files with 257 additions and 663 deletions

View File

@@ -0,0 +1,6 @@
-- Replace {{DATABASE_NAME}} before execution
-- Requires psql (uses \gexec)
SELECT format('CREATE DATABASE %I', '{{DATABASE_NAME}}')
WHERE NOT EXISTS (
SELECT 1 FROM pg_database WHERE datname = '{{DATABASE_NAME}}'
)\gexec;

View File

@@ -0,0 +1,12 @@
-- Replace placeholders before execution:
-- {{SCHEMA_NAME}} default: onoffline
-- {{TABLE_NAME}} default: onoffline_record
-- {{PARTITION_SUFFIX}} format: YYYYMMDD (example: 20260304)
-- {{START_TS_MS}} unix ms at 00:00:00.000
-- {{END_TS_MS}} unix ms at next day 00:00:00.000
-- {{TABLESPACE_CLAUSE}} either empty string or: TABLESPACE ts_hot
CREATE TABLE IF NOT EXISTS {{SCHEMA_NAME}}.{{TABLE_NAME}}_{{PARTITION_SUFFIX}}
PARTITION OF {{SCHEMA_NAME}}.{{TABLE_NAME}}
FOR VALUES FROM ({{START_TS_MS}}) TO ({{END_TS_MS}})
{{TABLESPACE_CLAUSE}};

View File

@@ -0,0 +1,27 @@
-- Replace placeholders before execution:
-- {{SCHEMA_NAME}} default: onoffline
-- {{TABLE_NAME}} default: onoffline_record
CREATE SCHEMA IF NOT EXISTS {{SCHEMA_NAME}};
CREATE TABLE IF NOT EXISTS {{SCHEMA_NAME}}.{{TABLE_NAME}} (
guid TEXT NOT NULL,
ts_ms BIGINT NOT NULL,
write_ts_ms BIGINT NOT NULL,
hotel_id SMALLINT,
mac TEXT,
device_id TEXT,
room_id TEXT,
ip TEXT,
current_status TEXT,
launcher_version TEXT,
reboot_reason TEXT,
PRIMARY KEY (ts_ms, guid)
) PARTITION BY RANGE (ts_ms);
CREATE INDEX IF NOT EXISTS idx_{{TABLE_NAME}}_guid ON {{SCHEMA_NAME}}.{{TABLE_NAME}} (guid);
CREATE INDEX IF NOT EXISTS idx_{{TABLE_NAME}}_device_id ON {{SCHEMA_NAME}}.{{TABLE_NAME}} (device_id);
CREATE INDEX IF NOT EXISTS idx_{{TABLE_NAME}}_hotel_id ON {{SCHEMA_NAME}}.{{TABLE_NAME}} (hotel_id);
CREATE INDEX IF NOT EXISTS idx_{{TABLE_NAME}}_mac ON {{SCHEMA_NAME}}.{{TABLE_NAME}} (mac);
CREATE INDEX IF NOT EXISTS idx_{{TABLE_NAME}}_room_id ON {{SCHEMA_NAME}}.{{TABLE_NAME}} (room_id);
CREATE INDEX IF NOT EXISTS idx_{{TABLE_NAME}}_status ON {{SCHEMA_NAME}}.{{TABLE_NAME}} (current_status);

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env node
import fs from 'fs';
import path from 'path';
const args = Object.fromEntries(
process.argv.slice(2).map((arg) => {
const [key, value] = arg.split('=');
return [key.replace(/^--/, ''), value];
})
);
const database = args.database || 'log_platform';
const schema = args.schema || 'onoffline';
const table = args.table || 'onoffline_record';
const output = args.output;
const sql = `SELECT format('CREATE DATABASE %I', '${database}')\nWHERE NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = '${database}')\\gexec;\n\nCREATE SCHEMA IF NOT EXISTS ${schema};\n\nCREATE TABLE IF NOT EXISTS ${schema}.${table} (\n guid TEXT NOT NULL,\n ts_ms BIGINT NOT NULL,\n write_ts_ms BIGINT NOT NULL,\n hotel_id SMALLINT,\n mac TEXT,\n device_id TEXT,\n room_id TEXT,\n ip TEXT,\n current_status TEXT,\n launcher_version TEXT,\n reboot_reason TEXT,\n PRIMARY KEY (ts_ms, guid)\n) PARTITION BY RANGE (ts_ms);\n\nCREATE INDEX IF NOT EXISTS idx_${table}_guid ON ${schema}.${table} (guid);\nCREATE INDEX IF NOT EXISTS idx_${table}_device_id ON ${schema}.${table} (device_id);\nCREATE INDEX IF NOT EXISTS idx_${table}_hotel_id ON ${schema}.${table} (hotel_id);\nCREATE INDEX IF NOT EXISTS idx_${table}_mac ON ${schema}.${table} (mac);\nCREATE INDEX IF NOT EXISTS idx_${table}_room_id ON ${schema}.${table} (room_id);\nCREATE INDEX IF NOT EXISTS idx_${table}_status ON ${schema}.${table} (current_status);\n`;
if (output) {
const outputPath = path.resolve(output);
fs.writeFileSync(outputPath, sql, 'utf8');
console.log(`Init SQL written to ${outputPath}`);
} else {
process.stdout.write(sql);
}

View File

@@ -0,0 +1,64 @@
#!/usr/bin/env node
import fs from 'fs';
import path from 'path';
const args = Object.fromEntries(
process.argv.slice(2).map((arg) => {
const [key, value] = arg.split('=');
return [key.replace(/^--/, ''), value];
})
);
const schema = args.schema || 'onoffline';
const table = args.table || 'onoffline_record';
const start = args.start;
const days = Number(args.days || '30');
const tablespace = args.tablespace || '';
const output = args.output;
if (!start) {
console.error('Missing required argument: --start=YYYY-MM-DD');
process.exit(1);
}
if (!Number.isFinite(days) || days <= 0) {
console.error('Invalid --days value. It must be a positive integer.');
process.exit(1);
}
const base = new Date(`${start}T00:00:00`);
if (Number.isNaN(base.getTime())) {
console.error('Invalid start date. Use format YYYY-MM-DD');
process.exit(1);
}
const statements = [];
for (let i = 0; i < days; i += 1) {
const date = new Date(base);
date.setDate(base.getDate() + i);
const startMs = date.getTime();
const endDate = new Date(date);
endDate.setDate(endDate.getDate() + 1);
const endMs = endDate.getTime();
const yyyy = date.getFullYear();
const mm = String(date.getMonth() + 1).padStart(2, '0');
const dd = String(date.getDate()).padStart(2, '0');
const suffix = `${yyyy}${mm}${dd}`;
const tablespaceClause = tablespace ? ` TABLESPACE ${tablespace}` : '';
statements.push(
`CREATE TABLE IF NOT EXISTS ${schema}.${table}_${suffix}\nPARTITION OF ${schema}.${table}\nFOR VALUES FROM (${startMs}) TO (${endMs})${tablespaceClause};`
);
}
const sql = `${statements.join('\n\n')}\n`;
if (output) {
const outputPath = path.resolve(output);
fs.writeFileSync(outputPath, sql, 'utf8');
console.log(`Partition range SQL written to ${outputPath}`);
} else {
process.stdout.write(sql);
}

View File

@@ -0,0 +1,49 @@
#!/usr/bin/env node
import fs from 'fs';
import path from 'path';
const args = Object.fromEntries(
process.argv.slice(2).map((arg) => {
const [key, value] = arg.split('=');
return [key.replace(/^--/, ''), value];
})
);
const schema = args.schema || 'onoffline';
const table = args.table || 'onoffline_record';
const dateInput = args.date;
const tablespace = args.tablespace || '';
const output = args.output;
if (!dateInput) {
console.error('Missing required argument: --date=YYYY-MM-DD');
process.exit(1);
}
const date = new Date(`${dateInput}T00:00:00`);
if (Number.isNaN(date.getTime())) {
console.error('Invalid date. Use format YYYY-MM-DD');
process.exit(1);
}
const startMs = date.getTime();
const endDate = new Date(date);
endDate.setDate(endDate.getDate() + 1);
const endMs = endDate.getTime();
const yyyy = date.getFullYear();
const mm = String(date.getMonth() + 1).padStart(2, '0');
const dd = String(date.getDate()).padStart(2, '0');
const suffix = `${yyyy}${mm}${dd}`;
const tablespaceClause = tablespace ? `TABLESPACE ${tablespace}` : '';
const sql = `CREATE TABLE IF NOT EXISTS ${schema}.${table}_${suffix}\nPARTITION OF ${schema}.${table}\nFOR VALUES FROM (${startMs}) TO (${endMs})\n${tablespaceClause};\n`;
if (output) {
const outputPath = path.resolve(output);
fs.writeFileSync(outputPath, sql, 'utf8');
console.log(`Partition SQL written to ${outputPath}`);
} else {
process.stdout.write(sql);
}