# 空间拓扑与用电架构 · 实现计划
## Context(为什么做)
现有「空间拓扑与用电架构」页面(`client/src/views/customer/topology/index.vue`)只实现了「客户 → 建筑 → 房间」三层树 + 建筑/房间增删改,**"用电架构"部分完全缺失**:数据库与后端没有任何承载"用电回路"的表或接口,页面上也没有配电拓扑可视化。
设计稿 `系统设计/UI/空间拓扑与用电架构/code.html` 定义了三栏工业级布局(左空间树 / 中配电单线拓扑流 / 右节点属性面板)。本次目标:在新表 + 新后端接口的基础上,把该页面补齐为三栏结构,让"空间"与"用电回路"打通。
## 已确认的边界(用户决策)
| 项 | 结论 |
| ------ | ---------------------------------------- |
| 目标形态 | 按设计稿做完整三栏(左树 / 中单线图 / 右属性面板) |
| 左树层级 | 客户 → 建筑 → **楼层** → 房间 → **回路**(不要"配电箱"层) |
| 楼层 | 由房间/回路按 `floor_no` 分组生成的**虚拟层**,不建表 |
| 中间图上层 | 进线柜 / 变压器:**只读展示**,不落库、不做 CRUD |
| 中间图末端 | **回路卡片**(一个回路一张卡) |
| 实时电气量 | 电压/电流/功率/温度等**一律占位**(显示 `—`),不造假数据、不接采集 |
| ICC 映射 | **不做**(设计稿里的网关层、ICC 设备码、告警阈值联动均不实现) |
| 覆盖端 | 仅 Web 管理端 `client/` |
| 数据来源 | 新建表 + 后端 CRUD 接口 |
## 数据模型(新增 1 张表)
`customer_circuit`(客户用电回路)—— 参照 `customer_room` 的建表风格(`database/db_init.sql#L364-L387`):
| 字段 | 类型 | 说明 |
| -------------- | --------------------- | ----------------------------------- |
| id | BIGSERIAL PK |
|
| customer\_id | BIGINT NOT NULL | → `customer(id)` |
| building\_id | BIGINT NOT NULL | → `customer_building(id)` |
| room\_id | BIGINT NULL | → `customer_room(id)`,允许挂建筑/楼层而不挂房间 |
| floor\_no | VARCHAR(16) | 楼层(冗余,便于按楼层分组) |
| circuit\_code | VARCHAR(64) NOT NULL | 回路编号,全局唯一 |
| circuit\_name | VARCHAR(128) NOT NULL | 回路名称 |
| loop\_spec | VARCHAR(128) | 回路规格(如 `三相380V / 铜芯4×25+1×16`) |
| load\_type | VARCHAR(32) | 负荷类型(照明/动力/空调/插座…) |
| rated\_voltage | VARCHAR(32) | 额定电压(配置值) |
| rated\_current | VARCHAR(32) | 额定电流(配置值) |
| rated\_power | NUMERIC(10,2) | 额定功率 kW(配置值) |
| status | SMALLINT DEFAULT 1 | 1 正常 / 0 停用 |
| remark | TEXT | 备注 |
| sort\_order | INT DEFAULT 0 | 排序 |
| created\_at | TIMESTAMP |
|
索引:`customer_id`、`building_id`、`room_id`、唯一键 `circuit_code`。
> 命名跟随现有习惯:表 `customer_circuit`、entity `CustomerCircuit`、路由 `/api/customer-circuits`、权限码 `customer:circuit`(与 `customer:building` / `customer:room` 同级)。
## 后端改动(Midway.js + sequelize-typescript)
参照样板:`customer_room` 全链路。
1. **迁移**:`database/migrations/20261011_customer_circuit.sql`(幂等 `CREATE TABLE IF NOT EXISTS`),并在 `database/db_init.sql` 同步该表定义。
2. **Entity**:`server/src/entity/customer_circuit.entity.ts`(照抄 `customer_building.entity.ts` 的 `@Table/@Column/@BelongsTo/@CreatedAt` 写法)。
3. **DTO**:`server/src/dto/customer_circuit.dto.ts`(Query/Create/Update,class-validator 风格照抄 `customer_room.dto.ts`)。
4. **Service**:`server/src/service/customer_circuit.service.ts`
* `list` / `detail` / `create` / `update` / `delete`(照抄 `customer_room.service.ts`,统一返回 `{ code, message, data }`)。
* `topology(customerId)`:**一次请求**返回整棵「建筑 → 楼层 → 房间 → 回路」结构,避免前端 N+1(现有页面是逐建筑拉房间)。返回形如:
```json
{ "buildings": [ { "id":1, "buildingName":"…", "floors":[ { "floorNo":"2", "rooms":[ { "id":9, "roomName":"…", "circuits":[…] } ], "circuits":[…] } ] } ] }
```
5. **Controller**:`server/src/controller/customer_circuit.controller.ts`(`@Controller("/api/customer-circuits")`,含 `GET /topology`)。
6. **权限注册**:`server/src/middleware/rbac.middleware.ts` 的 `PERMISSION_MAP` 新增 `"/api/customer-circuits": "customer:circuit"`。
7. **菜单/权限种子**:在 `database/db_seed.sql` 复用现有「空间拓扑与用电架构」菜单(id=32, `/customer/topology`, 权限码 `customer:topology`),并**参照** **`customer:room`** **的登记方式**新增按钮级权限 `customer:circuit`。
## 前端改动(Vue3 + Ant Design Vue)
1. **新增 API 封装** `client/src/api/customerCircuit.ts`:照抄 `customerRoom.ts` 风格(`getList/getDetail/create/update/delete/getTopology` + 类型定义 + 常量:负荷类型选项)。
2. **重写** `client/src/views/customer/topology/index.vue` 为三栏:
* **顶部**:面包屑 + 客户切换器 + 指标条 + 「新增回路」按钮。指标只展示**可由现有数据算出**的项(已接入建筑数、监测用电回路数、空间建模覆盖率 = 有回路的房间数/房间总数);无数据源的项(变电所/配电室、ICC 边缘网关)**不展示**。
* **左栏(≈280px)**:`a-tree` 渲染「客户 → 建筑 → 楼层 → 房间 → 回路」;节点徽章显示回路数;节点悬停操作:建筑/房间沿用现有增删改(`customerBuildingApi` / `customerRoomApi`),回路可新增/编辑/删除。
* **中栏**:单线图。上层为**只读示意**(10kV 进线柜 → 变压器 → 低压母线,按客户/建筑信息填充文案)、下层为**回路卡片网格**(编号/名称/所属房间/规格/额定参数,点击选中;实时电气量显示 `—`)。用 div 网格 + 内联 SVG 母线渲染(与设计稿一致),**不引入新依赖**(echarts 已在依赖中但不必要)。
* **右栏(≈320px)**:选中节点的属性面板(回路基本信息可编辑并保存;房间/建筑节点展示只读属性)。
* 样式沿用页面既有基线:背景 `#eef3f8`、主色 `#0054cd`、卡片圆角 `12px`、等宽字体用 `Consolas/Menlo`。
3. **路由/权限**:沿用现有 `customer/topology` 路由与 `customer:topology` 菜单,**不新增页面路由**。
## 不做(明确排除)
* ICC 网关节点、ICC 设备码映射、告警阈值联动、下发同步
* 实时电气量采集与展示(一律 `—`)
* 「从 ICC 同步拓扑」「CAD/BIM 导入」「导出高清拓扑图」按钮 → 先不实现(可留占位,若留则点击提示"暂未开放")
* 「空间三维映射」视图切换
* 配电箱/配电柜层
* Flutter 管理端
## 假设(如不认可请指出)
1. 中间图的上层(进线柜/变压器)**纯前端静态示意**,文案按所选客户/建筑动态填充,不落库。
2. `circuit_code` **全局唯一**(与设备台账 `equip_code` 一致);若你希望"客户内唯一",需改唯一约束。
3. 回路可以**不挂房间**(只挂建筑+楼层),以适应公共区域回路。
4. 顶部指标只做能算出来的 3 项,其余留白而非显示假数据。
## 验证方式
1. 执行迁移:`database/migrations/20261011_customer_circuit.sql`(以及 db\_seed 的权限登记),确认表与权限数据落库。
2. 后端:启动 `server/`,用带 token 的请求验证 `GET /api/customer-circuits/topology?customerId=1`、以及 create/update/delete;无 token 应 401、无权限应 403。
3. 前端:`client/` 执行 `npm run check`(vue-tsc)通过;`npm run dev` 后进入「客户管理 → 空间拓扑与用电架构」:
* 选客户 → 左树按「建筑 → 楼层 → 房间 → 回路」正确展开;
* 新增/编辑/删除回路后树与中间卡片同步刷新;
* 点击回路卡片 → 右栏属性正确回填、可保存;
* 中间图上层的进线柜/变压器为只读示意。
4. 回归:确认原有建筑/房间增删改仍可用(房间功能未被破坏)。