# Vol.Pro 框架前后端改造方案 > **版本**: 1.0 > **日期**: 2025-05-17 > **基准**: SecMPS 整合方案 v3.1 + Vol.Pro 框架官方文档 > **核心原则**: 所有改动必须在 Partial/extension 目录中,严禁修改框架生成代码 --- ## 1. 改造总览 ### 1.1 改造清单 | 层面 | 改造项 | 位置 | 是否破坏可升级性 | |------|--------|------|:---:| | 数据库 | 5 张新表 + 字典数据 | SQL 脚本 | ❌ | | 后端-Entity | 6 个实体 Partial 类 | `DomainModels/*/partial/` | ❌ | | 后端-Service | 6 个 Service Partial 类 | `Services/*/Partial/` | ❌ | | 后端-Controller | 3 个 Controller Partial 类 | `Controllers/*/Partial/` | ❌ | | 后端-Job | 3 个 Quartz 定时任务 | `Warehouse/Services/` | ❌ | | 后端-Config | 注册网关 HttpClient | `Startup.cs` | ⚠️ 需手动合并 | | 前端-主表 | 自定义操作列插槽 | `extension/warehouse/` | ❌ | | 前端-组件 | 5 个设备操作组件 | `views/warehouse/base_device/components/` | ❌ | | 网关 | GatewayClient HTTP 客户端 | `Warehouse/Services/` | ❌ | ### 1.2 框架扩展点总览 ``` Vol.Pro 框架约定: 自动生成代码 (代码生成器覆盖) 自定义代码 (不被覆盖) ───────────────────────────── ────────────────────────── Controllers/xxxController.cs Controllers/xxx/Partial/xxxController.cs Services/xxxService.cs Services/xxx/Partial/xxxService.cs IServices/IxxxService.cs IServices/xxx/Partial/IxxxService.cs Repositories/xxxRepository.cs (无需自定义) IRepositories/IxxxRepository.cs (无需自定义) DomainModels/xxx/xxx.cs DomainModels/xxx/partial/xxx.cs 前端 views/xxx/xxx.vue extension/xxx.jsx + views/xxx/components/ ``` --- ## 2. 数据库改造 ### 2.1 新增表 执行 `doc/db_init.sql`,创建 5 张表: | 表名 | 说明 | 层级 | |------|------|------| | `gateway_nodes` | 网关节点 | 顶层 | | `base_device` | 统一设备主表 | 核心,AdapterCode+SourceId 联合唯一 | | `video_channel` | 视频通道扩展 | base_device 子表 | | `video_record` | 录像文件 | video_channel 子表 | | `iot_alarm` | 告警记录 | base_device 子表 | | `iot_devicedata` | 数据归档 | base_device 子表 | ### 2.2 字典初始化 Phase 0 需在 Vol.Pro 管理端 → 字典管理 中创建 8 组数据字典: | 字典名称 | 字典编号 | 字典值 | |----------|:---:|------| | 设备种类 | — | 摄像机/硬盘录像机/温湿度变送器/空调控制器/... (18 项) | | 设备分组 | device_group | 视频设备/IoT设备/门禁设备/道闸设备/报警设备 | | 是否父设备 | — | 是/否 | | 在线状态 | — | 在线/离线 | | 启用状态 | — | 启用/禁用 | | 是否控制点 | — | 只读/可写 | | 告警等级 | — | 提示/普通/重要/紧急 | | 告警状态 | — | 未确认/已确认/已结束 | ### 2.3 区-点位-设备 三级关联 框架支持主从表三级显示。利用现有表关系: ``` warehouse_regions (区域) ← 代码生成器已有 └── warehouse_devicepoint (点位) ← 代码生成器已有 └── base_device (设备) ← 新建,用 PointId 关联点位 ├── video_channel ← 新建子表 ├── iot_devicedata ← 新建子表 └── iot_alarm ← 新建子表 ``` **关键**:代码生成器配置 base_device 的 `DetailTable` 属性,关联 video_channel/iot_devicedata/iot_alarm 作为子表,框架自动渲染主从表 Tab。 --- ## 3. 后端改造 ### 3.1 Entity 扩展(Partial 类) **原则**:框架生成的 Entity 在 `DomainModels/device_manager/base_device.cs`,不可修改。仅在 `partial/` 中添加。 ``` VolPro.Entity/DomainModels/device_manager/partial/ ├── base_device.cs # 添加导航属性 + AdapterCode/SourceId 唯一约束注解 ├── gateway_nodes.cs # 网关特有业务属性 ├── video_channel.cs # 通道流信息缓存 ├── video_record.cs # (留空,框架生成即够用) ├── iot_devicedata.cs # (留空) └── iot_alarm.cs # 告警确认/结束方法 ``` **base_device Partial 示例**: ```csharp namespace VolPro.Entity.DomainModels { public partial class base_device { // 导航属性(用于主从表) [Navigate(NavigateType.OneToOne, nameof(DeviceId), nameof(video_channel.DeviceId))] public video_channel? VideoChannel { get; set; } [Navigate(NavigateType.OneToMany, nameof(DeviceId), nameof(iot_alarm.DeviceId))] public List? Alarms { get; set; } [Navigate(NavigateType.OneToMany, nameof(DeviceId), nameof(iot_devicedata.DeviceId))] public List? DeviceData { get; set; } // 网关字段标识(供同步时判断哪些字段可覆盖) public static readonly HashSet GatewayFields = new() { nameof(IsOnline), nameof(IsParent), nameof(ParentDeviceId), nameof(ExtraData), nameof(IpAddress), nameof(Port), nameof(LastSyncTime) }; } } ``` ### 3.2 Service 扩展(Partial 类) ``` Warehouse/Services/device_manager/Partial/ ├── base_deviceService.cs # GetRegionTree / GetDevicesByPoint / 字典查询辅助 ├── gateway_nodesService.cs # 网关注册/心跳/同步入口 ├── video_channelService.cs # (留空) ├── video_recordService.cs # (留空) ├── iot_devicedataService.cs # (留空) └── iot_alarmService.cs # (留空) ``` **gateway_nodesService Partial**(核心同步入口): ```csharp namespace Warehouse.Services { public partial class gateway_nodesService { private readonly IHttpContextAccessor _httpContextAccessor; private readonly Igateway_nodesRepository _repository; [ActivatorUtilitiesConstructor] public gateway_nodesService( Igateway_nodesRepository dbRepository, IHttpContextAccessor httpContextAccessor ) : base(dbRepository) { _httpContextAccessor = httpContextAccessor; _repository = dbRepository; } // 网关注册 (Upsert) — 被 Controller A1 调用 public async Task RegisterNodeAsync(string nodeCode, string token, string adapterTypes, string baseUrl) { // 实现: Upsert 逻辑 } // 心跳更新 — 被 Controller A2 调用 public async Task UpdateHeartbeatAsync(string nodeCode, string token) { // 实现: 更新 LastHeartbeat + IsOnline } // 设备同步 — 被 Controller A3 调用 public async Task<(int added, int updated)> SyncDevicesAsync(int gatewayNodeId, List devices) { // 实现: 字段分治 + parentSourceId 映射 } } } ``` ### 3.3 Controller 扩展(Partial 类) ``` VolPro.WebApi/Controllers/Warehouse/Partial/ ├── base_deviceController.cs # GetRegionTree / GetDevicesByPoint / 操作代理 ├── gateway_nodesController.cs # A1-A4 网关 API ├── video_channelController.cs # (留空) ├── video_recordController.cs # (留空) ├── iot_devicedataController.cs # (留空) └── iot_alarmController.cs # (留空) ``` #### 3.3.1 gateway_nodesController(A 组 API) **核心改造**:在框架生成的 `gateway_nodesController` 基础上,Partial 中添加 4 个不受权限控制的网关回调接口。 ```csharp namespace Warehouse.Controllers { public partial class gateway_nodesController { private readonly IHttpContextAccessor _httpContextAccessor; [ActivatorUtilitiesConstructor] public gateway_nodesController( Igateway_nodesService service, IHttpContextAccessor httpContextAccessor ) : base(service) { _httpContextAccessor = httpContextAccessor; } /// A1: 网关注册 (Upsert) [HttpPost, Route("/api/gateway/register"), AllowAnonymous] public async Task RegisterGateway([FromBody] GatewayRegisterRequest req) { // 实现 } /// A2: 心跳 [HttpPost, Route("/api/gateway/heartbeat"), AllowAnonymous] public async Task GatewayHeartbeat([FromBody] GatewayHeartbeatRequest req) { // 实现 } /// A3: 设备数据同步 (字段分治) [HttpPost, Route("/api/gateway/sync/devices"), AllowAnonymous] public async Task SyncDevices([FromBody] SyncDevicesRequest req) { // 实现 } /// A4: 告警同步 [HttpPost, Route("/api/gateway/sync/alarms"), AllowAnonymous] public async Task SyncAlarms([FromBody] SyncAlarmsRequest req) { // 实现 } } } ``` **权限模型**:A 组接口加 `[AllowAnonymous]`,内部通过 `NodeToken` 二次认证。B 组接口走框架 JWT 权限。 #### 3.3.2 base_deviceController(B 组代理 + 设备树) ```csharp namespace Warehouse.Controllers { public partial class base_deviceController { /// 区域→点位→设备树(管理端左侧树形控件) [HttpGet, Route("/api/DeviceManager/GetRegionTree")] public async Task GetRegionTree() { // SELECT warehouse_regions JOIN warehouse_devicepoint → 树形结构 // 每个点位下统计 base_device 数量 } /// 点位下设备列表(含子设备) [HttpGet, Route("/api/DeviceManager/GetDevicesByPoint")] public async Task GetDevicesByPoint(int pointId, int page = 1, int size = 20) { // SELECT base_device WHERE PointId = pointId // Include 子设备(ParentDeviceId) } } } ``` ### 3.4 网关 HTTP 客户端(GatewayClient) ``` Warehouse/Services/GatewayClient.cs ``` ```csharp public class GatewayClient { private readonly IHttpClientFactory _httpFactory; private readonly IConfiguration _config; public GatewayClient(IHttpClientFactory httpFactory, IConfiguration config) { _httpFactory = httpFactory; _config = config; } /// 调网关 B3: 手动触发全量同步 public async Task TriggerFullSyncAsync(string baseUrl, string adapterTypes) { var http = _httpFactory.CreateClient(); // POST {baseUrl}/api/gateway/devices/sync?adapter={adapterTypes} } /// 调网关 B4: 获取实时点位值 public async Task> GetRealtimeAsync(string baseUrl, string adapter, string deviceId) { // GET {baseUrl}/api/gateway/realtime/{adapter}/{deviceId} } /// 调网关 B5: 设备控制 public async Task ControlDeviceAsync(string baseUrl, string adapter, string deviceId, int pointIndex, double value) { // POST {baseUrl}/api/gateway/realtime/{adapter}/control } } ``` ### 3.5 Quartz 定时任务 | Job | 触发器 | 职责 | |-----|:---:|------| | `SyncDevicesJob` | 每 5 分钟 | 遍历所有在线网关 → 调 GatewayClient.TriggerFullSyncAsync() | | `RealtimePollJob` | 每 10 秒 | 轮询 MC4.0 IoT 设备实时值 → 更新 iot_devicedata | | `HeartbeatMonitorJob` | 每 15 秒 | 扫描 gateway_nodes 心跳超时 → 标记离线 + 级联设备离线 | **注册方式**:在 Vol.Pro 管理端 → Quartz 任务管理 → 新建任务,指定 Job 类全名。 ### 3.6 Startup.cs 注册 ```csharp // 在 Startup.cs 或 Program.cs 中注册 builder.Services.AddHttpClient("VolPro", c => { c.Timeout = TimeSpan.FromSeconds(30); }); builder.Services.AddSingleton(); ``` --- ## 4. 前端改造 ### 4.1 架构决策 | 方案 | 描述 | 是否破坏可升级性 | |------|------|:---:| | ❌ 修改生成的 .vue | 直接改 views/warehouse/base_device/ 下框架生成文件 | ✅ 破坏 | | ✅ extension + components | 通过扩展注入 + 自定义组件实现操作列 | ❌ 不破坏 | ### 4.2 自定义操作列插槽 框架生成的 base_device 列表页面默认操作列只有"编辑/删除"。通过 **前端扩展文件** 自定义替换: ``` web.vite/src/extension/warehouse/base_device.jsx ``` ```javascript // 自定义操作列渲染 import VideoDeviceActions from '@/views/warehouse/base_device/components/VideoDeviceActions.vue' import IoTDeviceActions from '@/views/warehouse/base_device/components/IoTDeviceActions.vue' // 注册自定义组件 export default { components: { VideoDeviceActions, IoTDeviceActions }, // 替换框架默认操作列 slots: { // 操作列插槽 'col-action': (h, { row }) => { const comp = row.deviceGroup === '视频设备' ? 'VideoDeviceActions' : row.deviceGroup === 'IoT设备' ? 'IoTDeviceActions' : null if (comp) return h(comp, { props: { row } }) // fallback 到框架默认按钮 return null } } } ``` ### 4.3 组件目录 ``` web.vite/src/views/warehouse/base_device/components/ ├── VideoDeviceActions.vue # 实时预览/云台/回放/快照/同步通道 ├── IoTDeviceActions.vue # 实时数据/控制/刷新/告警 ├── AccessDeviceActions.vue # 远程开门 (Phase 3) ├── BarrierDeviceActions.vue # 抬杆/落杆 (Phase 3) ├── AlarmDeviceActions.vue # 告警/布防撤防 (Phase 3) ├── DeviceLivePreview.vue # Jessibuca 播放器弹窗 ├── PtzControlPanel.vue # ↑↓←→+ZOOM+停止 ├── RealtimeDataPanel.vue # 实时点位值表格弹窗 ├── DeviceControlPanel.vue # 控制写值面板 ├── DeviceEditDialog.vue # 设备编辑弹窗 (扩展字段) └── MapBindingPanel.vue # VgoMap 模型绑定 ``` ### 4.4 组件设计要点 **VideoDeviceActions.vue**(核心组件): ``` Props: row (base_device) 按钮组: [实时预览] → 打开 DeviceLivePreview.vue 弹窗 → GET /api/gateway/streams/{adapterCode}/{sourceId}/live → 流地址 → 内嵌 Jessibuca 播放器(fallback: