ether-docs/02-DESIGN/domains/06-INTEGRATION.md

789 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 集成中心领域技术方案
**领域编号**: 4.6
**微服务**: ether-hub
**最后更新**: 2026-02-10
---
## 一、领域概述
### 1.1 领域职责
集成中心领域负责 Ether 平台与外部系统的对接集成:
- 外部系统适配(停车系统、门禁系统、政府监管平台)
- IoT设备接入与管理
- 数据同步与转换
- 集成日志记录与监控
### 1.2 核心概念
| 概念 | 说明 | 对应实体 |
|------|------|----------|
| **集成适配器** | 外部系统连接配置 | IntegrationAdapter |
| **集成日志** | 接口调用记录 | IntegrationLog |
| **IoT设备** | 物联网设备管理 | IoTDevice |
| **数据映射** | 字段转换规则 | DataMapping |
---
## 二、领域模型
### 2.1 聚合根设计
#### IntegrationAdapter集成适配器
```java
@Entity
@Table(name = "hub_integration_adapter")
@Data
public class IntegrationAdapter {
@Id
private UUID id;
private UUID projectId;
private String name; // 适配器名称
private String systemType; // PARKING/ACCESS/GOVERNMENT/IOT
private String description; // 描述
// 协议配置
private String protocol; // HTTP/HTTPS/MQTT/COAP/SOAP
private String baseUrl; // 基础URL
private Integer timeout; // 超时时间(秒)
private Integer retryCount; // 重试次数
// 认证配置
private String authType; // NONE/API_KEY/OAUTH2/JWT/BASIC
private String authConfig; // 认证配置(JSON)
// API_KEY: {key, value, location: header/query}
// OAUTH2: {clientId, clientSecret, tokenUrl, scope}
// JWT: {secret, algorithm}
// BASIC: {username, password}
// 状态
private AdapterStatus status; // ONLINE/OFFLINE/ERROR
private LocalDateTime lastPingTime;
private String lastErrorMessage;
// 启用状态
private Boolean enabled;
// 审计字段
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
// 适配器状态枚举
public enum AdapterStatus {
ONLINE("在线"),
OFFLINE("离线"),
ERROR("错误");
}
```
#### IntegrationLog集成日志
```java
@Entity
@Table(name = "hub_integration_log")
@Data
public class IntegrationLog {
@Id
private UUID id;
private UUID adapterId;
// 请求信息
private String requestId; // 请求唯一标识
private String operation; // 操作类型
private String requestMethod; // GET/POST/PUT/DELETE
private String requestUrl; // 请求URL
private String requestHeaders; // 请求头(JSON)
private String requestBody; // 请求体
private LocalDateTime requestTime;
// 响应信息
private String responseBody; // 响应体
private Integer httpStatus; // HTTP状态码
private String responseHeaders;// 响应头(JSON)
private LocalDateTime responseTime;
// 执行信息
private Long durationMs; // 耗时(ms)
// 状态
private LogStatus status; // SUCCESS/FAIL/TIMEOUT
private String errorMessage; // 错误信息
private String errorStack; // 错误堆栈
// 审计字段
private LocalDateTime createdAt;
}
// 日志状态枚举
public enum LogStatus {
SUCCESS("成功"),
FAIL("失败"),
TIMEOUT("超时");
}
```
#### IoTDeviceIoT设备
```java
@Entity
@Table(name = "hub_iot_device")
@Data
public class IoTDevice {
@Id
private UUID id;
private UUID projectId;
// 设备信息
private String deviceId; // 设备唯一标识
private String deviceName; // 设备名称
private String deviceType; // 设备类型: SENSOR/CAMERA/GATE/...
private String protocol; // MQTT/CoAP/HTTP/HTTPS
// 关联信息
private UUID equipmentId; // 关联设备台账
private UUID spaceNodeId; // 关联空间节点
// MQTT配置
private String mqttBroker; // MQTT服务器地址
private Integer mqttPort; // MQTT端口
private String mqttTopic; // 订阅主题
private String mqttClientId; // 客户端ID
// 最后数据
private String lastPayload; // 最后上报数据
private LocalDateTime lastReportTime;
// 状态
private DeviceStatus status; // ONLINE/OFFLINE/FAULT
private LocalDateTime lastOnlineTime;
// 审计字段
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
// 设备状态枚举
public enum DeviceStatus {
ONLINE("在线"),
OFFLINE("离线"),
FAULT("故障");
}
```
#### DataMapping数据映射
```java
@Entity
@Table(name = "hub_data_mapping")
@Data
public class DataMapping {
@Id
private UUID id;
private UUID projectId;
private UUID adapterId; // 关联适配器
private String name; // 映射名称
private String entityType; // 实体类型: WORK_ORDER/EQUIPMENT/...
// 字段映射
private String fieldMappings; // [{"source": "orderNo", "target": "工单编号", "type": "STRING"}]
// 转换规则
private String transformRules; // 数据转换规则(JSON)
// 启用状态
private Boolean enabled;
// 审计字段
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
```
---
## 三、集成场景
### 3.1 停车系统集成
```java
@Service
public class ParkingIntegrationService {
@Autowired
private IntegrationAdapterRepository adapterRepository;
@Autowired
private IntegrationLogRepository logRepository;
// 同步车位状态
public void syncParkingSpaceStatus(UUID projectId) {
IntegrationAdapter adapter = adapterRepository
.findByProjectIdAndSystemType(projectId, "PARKING")
.orElseThrow(() -> new NotFoundException("停车系统适配器未配置"));
try {
// 调用停车系统API
String response = callExternalApi(adapter, "/api/parking/spaces", "GET", null);
// 解析响应
List<ParkingSpaceDTO> spaces = parseResponse(response);
// 更新本地车位状态
for (ParkingSpaceDTO space : spaces) {
updateLocalParkingSpace(space);
}
} catch (Exception e) {
log.error("同步车位状态失败", e);
throw new IntegrationException("同步车位状态失败: " + e.getMessage());
}
}
// 车牌识别通知
public void handlePlateRecognition(PlateRecognitionEvent event) {
// 处理车牌识别结果
String plateNumber = event.getPlateNumber();
// 查询访客预约
Optional<VisitorAppointment> appointment = visitorService
.findByPlateNumber(plateNumber);
if (appointment.isPresent()) {
// 访客车辆,自动抬杆
sendOpenGateCommand(event.getGateId());
// 记录通行
recordVehicleAccess(plateNumber, event.getGateId(), "VISITOR");
} else {
// 业主车辆,查询业主信息
Optional<Owner> owner = ownerService.findByPlateNumber(plateNumber);
if (owner.isPresent()) {
sendOpenGateCommand(event.getGateId());
recordVehicleAccess(plateNumber, event.getGateId(), "OWNER");
} else {
// 陌生车辆,记录并提示
recordVehicleAccess(plateNumber, event.getGateId(), "UNKNOWN");
}
}
}
}
```
### 3.2 门禁系统集成
```java
@Service
public class AccessIntegrationService {
// 下发访客二维码到门禁
public void syncVisitorCredentialToAccess(UUID credentialId) {
VisitorCredential credential = credentialRepository.findById(credentialId)
.orElseThrow(() -> new NotFoundException("凭证不存在"));
// 查询门禁系统适配器
IntegrationAdapter adapter = adapterRepository
.findByProjectIdAndSystemType(credential.getProjectId(), "ACCESS")
.orElseThrow(() -> new NotFoundException("门禁系统适配器未配置"));
// 构建下发数据
AccessCredentialDTO dto = new AccessCredentialDTO();
dto.setCredentialCode(credential.getCredentialCode());
dto.setQrCode(credential.getQrCode());
dto.setExpireTime(credential.getExpireTime());
dto.setAccessGates(parseAccessGates(credential.getAccessGates()));
// 调用门禁系统API
callExternalApi(adapter, "/api/access/credentials", "POST", dto);
}
// 接收门禁通行记录
@PostMapping("/webhook/access/record")
public void receiveAccessRecord(@RequestBody AccessRecordDTO record) {
// 保存通行记录
VisitorAccessRecord accessRecord = new VisitorAccessRecord();
accessRecord.setId(UUID.randomUUID());
accessRecord.setCredentialCode(record.getCredentialCode());
accessRecord.setAccessTime(record.getAccessTime());
accessRecord.setGateId(record.getGateId());
accessRecord.setAccessType(record.getAccessType()); // ENTRY/EXIT
accessRecord.setPhotoUrl(record.getPhotoUrl());
accessRecordRepository.save(accessRecord);
// 更新凭证使用记录
credentialService.updateCredentialUsage(record.getCredentialCode());
// 通知被访人
notificationService.notifyHost(accessRecord);
}
}
```
### 3.3 IoT设备接入
```java
@Service
public class IoTDeviceService {
@Autowired
private MqttClient mqttClient;
// 设备数据监听
@EventListener
public void onDeviceMessage(DeviceMessageEvent event) {
String deviceId = event.getDeviceId();
String payload = event.getPayload();
// 查询设备
IoTDevice device = deviceRepository.findByDeviceId(deviceId)
.orElse(null);
if (device == null) {
log.warn("未知设备: {}", deviceId);
return;
}
// 更新设备状态
device.setLastPayload(payload);
device.setLastReportTime(LocalDateTime.now());
device.setStatus(DeviceStatus.ONLINE);
device.setLastOnlineTime(LocalDateTime.now());
deviceRepository.save(device);
// 解析数据
DeviceData data = parseDeviceData(device.getDeviceType(), payload);
// 根据设备类型处理
switch (device.getDeviceType()) {
case "TEMPERATURE_SENSOR":
handleTemperatureData(device, data);
break;
case "HUMIDITY_SENSOR":
handleHumidityData(device, data);
break;
case "SMOKE_DETECTOR":
handleSmokeAlarm(device, data);
break;
case "WATER_LEAK_DETECTOR":
handleWaterLeakAlarm(device, data);
break;
default:
log.info("未处理的设备类型: {}", device.getDeviceType());
}
}
// 处理烟雾报警
private void handleSmokeAlarm(IoTDevice device, DeviceData data) {
Boolean smokeDetected = data.getBoolean("smokeDetected");
if (smokeDetected) {
// 创建紧急工单
WorkOrder workOrder = new WorkOrder();
workOrder.setOrderType(WorkOrderType.EMERGENCY);
workOrder.setTitle("烟雾报警: " + device.getDeviceName());
workOrder.setDescription("设备位置: " + device.getSpaceNodeId() +
"\n报警时间: " + LocalDateTime.now());
workOrder.setPriority(WorkOrderPriority.URGENT);
workOrder.setSource(WorkOrderSource.IOT);
workOrder.setEquipmentId(device.getEquipmentId());
workOrder.setSpaceNodeId(device.getSpaceNodeId());
workOrderService.create(workOrder);
// 发送紧急通知
notificationService.sendEmergencyAlert(device, "烟雾报警");
}
}
// 下发命令到设备
public void sendCommandToDevice(String deviceId, String command, Map<String, Object> params) {
IoTDevice device = deviceRepository.findByDeviceId(deviceId)
.orElseThrow(() -> new NotFoundException("设备不存在"));
// 构建MQTT消息
MqttMessage message = new MqttMessage();
message.setTopic(device.getMqttTopic() + "/command");
Map<String, Object> payload = new HashMap<>();
payload.put("command", command);
payload.put("params", params);
payload.put("timestamp", System.currentTimeMillis());
message.setPayload(JsonUtils.toJson(payload).getBytes());
// 发送消息
mqttClient.publish(message);
}
}
```
---
## 四、集成适配器框架
### 4.1 适配器接口
```java
// 集成适配器接口
public interface IntegrationAdapterClient {
// 发送请求
String sendRequest(String operation, String method, String path,
Map<String, String> headers, Object body);
// 健康检查
boolean healthCheck();
// 获取适配器信息
AdapterInfo getAdapterInfo();
}
// HTTP适配器实现
@Component
public class HttpIntegrationAdapter implements IntegrationAdapterClient {
@Autowired
private RestTemplate restTemplate;
private IntegrationAdapter config;
public HttpIntegrationAdapter(IntegrationAdapter config) {
this.config = config;
}
@Override
public String sendRequest(String operation, String method, String path,
Map<String, String> headers, Object body) {
String url = config.getBaseUrl() + path;
// 构建请求头
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
// 添加认证信息
addAuthenticationHeaders(httpHeaders);
// 添加自定义头
if (headers != null) {
headers.forEach(httpHeaders::add);
}
// 构建请求体
HttpEntity<Object> entity = new HttpEntity<>(body, httpHeaders);
// 发送请求
ResponseEntity<String> response = restTemplate.exchange(
url,
HttpMethod.valueOf(method),
entity,
String.class
);
return response.getBody();
}
private void addAuthenticationHeaders(HttpHeaders headers) {
String authType = config.getAuthType();
String authConfig = config.getAuthConfig();
switch (authType) {
case "API_KEY":
Map<String, String> apiKeyConfig = JsonUtils.fromJson(authConfig, Map.class);
String location = apiKeyConfig.get("location");
if ("header".equals(location)) {
headers.add(apiKeyConfig.get("key"), apiKeyConfig.get("value"));
}
break;
case "OAUTH2":
String token = getOAuth2Token(authConfig);
headers.setBearerAuth(token);
break;
case "BASIC":
Map<String, String> basicConfig = JsonUtils.fromJson(authConfig, Map.class);
String credentials = basicConfig.get("username") + ":" + basicConfig.get("password");
String encoded = Base64.getEncoder().encodeToString(credentials.getBytes());
headers.setBasicAuth(encoded);
break;
}
}
@Override
public boolean healthCheck() {
try {
sendRequest("healthCheck", "GET", "/health", null, null);
return true;
} catch (Exception e) {
return false;
}
}
}
```
### 4.2 集成日志记录
```java
@Aspect
@Component
public class IntegrationLogAspect {
@Autowired
private IntegrationLogRepository logRepository;
@Around("execution(* com.ether.hub.client.IntegrationAdapterClient.*(..))")
public Object around(ProceedingJoinPoint point) throws Throwable {
// 获取方法参数
Object[] args = point.getArgs();
String operation = (String) args[0];
String method = (String) args[1];
String path = (String) args[2];
Map<String, String> headers = (Map<String, String>) args[3];
Object body = args[4];
// 创建日志
IntegrationLog log = new IntegrationLog();
log.setId(UUID.randomUUID());
log.setRequestId(UUID.randomUUID().toString());
log.setOperation(operation);
log.setRequestMethod(method);
log.setRequestUrl(path);
log.setRequestHeaders(JsonUtils.toJson(headers));
log.setRequestBody(JsonUtils.toJson(body));
log.setRequestTime(LocalDateTime.now());
long startTime = System.currentTimeMillis();
try {
// 执行目标方法
Object result = point.proceed();
// 记录成功
log.setResponseBody((String) result);
log.setHttpStatus(200);
log.setStatus(LogStatus.SUCCESS);
return result;
} catch (Exception e) {
// 记录失败
log.setStatus(LogStatus.FAIL);
log.setErrorMessage(e.getMessage());
throw e;
} finally {
log.setDurationMs(System.currentTimeMillis() - startTime);
log.setResponseTime(LocalDateTime.now());
log.setCreatedAt(LocalDateTime.now());
// 异步保存日志
logRepository.save(log);
}
}
}
```
---
## 五、API 接口
```java
@RestController
@RequestMapping("/api/v1/hub")
@Tag(name = "集成中心")
public class IntegrationController {
// 适配器管理
@PostMapping("/adapters")
@Operation(summary = "创建适配器")
public Result<IntegrationAdapterVO> createAdapter(@RequestBody @Valid AdapterCreateRequest request);
@GetMapping("/adapters")
@Operation(summary = "查询适配器列表")
public Result<List<IntegrationAdapterVO>> listAdapters();
@GetMapping("/adapters/{id}")
@Operation(summary = "获取适配器详情")
public Result<IntegrationAdapterVO> getAdapter(@PathVariable UUID id);
@PutMapping("/adapters/{id}")
@Operation(summary = "更新适配器")
public Result<IntegrationAdapterVO> updateAdapter(@PathVariable UUID id,
@RequestBody @Valid AdapterUpdateRequest request);
@DeleteMapping("/adapters/{id}")
@Operation(summary = "删除适配器")
public Result<Void> deleteAdapter(@PathVariable UUID id);
@PostMapping("/adapters/{id}/test")
@Operation(summary = "测试连接")
public Result<ConnectionTestResult> testConnection(@PathVariable UUID id);
// 集成日志
@GetMapping("/logs")
@Operation(summary = "查询集成日志")
public Result<Page<IntegrationLogVO>> pageLogs(IntegrationLogQueryRequest request);
// IoT设备管理
@PostMapping("/iot-devices")
@Operation(summary = "注册IoT设备")
public Result<IoTDeviceVO> registerDevice(@RequestBody @Valid IoTDeviceRegisterRequest request);
@GetMapping("/iot-devices")
@Operation(summary = "查询设备列表")
public Result<List<IoTDeviceVO>> listDevices();
@PostMapping("/iot-devices/{id}/command")
@Operation(summary = "发送设备命令")
public Result<Void> sendDeviceCommand(@PathVariable UUID id,
@RequestBody DeviceCommandRequest request);
// 数据映射
@PostMapping("/data-mappings")
@Operation(summary = "创建数据映射")
public Result<DataMappingVO> createDataMapping(@RequestBody @Valid DataMappingCreateRequest request);
@PostMapping("/data-mappings/{id}/transform")
@Operation(summary = "测试数据转换")
public Result<Map<String, Object>> testTransform(@PathVariable UUID id,
@RequestBody Map<String, Object> sourceData);
}
```
---
## 六、实现状态与差异
### 6.1 实现状态
| 功能模块 | 实现状态 | 备注 |
|---------|---------|------|
| IntegrationAdapter | 🔴 未实现 | 待开发 |
| IntegrationLog | 🔴 未实现 | 待开发 |
| IoTDevice | 🔴 未实现 | 待开发 |
| DataMapping | 🔴 未实现 | 待开发 |
| 停车系统集成 | 🔴 未实现 | 待开发 |
| 门禁系统集成 | 🔴 未实现 | 待开发 |
| IoT接入 | 🔴 未实现 | 待开发 |
### 6.2 与设计方案的差异
| 设计项 | 设计方案 | 现有实现 | 差异分析 |
|--------|----------|----------|----------|
| **ether-hub服务** | 完整实现 | 未创建 | 🔴 整个服务缺失 |
| **外部系统集成** | 停车/门禁/政府 | 未实现 | 🔴 功能缺失 |
| **IoT接入** | MQTT/CoAP | 未实现 | 🔴 功能缺失 |
### 6.3 改进计划
| 优先级 | 改进项 | 说明 |
|--------|--------|------|
| P2 | 创建ether-hub服务 | 新建集成中心微服务 |
| P3 | 实现停车集成 | 对接停车系统API |
| P3 | 实现门禁集成 | 对接门禁系统 |
| P3 | 实现IoT接入 | MQTT Broker配置 |
---
## 七、数据库表结构
```sql
-- 集成适配器表
CREATE TABLE hub_integration_adapter (
id UUID PRIMARY KEY,
project_id UUID NOT NULL,
name VARCHAR(100) NOT NULL,
system_type VARCHAR(50) NOT NULL,
description VARCHAR(500),
protocol VARCHAR(20) NOT NULL,
base_url VARCHAR(255) NOT NULL,
timeout INTEGER DEFAULT 30,
retry_count INTEGER DEFAULT 3,
auth_type VARCHAR(20) NOT NULL,
auth_config JSONB,
status VARCHAR(20) DEFAULT 'OFFLINE',
last_ping_time TIMESTAMP,
last_error_message TEXT,
enabled BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- 集成日志表
CREATE TABLE hub_integration_log (
id UUID PRIMARY KEY,
adapter_id UUID NOT NULL,
request_id VARCHAR(50) NOT NULL,
operation VARCHAR(100) NOT NULL,
request_method VARCHAR(10) NOT NULL,
request_url VARCHAR(255) NOT NULL,
request_headers JSONB,
request_body TEXT,
request_time TIMESTAMP NOT NULL,
response_body TEXT,
http_status INTEGER,
response_headers JSONB,
response_time TIMESTAMP,
duration_ms BIGINT,
status VARCHAR(20) NOT NULL,
error_message TEXT,
error_stack TEXT,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- IoT设备表
CREATE TABLE hub_iot_device (
id UUID PRIMARY KEY,
project_id UUID NOT NULL,
device_id VARCHAR(100) NOT NULL,
device_name VARCHAR(100) NOT NULL,
device_type VARCHAR(50) NOT NULL,
protocol VARCHAR(20) NOT NULL,
equipment_id UUID,
space_node_id UUID,
mqtt_broker VARCHAR(255),
mqtt_port INTEGER,
mqtt_topic VARCHAR(255),
mqtt_client_id VARCHAR(100),
last_payload TEXT,
last_report_time TIMESTAMP,
status VARCHAR(20) DEFAULT 'OFFLINE',
last_online_time TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
UNIQUE(project_id, device_id)
);
-- 数据映射表
CREATE TABLE hub_data_mapping (
id UUID PRIMARY KEY,
project_id UUID NOT NULL,
adapter_id UUID,
name VARCHAR(100) NOT NULL,
entity_type VARCHAR(50) NOT NULL,
field_mappings JSONB,
transform_rules JSONB,
enabled BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- 创建索引
CREATE INDEX idx_adapter_project ON hub_integration_adapter(project_id);
CREATE INDEX idx_adapter_system ON hub_integration_adapter(system_type);
CREATE INDEX idx_adapter_status ON hub_integration_adapter(status);
CREATE INDEX idx_log_adapter ON hub_integration_log(adapter_id);
CREATE INDEX idx_log_request ON hub_integration_log(request_id);
CREATE INDEX idx_log_created ON hub_integration_log(created_at);
CREATE INDEX idx_iot_device_project ON hub_iot_device(project_id);
CREATE INDEX idx_iot_device_status ON hub_iot_device(status);
CREATE INDEX idx_mapping_project ON hub_data_mapping(project_id);
```
---
**文档维护**: 本领域技术方案由 ether-hub 服务负责人维护