Слушает telemetry.new_reading (ручной ack/nack, реквизишн только при транспортных ошибках gRPC), кэширует активные automation_rules в памяти с периодическим обновлением из PostgreSQL (JOIN с devices — резолвит внутренние ID в external_id, которым оперируют MQTT/Redis/gRPC). Условия правил (>,<,>=,<=,=,!=) оцениваются обобщённо, без привязки к конкретным типам устройств. При срабатывании вызывает device-control-service по gRPC и публикует automation.rule_triggered в RabbitMQ. Проверено сквозным тестом через docker compose на полном пайплайне: mosquitto_pub → ingest-service (ClickHouse + telemetry.new_reading) → rule-engine-service (совпадение правила) → device-control-service (gRPC → Redis desired_state + MQTT-команда) → automation.rule_triggered. Показание ниже порога проверено отдельно — правило корректно не срабатывает.
51 lines
1.4 KiB
Go
51 lines
1.4 KiB
Go
package rulecache
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
// activeRulesQuery resolves condition_source_device_id/target_device_id (FKs
|
|
// into devices.id) to devices.external_id, since every other part of the
|
|
// platform (MQTT, Redis, gRPC) keys devices by their external ID, not
|
|
// PostgreSQL's internal bigserial ID.
|
|
const activeRulesQuery = `
|
|
SELECT
|
|
r.id AS rule_id,
|
|
r.zone_id AS zone_id,
|
|
src.external_id AS source_external_id,
|
|
tgt.external_id AS target_external_id,
|
|
r.condition_sensor_type AS condition_sensor_type,
|
|
r.condition_operator AS condition_operator,
|
|
r.condition_value AS condition_value,
|
|
r.action_type AS action_type,
|
|
r.action_params AS action_params
|
|
FROM automation_rules r
|
|
JOIN devices src ON src.id = r.condition_source_device_id
|
|
JOIN devices tgt ON tgt.id = r.target_device_id
|
|
WHERE r.is_active = true
|
|
`
|
|
|
|
type PostgresFetcher struct {
|
|
pool *pgxpool.Pool
|
|
}
|
|
|
|
func NewPostgresFetcher(pool *pgxpool.Pool) *PostgresFetcher {
|
|
return &PostgresFetcher{pool: pool}
|
|
}
|
|
|
|
func (f *PostgresFetcher) FetchActiveRules(ctx context.Context) ([]Row, error) {
|
|
dbRows, err := f.pool.Query(ctx, activeRulesQuery)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("query active rules: %w", err)
|
|
}
|
|
rows, err := pgx.CollectRows(dbRows, pgx.RowToStructByName[Row])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("scan active rules: %w", err)
|
|
}
|
|
return rows, nil
|
|
}
|