feat: 添加 Kafka 消费者和消息处理功能

- 新增 Kafka 消费者实现,支持消息处理和错误处理。
- 实现 OffsetTracker 类,用于跟踪消息偏移量。
- 新增消息解析和数据库插入逻辑,支持从 Kafka 消息构建数据库行。
- 实现 UDP 数据包解析功能,支持不同类型的 UDP 消息。
- 新增 Redis 错误队列处理,支持错误重试机制。
- 实现 Redis 客户端和集成类,支持日志记录和心跳机制。
- 添加 Zod 验证模式,确保 Kafka 消息有效性。
- 新增日志记录和指标收集工具,支持系统监控。
- 添加 UUID 生成工具,支持唯一标识符生成。
- 编写处理器逻辑的单元测试,确保功能正确性。
- 配置 Vite 构建工具,支持 Node.js 环境下的构建。
This commit is contained in:
2026-03-14 17:33:19 +08:00
parent d62f83b4a4
commit 677dda80b9
101 changed files with 14904 additions and 0 deletions

View File

@@ -0,0 +1,103 @@
# Spec: onoffline-backend
## Purpose
从 Kafka 消费设备上下线事件并按规则写入 PostgreSQL 分区表,确保高可靠性、幂等写入和错误恢复能力。
## Requirements
### Requirement: 消费并落库
系统 SHALL 从 blwlog4Nodejs-rcu-onoffline-topic-0 消费消息,并写入 log_platform.onoffline.onoffline_record。
#### Scenario: 非重启数据写入
- **GIVEN** RebootReason 为空或不存在
- **WHEN** 消息被处理
- **THEN** current_status 等于 CurrentStatus (截断至 255 字符)
### Requirement: 重启数据处理
系统 SHALL 在 RebootReason 非空时强制 current_status 为 on。
#### Scenario: 重启数据写入
- **GIVEN** RebootReason 为非空值
- **WHEN** 消息被处理
- **THEN** current_status 等于 on
### Requirement: 空值保留
系统 SHALL 保留上游空值,不对字段进行补 0。
#### Scenario: 空值写入
- **GIVEN** LauncherVersion 或 RebootReason 为空字符串
- **WHEN** 消息被处理
- **THEN** 数据库存储值为对应的空字符串
### Requirement: 数据库分区策略
系统 SHALL 使用 Range Partitioning 按天分区,并自动维护未来 30 天的分区表,子表依赖 PostgreSQL 原生机制继承主表索引。
#### Scenario: 分区预创建
- **GIVEN** 系统启动或每日凌晨
- **WHEN** 运行分区维护任务
- **THEN** 确保数据库中存在未来 30 天的分区表,无需对子表显式创建单列表索引
### Requirement: 消费可靠性 (At-Least-Once)
系统 SHALL 仅在数据成功写入数据库后,才向 Kafka 提交消费位点。
#### Scenario: 逐条确认与顺序提交
- **GIVEN** 并发处理多条消息 (Offset 1, 2, 3)
- **WHEN** Offset 2 先完成Offset 1 尚未完成
- **THEN** 系统不提交 Offset 2直到 Offset 1 也完成,才提交 Offset 3 (即 1, 2, 3 都完成)
### Requirement: 数据库离线保护
系统 SHALL 在数据库连接丢失时暂停消费,防止数据堆积或丢失。
#### Scenario: 数据库断连
- **GIVEN** 数据库连接失败 (ECONNREFUSED 等)
- **WHEN** 消费者尝试写入
- **THEN** 暂停 Kafka 消费 1 分钟,并进入轮询检测模式,直到数据库恢复
### Requirement: 幂等写入
系统 SHALL 处理重复消费的数据,防止主键冲突。
#### Scenario: 重复数据处理
- **GIVEN** Kafka 重新投递已处理过的消息
- **WHEN** 尝试写入数据库
- **THEN** 使用 `ON CONFLICT DO NOTHING` 忽略冲突,视为处理成功
### Requirement: 性能与日志
系统 SHALL 最小化正常运行时的日志输出。
#### Scenario: 正常运行日志
- **GIVEN** 数据正常处理
- **WHEN** 写入成功
- **THEN** 不输出单条日志,仅每分钟输出聚合统计 (Pulled/Inserted)
### Requirement: 字段长度限制与截断
系统 SHALL 将部分变长字段截断至数据库允许的最大长度 (VARCHAR(255)),防止写入失败。
#### Scenario: 超长字段处理
- **GIVEN** LauncherVersion, CurrentStatus 或 RebootReason 超过 255 字符
- **WHEN** 消息被处理
- **THEN** 字段被截断为前 255 个字符并入库
### Requirement: 时间戳单位自动识别
系统 SHALL 自动识别 UnixTime 字段是秒还是毫秒,并统一转换为毫秒。
#### Scenario: 秒级时间戳转换
- **GIVEN** UnixTime < 100000000000 (约 1973 年前)
- **WHEN** 解析时间戳
- **THEN** 自动乘以 1000 转换为毫秒
### Requirement: 批量消费与写入
系统 SHALL 对 Kafka 消息进行缓冲,并按批次写入数据库,以提高吞吐量;当写入失败时,系统 SHALL 执行连接恢复重试与降级策略,但不在运行时创建数据库分区。
#### Scenario: 批量写入
- **GIVEN** 短时间内收到多条消息 (e.g., 500条)
- **WHEN** 缓冲区满或超时 (e.g., 200ms)
- **THEN** 执行一次批量数据库插入操作
#### Scenario: 写入失败降级
- **GIVEN** 批量写入因数据错误失败 (非连接错误)
- **WHEN** 捕获异常
- **THEN** 自动降级为逐条写入,以隔离错误数据并确保有效数据入库
#### Scenario: 分区缺失错误处理
- **GIVEN** 写入时数据库返回分区缺失错误
- **WHEN** 服务处理该错误
- **THEN** 服务记录错误并按既有错误处理机制处理,不在运行时执行分区创建

View File

@@ -0,0 +1,11 @@
## Implementation Status
- **Date**: 2026-02-04
- **Status**: Completed
- **Notes**:
- 已完成核心消费逻辑、分区管理、数据库幂等写入。
- 已处理数据库连接泄露 (EADDRINUSE) 问题,增加了离线保护机制。
- 已修复时间戳单位问题 (Seconds -> MS)。
- 已将关键字段长度扩展至 VARCHAR(255) 并增加了代码层截断保护。
- 验证了数据积压消费能力。
- 本阶段开发任务已归档。