feat(heartbeat): 添加版本号字段并处理亮度值-1为NULL
- 在心跳事件表中新增 version 字段,用于存储版本号信息 - 将 bright_g 字段的 -1 值映射为数据库中的 NULL,避免语义混淆 - 更新相关文档、数据库迁移脚本和测试用例
This commit is contained in:
@@ -34,7 +34,8 @@
|
||||
| device_count | int2 | 是 | 设备数量/上报设备数量(语义待确认) |
|
||||
| comm_seq | int4 | 是 | 通讯序号(语义待确认) |
|
||||
| insert_card | int2 | 否 | 是否插卡(整数;可为空;不建索引) |
|
||||
| bright_g | int2 | 否 | 全局亮度值(整数;可为空;不建索引) |
|
||||
| bright_g | int2 | 否 | 全局亮度值(整数;可为空;若值为 -1 则存 NULL;不建索引) |
|
||||
| version | int2 | 否 | 版本号(int2;可为空;不建索引) |
|
||||
| elec_address | text[] | 否 | 电力设备地址数组(与 voltage[] 等按下标对齐) |
|
||||
| voltage | double precision[] | 否 | 电压数组 |
|
||||
| ampere | double precision[] | 否 | 电流数组 |
|
||||
|
||||
@@ -40,7 +40,8 @@
|
||||
| electricity | array<object> | [{"address":"add11","voltage":3.2,...}] | 电力设备数组(按原始顺序拆列落库为数组列) |
|
||||
| air_conditioner | array<object> | [{"address":"ac1","state":1,...}] | 空调设备数组(按原始顺序拆列落库为数组列) |
|
||||
| insert_card | number/int | 1 | 是否插卡(整数,可为空) |
|
||||
| bright_g | number/int | 80 | 全局亮度值(整数,可为空) |
|
||||
| bright_g | number/int | 80 | 全局亮度值(整数,可为空;若为 -1 则落库为 null) |
|
||||
| version | number/int | 1 | 版本号(int2/short,可为空) |
|
||||
|
||||
## 4. JSON 示例
|
||||
```json
|
||||
@@ -60,6 +61,7 @@
|
||||
"comm_seq": 7,
|
||||
"insert_card": 1,
|
||||
"bright_g": 80,
|
||||
"version": "1.3.0",
|
||||
"electricity": [
|
||||
{
|
||||
"address": "add11",
|
||||
|
||||
@@ -30,6 +30,7 @@ CREATE TABLE IF NOT EXISTS heartbeat.heartbeat_events (
|
||||
|
||||
insert_card int2,
|
||||
bright_g int2,
|
||||
version int4,
|
||||
|
||||
elec_address text[],
|
||||
air_address text[],
|
||||
@@ -85,6 +86,7 @@ ALTER TABLE heartbeat.heartbeat_events ADD COLUMN IF NOT EXISTS now_temp int2[];
|
||||
ALTER TABLE heartbeat.heartbeat_events ADD COLUMN IF NOT EXISTS solenoid_valve int2[];
|
||||
ALTER TABLE heartbeat.heartbeat_events ADD COLUMN IF NOT EXISTS insert_card int2;
|
||||
ALTER TABLE heartbeat.heartbeat_events ADD COLUMN IF NOT EXISTS bright_g int2;
|
||||
ALTER TABLE heartbeat.heartbeat_events ADD COLUMN IF NOT EXISTS version int2;
|
||||
|
||||
-- 指定索引
|
||||
CREATE INDEX IF NOT EXISTS idx_heartbeat_events_hotel_id ON heartbeat.heartbeat_events (hotel_id);
|
||||
|
||||
@@ -420,6 +420,7 @@ class DatabaseManager {
|
||||
'comm_seq',
|
||||
'insert_card',
|
||||
'bright_g',
|
||||
'version',
|
||||
'elec_address',
|
||||
'air_address',
|
||||
'voltage',
|
||||
@@ -453,7 +454,8 @@ class DatabaseManager {
|
||||
e.device_count,
|
||||
e.comm_seq,
|
||||
e.insert_card ?? null,
|
||||
e.bright_g ?? null,
|
||||
(e.bright_g === -1 || e.bright_g === '-1') ? null : (e.bright_g ?? null),
|
||||
e.version ?? null,
|
||||
Array.isArray(e.elec_address) ? e.elec_address : null,
|
||||
Array.isArray(e.air_address) ? e.air_address : null,
|
||||
Array.isArray(e.voltage) ? e.voltage : null,
|
||||
|
||||
@@ -514,6 +514,7 @@ class HeartbeatProcessor {
|
||||
comm_seq: pick(['comm_seq', 'commSeq', 'CommSeq']),
|
||||
insert_card: pick(['insert_card', 'insertCard', 'InsertCard']),
|
||||
bright_g: pick(['bright_g', 'brightG', 'BrightG']),
|
||||
version: pick(['version', 'Version', 'ver', 'Ver']),
|
||||
extra: pick(['extra', 'Extra']),
|
||||
electricity: pick(['electricity', 'Electricity']),
|
||||
air_conditioner: pick(['air_conditioner', 'airConditioner', 'AirConditioner']),
|
||||
@@ -566,7 +567,9 @@ class HeartbeatProcessor {
|
||||
normalized.device_count = toIntOrUndefined(normalized.device_count);
|
||||
normalized.comm_seq = toIntOrUndefined(normalized.comm_seq);
|
||||
normalized.insert_card = toIntOrUndefined(normalized.insert_card);
|
||||
normalized.bright_g = toIntOrUndefined(normalized.bright_g);
|
||||
const bg = toIntOrUndefined(normalized.bright_g);
|
||||
normalized.bright_g = bg === -1 ? undefined : bg;
|
||||
normalized.version = toIntOrUndefined(normalized.version);
|
||||
|
||||
// 其余未知字段塞进 extra(避免丢信息),但不覆盖显式 extra
|
||||
if (!normalized.extra || typeof normalized.extra !== 'object') {
|
||||
@@ -591,6 +594,7 @@ class HeartbeatProcessor {
|
||||
'comm_seq','commSeq','CommSeq',
|
||||
'insert_card','insertCard','InsertCard',
|
||||
'bright_g','brightG','BrightG',
|
||||
'version','Version','ver','Ver',
|
||||
'extra','Extra',
|
||||
'electricity','Electricity',
|
||||
'air_conditioner','airConditioner','AirConditioner'
|
||||
|
||||
@@ -24,6 +24,36 @@ describe('HeartbeatProcessor smoke', () => {
|
||||
const payload = { tsMs: 1700000000123, hotelId: 1, roomId: 2, deviceId: 'd', ip: '127.0.0.1', powerState: 1, guestType: 0, cardlessState: 0, serviceMask: 1, pmsState: 1, carbonState: 0, deviceCount: 1, commSeq: 1 };
|
||||
assert.equal(processor.validateData(payload), true);
|
||||
});
|
||||
|
||||
it('parses version field', () => {
|
||||
const processor = new HeartbeatProcessor(
|
||||
{ batchSize: 100, batchTimeout: 1000 },
|
||||
{ insertHeartbeatEvents: async () => {} }
|
||||
);
|
||||
const payload = { version: 10203 };
|
||||
const normalized = processor.normalizeHeartbeat(payload);
|
||||
assert.equal(normalized.version, 10203);
|
||||
});
|
||||
|
||||
it('treats bright_g -1 as undefined', () => {
|
||||
const processor = new HeartbeatProcessor(
|
||||
{ batchSize: 100, batchTimeout: 1000 },
|
||||
{ insertHeartbeatEvents: async () => {} }
|
||||
);
|
||||
const payload = { bright_g: -1 };
|
||||
const normalized = processor.normalizeHeartbeat(payload);
|
||||
assert.equal(normalized.bright_g, undefined);
|
||||
});
|
||||
|
||||
it('treats bright_g normal value as number', () => {
|
||||
const processor = new HeartbeatProcessor(
|
||||
{ batchSize: 100, batchTimeout: 1000 },
|
||||
{ insertHeartbeatEvents: async () => {} }
|
||||
);
|
||||
const payload = { bright_g: 50 };
|
||||
const normalized = processor.normalizeHeartbeat(payload);
|
||||
assert.equal(normalized.bright_g, 50);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RedisIntegration protocol', () => {
|
||||
|
||||
Reference in New Issue
Block a user