Files
Web_BLS_OldRcu_Heartbeat_Se…/bls-oldrcu-heartbeat-backend/spec/database.md
XuJiacheng e45d14b720 feat: 实现心跳消息处理模块
- 新增 HeartbeatBuffer 类,用于收集和去重 Kafka 心跳消息,并定期将数据刷新到数据库。
- 新增 HeartbeatDbManager 类,负责与 PostgreSQL 数据库的交互,支持批量 upsert 操作。
- 新增配置文件 config.js,支持从环境变量加载配置。
- 新增 Kafka 消费者模块,支持从 Kafka 中消费心跳消息。
- 新增 Redis 集成模块,支持将日志和心跳信息推送到 Redis。
- 新增心跳消息解析器,负责解析 Kafka 消息并提取心跳字段。
- 新增日志记录工具,支持不同级别的日志输出。
- 新增指标收集器,跟踪 Kafka 消息处理和数据库操作的指标。
- 新增单元测试,覆盖 HeartbeatBuffer 和 HeartbeatDbManager 的主要功能。
- 新增数据库表结构 SQL 文件,定义 room_status_moment_g5 表的结构。
- 配置 Vite 构建工具,支持 Node.js 环境的构建。
2026-03-12 14:11:02 +08:00

2.8 KiB
Raw Blame History

数据库规范 (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 表使用 smallintKafka 送字符串避免精度问题
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