- 新增 HeartbeatBuffer 类,用于收集和去重 Kafka 心跳消息,并定期将数据刷新到数据库。 - 新增 HeartbeatDbManager 类,负责与 PostgreSQL 数据库的交互,支持批量 upsert 操作。 - 新增配置文件 config.js,支持从环境变量加载配置。 - 新增 Kafka 消费者模块,支持从 Kafka 中消费心跳消息。 - 新增 Redis 集成模块,支持将日志和心跳信息推送到 Redis。 - 新增心跳消息解析器,负责解析 Kafka 消息并提取心跳字段。 - 新增日志记录工具,支持不同级别的日志输出。 - 新增指标收集器,跟踪 Kafka 消息处理和数据库操作的指标。 - 新增单元测试,覆盖 HeartbeatBuffer 和 HeartbeatDbManager 的主要功能。 - 新增数据库表结构 SQL 文件,定义 room_status_moment_g5 表的结构。 - 配置 Vite 构建工具,支持 Node.js 环境的构建。
2.8 KiB
2.8 KiB
数据库规范 (Database Specification)
1. PostgreSQL G5 连接配置
1.1 连接信息
| 配置项 | 值 | 说明 |
|---|---|---|
| 主机 | 10.8.8.80 | G5 数据库服务器 |
| 端口 | 5434 | 非标准端口 |
| 数据库 | dbv6 | 目标数据库 |
| 用户 | (从环境变量) | POSTGRES_USER_G5 |
| 密码 | (从环密变量) | POSTGRES_PASSWORD_G5 |
1.2 表结构定义
CREATE TABLE IF NOT EXISTS public.room_status_moment_g5 (
hotel_id SMALLINT NOT NULL,
room_id TEXT NOT NULL,
device_id VARCHAR(255),
ts_ms BIGINT NOT NULL,
status SMALLINT DEFAULT 1,
-- 主键:确保同一房间只有一行
PRIMARY KEY (hotel_id, room_id)
);
2. Upsert 操作(批量插入/更新)
2.1 SQL 语句结构
INSERT INTO room_status_moment_g5
(hotel_id, room_id, device_id, ts_ms, status)
VALUES
($1::smallint, $2, $3, $4, 1),
($5::smallint, $6, $7, $8, 1),
...
ON CONFLICT (hotel_id, room_id)
DO UPDATE SET
ts_ms = EXCLUDED.ts_ms,
status = 1
WHERE EXCLUDED.ts_ms >= room_status_moment_g5.ts_ms;
2.2 类型转换设计决策
| 转换 | 理由 |
|---|---|
| hotel_id: string → ::smallint | G5 表使用 smallint;Kafka 送字符串避免精度问题 |
| room_id: string → text | 支持中文、特殊字符 |
| device_id: string → varchar | 与 G5 schema 兼容 |
| ts_ms: number → bigint | JavaScript number 足以覆盖 64-bit 整数范围 |
3. 批量处理实现
3.1 HeartbeatDbManager 类
位置: src/db/heartbeatDbManager.js
export class HeartbeatDbManager {
constructor(pool) {
this.pool = pool;
}
async upsertBatch(records) {
if (!records || records.length === 0) {
return; // 无需写入
}
// 构建参数化查询
const valueClauses = [];
const params = [];
records.forEach((record, idx) => {
const baseParamIdx = idx * 4;
valueClauses.push(
`($${baseParamIdx + 1}::smallint, $${baseParamIdx + 2}, $${baseParamIdx + 3}, $${baseParamIdx + 4}, 1)`
);
params.push(
record.hotel_id, // 字符串,::smallint 转换
record.room_id,
record.device_id,
record.ts_ms
);
});
const query = `
INSERT INTO room_status_moment_g5
(hotel_id, room_id, device_id, ts_ms, status)
VALUES
${valueClauses.join(',')}
ON CONFLICT (hotel_id, room_id) DO UPDATE SET
ts_ms = EXCLUDED.ts_ms,
status = 1
WHERE EXCLUDED.ts_ms >= room_status_moment_g5.ts_ms
`;
try {
const result = await this.pool.query(query, params);
logger.info(`Batch upsert: ${records.length} records, ${result.rowCount} rows affected`);
return result;
} catch (err) {
logger.error(`Batch upsert failed: ${err.message}`, err);
throw err;
}
}
}
上次修订: 2026-03-11