feat: 初始化后端服务基础架构与核心组件

- 添加项目基础结构,包括 .gitignore、package.json、Docker 配置和环境变量示例
- 实现核心模块:Kafka 消费者、PostgreSQL 数据库管理器、Redis 客户端与错误队列
- 添加工具类:日志记录器、指标收集器、UUID 生成器
- 实现数据处理器,支持 0x36 上报和 0x0F 命令的解析与存储
- 添加数据库初始化脚本和分区管理,支持按时间范围分区
- 引入 Zod 数据验证和 Vitest 单元测试框架
- 提供完整的项目文档,包括数据库设计、Kafka 格式规范和 Redis 集成协议
This commit is contained in:
2026-01-30 11:05:00 +08:00
parent ec2b44b165
commit 86a1e79153
51 changed files with 5921 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
# Add Validation and Unit Tests
## Summary
Introduced robust data validation using `zod` and unit testing using `vitest` to ensure system stability and correctness of data processing logic.
## Changes
### 1. Data Validation
- **Library**: `zod`
- **File**: `src/schema/kafkaPayload.js`
- **Logic**:
- Defined strict schema for Kafka payload including required headers (`ts_ms`, `hotel_id`, etc.) and optional arrays (`device_list`, `fault_list`).
- Implemented automatic type transformation (e.g., `room_id` to string, `cmd_word` normalization).
- Integrated into `src/processor/index.js` to validate incoming messages before processing.
### 2. Unit Testing
- **Framework**: `vitest`
- **File**: `tests/processor.test.js`
- **Coverage**:
- Required field validation (throws error on missing fields).
- `0x36` Status Report processing (device list expansion).
- `0x36` Fault Report processing (fault list expansion).
- `0x36` Mixed Report processing.
- `0x0F` Control Command processing (control list expansion).
- `0x0F` ACK processing.
- Fallback logic for empty lists.
### 3. Scripts
- Added `test` script to `package.json`: `vitest run`.
## Verification
- Ran `npm test` and all 7 tests passed successfully.

View File

@@ -0,0 +1,9 @@
# Operations Log
## [2026-01-28] Correction: Kafka JSON Structure
- **Action**: Corrected `docs/kafka_format.md` based on user feedback.
- **Details**:
- **Shifted Parsing Responsibility**: Clarified that the upstream service performs the raw parsing.
- **Structured Arrays**: Introduced `device_list`, `fault_list`, and `control_list` arrays in the JSON schema.
- **Flattened Logic**: Backend no longer parses `udp_raw` for list items but iterates over the provided JSON arrays.
- **Updated Examples**: Provided clear examples of nested JSON objects for devices and faults.

View File

@@ -0,0 +1,9 @@
# Operations Log
## [2026-01-28] Documentation Update: Kafka Format & Splitting Logic
- **Action**: Updated `docs/kafka_format.md`.
- **Details**:
- Defined strict splitting logic for `0x36` commands: One Kafka message -> Multiple DB records (based on `report_count` and `fault_count`).
- Updated Kafka JSON Schema to include all database fields (Header + Logic/Parsing fields).
- Clarified `action_type` mapping and `sys_lock_status` propagation.
- Added table-based parsing example for visual clarity.

View File

@@ -0,0 +1,33 @@
# Database Partitioning and Initialization Strategy
## Summary
Implemented automatic database initialization and time-based table partitioning to ensure system scalability and ease of deployment.
## Changes
### 1. Database Initialization (`src/db/initializer.js`)
- **Logic**:
1. **Database Check**: Connects to the default `postgres` database to check if the target database (defined in `.env`) exists. Creates it if missing.
2. **Schema & Table Check**: Connects to the target database and executes `scripts/init_db.sql`.
3. **Partition Check**: Calls `PartitionManager` to ensure partition tables exist for the next 30 days.
- **Integration**: Called during application bootstrap in `src/index.js`.
### 2. Table Partitioning (`src/db/partitionManager.js`)
- **Strategy**: Partition by Range on `ts_ms` (milliseconds).
- **Partition Size**: Daily partitions (e.g., `rcu_action.rcu_action_events_20260129`).
- **Automation**:
- `ensurePartitions(daysAhead)`: Calculates daily ranges and creates partitions if they don't exist.
- **Scheduled Task**: `node-cron` job runs daily at 00:00 to pre-create partitions for the next 30 days.
### 3. Schema Updates (`scripts/init_db.sql`)
- Modified `rcu_action_events` table definition to use `PARTITION BY RANGE (ts_ms)`.
- **Note**: The primary key `(ts_ms, guid)` supports this partitioning strategy perfectly.
### 4. Code Structure Updates
- **Dependency**: Added `node-cron`.
- **Singleton**: Updated `src/db/databaseManager.js` to export a singleton instance for shared connection pooling.
- **Bootstrap**: Updated `src/index.js` to include initialization and cron scheduling.
## Verification
- **Startup**: Application will auto-initialize DB and partitions on first run.
- **Maintenance**: Cron job ensures future partitions are always ready.

View File

@@ -0,0 +1,19 @@
# System Robustness Improvements
## Context
A review of the project revealed missing initialization scripts and lack of graceful shutdown handling, which are critical for production stability and deployment.
## Changes
1. **Database Initialization (`scripts/init_db.sql`)**:
* Created a SQL script to initialize the `rcu_action` schema and `rcu_action_events` table.
* Added indexes for performance optimization (`ts_ms`, `hotel_id`, `room_id`, `direction`, `cmd_word`, `action_type`).
* Added a composite index (`hotel_id`, `room_id`, `ts_ms DESC`) for common query patterns.
2. **Graceful Shutdown (`src/index.js`)**:
* Implemented `SIGTERM` and `SIGINT` signal handlers.
* Ensures resources are closed in order: Kafka Consumer -> Redis Client -> Database Pool.
* Prevents data corruption or zombie connections during container restart/stop.
3. **Error Handling Enhancements**:
* Decoupled `startErrorRetryWorker` from the main bootstrap chain to prevent blocking/crashing on startup.
* Added `try/catch` block in `handleMessage` to ensure errors are logged and bubbled up to the retry mechanism properly.

View File

@@ -0,0 +1,27 @@
# Kafka Partition and Database Connection Strategy
## Context
User highlighted two specific constraints for the production environment:
1. The Kafka topic `blwlog4Nodejs-rcu-action-topic` has 6 partitions, but the application runs as a single PM2 instance.
2. Database connections must be minimized, using a single connection pool.
## Analysis & Implementation
### 1. Kafka Partition Handling
- **Constraint**: Single instance must consume from all 6 partitions.
- **Solution**: We are using `kafka-node`'s `ConsumerGroup`.
- **Mechanism**:
- `ConsumerGroup` automatically manages partition assignment.
- When a single consumer joins the group, the group coordinator assigns all available partitions (0-5) to that single member.
- Protocol is set to `['roundrobin']`, ensuring even distribution if scaling up later, but effective for full coverage in single-mode.
- **Verification**: Checked `src/kafka/consumer.js`. No code changes needed.
### 2. Database Connection Pool
- **Constraint**: Single pool, limited connections.
- **Solution**:
- **Singleton Pattern**: `src/db/databaseManager.js` exports a pre-instantiated `dbManager` object. All modules import this single instance.
- **Connection Limit**: `src/config/config.js` sets `max` connections to `process.env.DB_MAX_CONNECTIONS` with a default of **10**.
- **Verification**: Checked `src/db/databaseManager.js` and `src/config/config.js`. Implementation complies with constraints.
## Conclusion
The current architecture inherently satisfies these requirements without further modification.

View File

@@ -0,0 +1,23 @@
# Processor Refactoring for Unified Kafka Topic
## Context
The system receives all messages (Status, Fault, Control, ACK) from a single Kafka topic. The message key is unreliable (random). Therefore, the backend must rely on `cmd_word` and `direction` within the message payload to determine the processing logic and storage structure.
## Changes
1. **Refactored `src/processor/index.js`**:
* Removed `udpParser.js` dependency (parsing is now upstream responsibility).
* Implemented `resolveActionType` to categorize messages into:
* `"36上报"` (Status/Fault)
* `"0F下发"` (Control)
* `"0FACK"` (ACK)
* Implemented `buildRowsFromPayload` with specific logic for each action type:
* **36上报**: Iterates over `device_list` and `fault_list` (provided by upstream JSON) to create multiple DB rows. Maps `dev_type`, `dev_addr`, `dev_loop`, `dev_data`, `error_type`, `error_data`.
* **0F下发**: Iterates over `control_list`. Maps `dev_type`, `dev_addr`, `loop`->`dev_loop`, `type_l`, `type_h`.
* **0FACK**: Creates a single DB row (fallback).
* Ensured `details` column stores the full lists (`device_list`, `fault_list`, `control_list`) for traceability.
* Ensured `extra` column stores the `raw_hex` (udp_raw).
## Impact
* Backend is now fully compliant with the "Upstream Parsing" requirement.
* Correctly handles one-to-many storage for Status, Fault, and Control messages.
* No longer relies on Kafka Keys for dispatching.

View File

@@ -0,0 +1,33 @@
# Initial Setup & Backend Implementation
## Operations Log
### Configuration & Code Update
- **Action**: Updated codebase to support advanced environment configurations.
- **Details**:
- Updated `src/config/config.js` to read SASL and SSL configurations from `.env`.
- Updated `src/kafka/consumer.js` to support SASL authentication.
- Updated `src/db/databaseManager.js` to support PostgreSQL SSL connections.
- Verified `.env` integration with `dotenv`.
### Backend Component Construction
- **Action**: Initialized backend scaffolding and core implementation.
- **Details**:
- Created project structure under `bls-rcu-action-backend`.
- Implemented `DatabaseManager` with connection pooling.
- Implemented `KafkaConsumer` with error handling.
- Implemented `RedisIntegration` for heartbeats and error logging.
- Added Docker and PM2 configuration files.
### Documentation Updates
- **Action**: Refined data schema and documentation.
- **Details**:
- **Schema Changes**:
- `room_id`: Changed to `VARCHAR(32)` (String).
- `direction`: Changed to `VARCHAR(10)` (String: "上传"/"下发").
- `cmd_word`: Changed to `VARCHAR(10)` (String: "0x36").
- Added `extra` field (JSONB) for raw communication data.
- Defined Primary Key: 32-bit Unsigned UUID (`guid`).
- Defined Composite Key logic: `ts_ms` (Key1) + `guid` (Key2).
- **Indices**: Marked `hotel_id`, `room_id`, `direction`, `cmd_word` for indexing.
- **Kafka Format**: Created `docs/kafka_format.md` with updated JSON reference.

View File

@@ -0,0 +1,19 @@
# Database Schema Unification
## Context
Refined the database schema and Kafka JSON format to eliminate redundant fields and better support the `0x36` (Report) and `0x0F` (Control) commands.
## Changes
1. **Unified Device Identification**:
* Removed `fault_dev_type`, `fault_dev_addr`, `fault_dev_loop`.
* Consolidated all device identification into `dev_type`, `dev_addr`, and `dev_loop`. These fields are now shared across status reports, fault reports, and control commands.
2. **Field Additions for Control Commands**:
* Added `type_l` (SMALLINT) and `type_h` (SMALLINT) to support `0x0F` control command parameters.
3. **Document Updates**:
* Updated `docs/readme.md`: Reflected the schema changes in the table definition.
* Updated `docs/kafka_format.md`: Updated the JSON schema and mapping rules to align with the unified database fields.
4. **Code Updates**:
* Updated `bls-rcu-action-backend/src/db/databaseManager.js`: Modified the `columns` array to match the new schema.

View File

@@ -0,0 +1,22 @@
# Action Type Field Update
## Context
The `action_type` field was previously defined as a `SMALLINT` with integer mappings (1, 2, 3, 4). The requirement has changed to store explicit string values for better readability and direct mapping.
## Changes
1. **Field Definition**:
* Changed `action_type` from `SMALLINT` to `VARCHAR(20)`.
2. **Enum Values**:
* Old: `1` (ACK), `2` (Control), `4` (Status)
* New:
* `"0FACK"`: ACK (0x0F 上报)
* `"0F下发"`: Control (0x0F 下发)
* `"36上报"`: Status (0x36)
3. **Document Updates**:
* `docs/readme.md`: Updated table definition and dictionary.
* `docs/kafka_format.md`: Updated JSON schema and backend logic examples.
4. **Code Updates**:
* `src/processor/index.js`: Updated `resolveActionType` to return the new string enums.

View File

@@ -0,0 +1,19 @@
# Update Database Primary Key Definition
## Summary
Updated the database schema initialization script to define a composite primary key `(ts_ms, guid)` instead of a single primary key on `guid`, aligning with the project documentation requirements.
## Changes
### 1. Database Schema (`scripts/init_db.sql`)
- **Primary Key**: Changed from `guid` to composite `(ts_ms, guid)`.
- **Reasoning**: `ts_ms` is the partition key or main ordering field (Key1), and `guid` provides uniqueness (Key2). This structure optimizes time-series based queries and data distribution.
- **Indexes**: Removed `idx_rcu_action_ts_ms` as it is now redundant (covered by the primary key index).
## Impact
- **Table Structure**: `rcu_action.rcu_action_events` now enforces uniqueness on the combination of timestamp and GUID.
- **Performance**: Queries filtering by `ts_ms` will utilize the primary key index.
## Verification
- Reviewed SQL syntax in `scripts/init_db.sql`.
- Confirmed alignment with `docs/readme.md` requirements.

View File

@@ -0,0 +1,22 @@
# Add device_id Field
## Context
User requested to add a `device_id` field (string) to the Kafka payload and database table.
## Changes
1. **Documentation**:
- Updated `docs/readme.md` to include `device_id` in the table structure.
- Updated `docs/kafka_format.md` to include `device_id` in the JSON schema and examples.
2. **Database**:
- Updated `bls-rcu-action-backend/scripts/init_db.sql` to add `device_id` column and a corresponding index.
3. **Backend Code**:
- `src/schema/kafkaPayload.js`: Added `device_id` to the Zod validation schema (string, required).
- `src/db/databaseManager.js`: Added `device_id` to the list of columns for insertion.
- `src/processor/index.js`: Updated logic to extract `device_id` from the payload and pass it to the row object.
- `tests/processor.test.js`: Updated test cases to include `device_id` in the mock payload.
## Verification
- Unit tests updated and should pass.
- Schema changes aligned across documentation and code.

View File

@@ -0,0 +1,18 @@
# Remove action_type from Kafka Payload
## Context
User requested to ensure `action_type` is determined solely by the backend logic and is NOT accepted from the Kafka payload.
## Changes
1. **Documentation**:
- Updated `docs/kafka_format.md` to remove `action_type` from the input schema and examples.
2. **Schema Validation**:
- Updated `src/schema/kafkaPayload.js` to remove `action_type` from the Zod schema.
3. **Processor Logic**:
- Confirmed `src/processor/index.js` already independently calculates `action_type` using `resolveActionType` (based on `direction` and `cmd_word`).
- The code does not use any `action_type` from the payload, ensuring strict adherence to the new rule.
## Verification
- Unit tests (`tests/processor.test.js`) pass successfully.
- The system now ignores any `action_type` field if it were to be sent (schema validation would strip it if `strict` mode was on, but default Zod `parse` strips unknown keys unless `passthrough` is used. Wait, Zod default behavior strips unknown keys).
- Actually, Zod by default *strips* unknown keys. So if `action_type` is sent but not in schema, it will be removed. Perfect.

20
openspec/config.yaml Normal file
View File

@@ -0,0 +1,20 @@
schema: spec-driven
# Project context (optional)
# This is shown to AI when creating artifacts.
# Add your tech stack, conventions, style guides, domain knowledge, etc.
# Example:
# context: |
# Tech stack: TypeScript, React, Node.js
# We use conventional commits
# Domain: e-commerce platform
# Per-artifact rules (optional)
# Add custom rules for specific artifacts.
# Example:
# rules:
# proposal:
# - Keep proposals under 500 words
# - Always include a "Non-goals" section
# tasks:
# - Break tasks into chunks of max 2 hours

23
openspec/project.md Normal file
View File

@@ -0,0 +1,23 @@
# BLS RCU Action Server
## Overview
Backend service for processing RCU action events from Kafka, parsing them, and storing them in PostgreSQL. Includes error handling via Redis and heartbeat monitoring.
## Architecture
- **Input**: Kafka Topic (`blwlog4Nodejs-rcu-action-topic` or configured via env)
- **Processing**: Node.js Service
- **Consumer**: `kafka-node` consumer group
- **Parser**: Parses JSON messages, handles UDP raw data decoding
- **Database**: PostgreSQL (Batch insert)
- **Error Handling**: Redis List (`error_queue`) for failed messages + Retry mechanism
- **Output**: PostgreSQL Table (`rcu_action_events`)
## Configuration (Environment Variables)
The project is configured via `.env`. Key variables:
- **Kafka**: `KAFKA_BROKERS`, `KAFKA_TOPIC`, `KAFKA_SASL_USERNAME`, `KAFKA_SASL_PASSWORD`
- **Database**: `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASSWORD`, `DB_DATABASE`, `DB_SSL`
- **Redis**: `REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWORD`
## Development Constraints
- **Schema**: Must strictly follow `docs/readme.md`.
- **Database**: Do not alter Schema Name (`rcu_action`) or Table Name (`rcu_action_events`) unless explicitly requested.