Files
Web_BLS_OldRcu_Heartbeat_Se…/bls-oldrcu-heartbeat-backend/spec/database.md

117 lines
2.8 KiB
Markdown
Raw Normal View History

# 数据库规范 (Database Specification)
## 1. PostgreSQL G5 连接配置
### 1.1 连接信息
| 配置项 | 值 | 说明 |
|--------|-----|------|
| 主机 | 10.8.8.80 | G5 数据库服务器 |
| 端口 | 5434 | 非标准端口 |
| 数据库 | dbv6 | 目标数据库 |
| 用户 | (从环境变量) | POSTGRES_USER_G5 |
| 密码 | (从环密变量) | POSTGRES_PASSWORD_G5 |
### 1.2 表结构定义
```sql
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 语句结构
```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`
```javascript
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