技术方案.md 8.4 KB

企业能耗管理平台 (EMS) 技术架构实施方案

1. 总体技术架构设计 (System Architecture)

系统采用 B/S 架构,前后端分离,后端采用模块化单体 (Modular Monolith) 架构(便于部署与维护,内部模块解耦),数据层采用冷热分离策略。

1.1 拓扑分层

  • 接入层 (Edge Layer): 负责协议解析、长连接维持 (MQTT/WS)、数据清洗。
  • 服务层 (Service Layer): 业务逻辑核心,包含 API 服务、计算引擎、权限拦截。
  • 数据层 (Data Layer): 关系型数据 (Meta) + 时序数据 (TimeSeries) + 缓存 (Cache)。
  • 表现层 (Client Layer): PC Web SPA (单页应用)。

1.2 技术栈清单 (Tech Stack)

领域 技术选型 版本/备注
后端语言 Golang 1.21+
Web 框架 Gin 高性能 HTTP 框架
ORM 框架 GORM 支持 PG JSONB 与 关联查询
前端框架 Vue 3 Composition API + TypeScript
构建工具 Vite 5 + Pinia (状态管理)
UI 组件 Element Plus + ECharts 5 (图表) + AntV L7 (GIS)
关系型库 PostgreSQL 15+ (利用 JSONB, CTE 递归特性)
时序数据库 TDengine 3.0+ (核心,存储电力数据)
缓存/队列 Redis 7.0 (缓存/PubSub)
消息中间件 EMQX 5.0 (MQTT Broker)
反向代理 Nginx 1.24+ (静态托管/网关)

2. 核心数据模型设计 (Database Schema)

2.1 关系型数据库 (PostgreSQL)

利用 PG 的高级特性处理复杂属性与层级。

-- 1. 接入源配置 (存敏感信息与异构配置)
CREATE TABLE integration_source (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    driver_type VARCHAR(32) NOT NULL, -- 'HOME_ASSISTANT', 'MODBUS_TCP', 'MQTT'
    config JSONB NOT NULL,            -- e.g. {"ip":"192.168.1.10", "token":"eyJ...", "port":502}
    status SMALLINT DEFAULT 0,        -- 0:Offline, 1:Online
    sync_policy JSONB,                -- 同步策略配置
    updated_at TIMESTAMP
);

-- 2. 统一设备表 (核心资产)
CREATE TABLE device (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    source_id UUID REFERENCES integration_source(id),
    external_id VARCHAR(128) NOT NULL, 
    
    -- 属性
    name VARCHAR(128),
    device_type VARCHAR(32),          -- 'SWITCH', 'METER', 'SENSOR'
    
    -- 计量核心逻辑
    metering_mode VARCHAR(16),        -- 'REAL', 'VIRTUAL', 'COMPOUND'
    rated_power NUMERIC(10, 2),       -- 虚拟计量额定功率 (W)
    attribute_mapping JSONB,          -- 复合映射: {"power": "sensor.p1", "temp": "sensor.t1"}
    
    -- 关联关系
    location_id UUID REFERENCES sys_location(id),
    dept_id UUID REFERENCES sys_dept(id),
    
    is_active BOOLEAN DEFAULT TRUE,
    UNIQUE (source_id, external_id)   -- 防止重复导入
);

-- 3. 空间拓扑表 (支持无限层级)
CREATE TABLE sys_location (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    parent_id UUID,
    name VARCHAR(64),
    type VARCHAR(16),                 -- 'PARK', 'BUILDING', 'FLOOR', 'ROOM'
    ancestors TEXT,                   -- 祖级列表 "0,uuid1,uuid2" (便于快速搜索子孙)
    sort INT
);
-- 索引优化: CREATE INDEX idx_loc_ancestors ON sys_location(ancestors);

2.2 时序数据库 (TDengine)

利用超级表 (Super Table) 实现降维聚合。

-- 电力数据超级表
CREATE STABLE power_readings (
    ts TIMESTAMP,
    power FLOAT,       -- 瞬时功率 (W)
    voltage FLOAT,     -- 电压 (V)
    current FLOAT,     -- 电流 (A)
    energy_acc DOUBLE, -- 累计能耗 (kWh, 可选,若设备自身有读数)
    is_virtual BOOL    -- 是否为虚拟计算值
) TAGS (
    device_id BINARY(36),  -- 对应 PG device.id
    location_id BINARY(36),-- 冗余存储,加速空间聚合查询
    dept_id BINARY(36)     -- 冗余存储,加速部门聚合查询
);

-- 自动建表策略: 写入时自动根据 device_id 创建子表
-- INSERT INTO d_uuid123 USING power_readings TAGS ('uuid123', 'loc01', 'dept01') VALUES (NOW, 100.5, 220, 0.5, 0, false);


3. 关键模块技术实现细节

3.1 南向驱动层 (Driver Interface Pattern)

Go 语言中使用 Interface 实现多协议适配。

// 定义标准驱动接口
type IDriver interface {
    // 连通性测试
    TestConnection(config string) error
    // 服务发现 (返回标准化的临时设备结构)
    Discover() ([]DiscoveredDevice, error)
    // 启动监听 (长连接或轮询)
    StartWatch(dataChan chan<- StandardDataPoint)
}

// 统一数据点结构 (归一化)
type StandardDataPoint struct {
    DeviceExternalID string
    Timestamp        int64
    Type             string // "STATE", "POWER", "TEMP"
    Value            interface{}
}

// 工厂模式获取实例
func NewDriver(type string, configJSON string) IDriver { ... }

3.2 虚实结合计量引擎 (Virtual Metering Engine)

逻辑流程:

  1. Event: 接收到 StandardDataPoint
  2. Check: 查询内存缓存 (Redis) 获取该设备的 metering_mode
  3. Process:
  4. Case REAL: 直接将 Value 写入 TDengine。
  5. Case VIRTUAL:
  6. 若 Type == "STATE":
  7. State = "ON" -> 取 device.rated_power -> 写入 TDengine (Power=Rated)。
  8. State = "OFF" -> 写入 TDengine (Power=0)。
  • Case COMPOUND:
  • 根据 attribute_mapping 解析,将多个 sensor 消息合并或分别写入。

3.3 数据权限拦截器 (Data Scope Middleware)

利用 GORM 的 Scope 或 Gin 中间件实现 SQL 注入。

实现逻辑:

  1. 用户登录后,计算其 ScopeSQL 并存入 Redis。
  2. Level 2 (A栋): location_id IN ('uuid-a-building', 'uuid-room-101', ...)
  3. Level 4 (个人): location_id = 'uuid-my-office'

  4. GORM Hook: ```go // 定义 GORM Scope func DataScope(user *User) func(db *gorm.DB) *gorm.DB { return func(db *gorm.DB) *gorm.DB {

    if user.IsSuperAdmin {
        return db
    }
    // 自动注入 WHERE 条件
    return db.Where(user.DataScopeSQL) 
    

    } }

// 业务代码调用 (完全无感) db.Scopes(DataScope(currentUser)).Find(&devices)




### 3.4 实时状态推送 (WebSocket Manager)

用于前端大屏实时跳变、告警弹窗。

* **技术:** `github.com/gorilla/websocket`
* **架构:**
* **Hub:** 维护所有 Client 连接。
* **Channel:** 基于 Redis Pub/Sub 订阅后端 Go 服务的消息。
* **Filter:** 推送前判断 Client 的 `DataScope`,A栋的用户不推B栋的告警。



---

## 4. API 接口设计规范

采用 RESTful 风格,统一响应封装。

**Request Header:**
`Authorization: Bearer <JWT_TOKEN>`

**Response Struct:**

```json
{
  "code": 200,      // 业务状态码
  "msg": "success", // 提示信息
  "data": { ... }   // 业务数据
}

Core API Definitions:

  • POST /api/v1/auth/login -> JWT Token
  • GET /api/v1/sources/{id}/scan -> []DiscoveredDevice (带 Filter 逻辑)
  • POST /api/v1/devices/import -> (批量 Upsert 逻辑)
  • GET /api/v1/analysis/energy -> (查询 TDengine 聚合数据)
  • Query Params: group_by=location, interval=1d, start_ts=...

  • GET /api/v1/monitor/dashboard -> (混合数据:Redis在线数 + TDengine实时功率)


5. 部署架构 (Deployment)

基于 Docker Compose 的单机微服务化部署。

docker-compose.yaml 配置摘要:

version: '3.8'

services:
  # 1. 前端网关
  nginx:
    image: nginx:alpine
    ports: ["80:80", "443:443"]
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./dist:/usr/share/nginx/html # Vue Build Output
    depends_on:
      - app-server

  # 2. Go 后端服务
  app-server:
    build: .
    restart: always
    environment:
      - DB_DSN=host=postgres user=ems password=pass dbname=ems port=5432 sslmode=disable
      - TD_DSN=root:root@tcp(tdengine:6030)/power_db
      - REDIS_ADDR=redis:6379
    volumes:
      - ./logs:/app/logs

  # 3. 基础组件
  postgres:
    image: postgres:15-alpine
    volumes:
      - pg_data:/var/lib/postgresql/data
  
  tdengine:
    image: tdengine/tdengine:3.0
    ports: ["6030:6030"]
    volumes:
      - taos_data:/var/lib/taos

  redis:
    image: redis:7-alpine
  
  emqx:
    image: emqx/emqx:5.0
    ports: ["1883:1883", "8083:8083"]

volumes:
  pg_data:
  taos_data:

6. 开发环境要求

  • Golang SDK: 1.21+ (开启 Go Modules)
  • Node.js: 18+ (pnpm 推荐)
  • IDE: VS Code (前端) / GoLand (后端)
  • OS: Linux (推荐 Ubuntu 22.04 LTS) 用于生产环境部署。