|
|
@ -49,6 +49,26 @@ public class HikNvrCameraTargetService { |
|
|
public HikNvrCameraTarget resolve(String cameraCode) { |
|
|
public HikNvrCameraTarget resolve(String cameraCode) { |
|
|
// 先校验完整编码结构,确保后续拆分不会把错误输入映射到其他设备。 |
|
|
// 先校验完整编码结构,确保后续拆分不会把错误输入映射到其他设备。 |
|
|
String normalizedCode = normalizeCameraCode(cameraCode); |
|
|
String normalizedCode = normalizeCameraCode(cameraCode); |
|
|
|
|
|
// 摄像机必须已同步到 ivs_camera,禁止仅凭编码格式访问未同步设备。 |
|
|
|
|
|
requireSyncedCamera(normalizedCode); |
|
|
|
|
|
// 设备编码最后三位是 IVS 页面逻辑通道号。 |
|
|
|
|
|
int channel = parseChannel(normalizedCode); |
|
|
|
|
|
// 去掉通道三位后恢复摄像机所属 NVR 的完整 IVS 编码。 |
|
|
|
|
|
String expectedNvrCode = buildExpectedNvrCode(normalizedCode); |
|
|
|
|
|
// 从现有 NVR 数据表中寻找唯一启用且编码完全匹配的设备。 |
|
|
|
|
|
IvsNvr matchedNvr = findUniqueEnabledNvr(normalizedCode, expectedNvrCode); |
|
|
|
|
|
// 按匹配出的数据库 IP 读取完整端口和凭据,供海康回放能力在服务端使用。 |
|
|
|
|
|
NvrInfo nvrInfo = resolveNvrCredential(matchedNvr); |
|
|
|
|
|
// 返回统一目标,调用方不再接受浏览器提交的 NVR IP 或通道号。 |
|
|
|
|
|
return new HikNvrCameraTarget(normalizedCode, channel, nvrInfo); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 校验规范化后的完整编码确实存在于 ivs_camera。 |
|
|
|
|
|
* |
|
|
|
|
|
* @param normalizedCode 已规范化的完整摄像机编码 |
|
|
|
|
|
*/ |
|
|
|
|
|
private void requireSyncedCamera(String normalizedCode) { |
|
|
// 必须在 ivs_camera 中精确命中该编码,禁止仅凭编码格式访问未同步设备。 |
|
|
// 必须在 ivs_camera 中精确命中该编码,禁止仅凭编码格式访问未同步设备。 |
|
|
IvsCamera camera = ivsCameraMapper.selectByCode(normalizedCode); |
|
|
IvsCamera camera = ivsCameraMapper.selectByCode(normalizedCode); |
|
|
// 数据库没有该摄像机时返回明确的资源不存在错误。 |
|
|
// 数据库没有该摄像机时返回明确的资源不存在错误。 |
|
|
@ -56,12 +76,18 @@ public class HikNvrCameraTargetService { |
|
|
throw HikNvrPlaybackException.notFound( |
|
|
throw HikNvrPlaybackException.notFound( |
|
|
"ivs_camera中未找到cameraCode:" + normalizedCode); |
|
|
"ivs_camera中未找到cameraCode:" + normalizedCode); |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 拆出 # 前设备编码和 # 后域编码,二者共同唯一定位所属 NVR。 |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 从完整编码的设备段末尾解析 IVS 页面逻辑通道号。 |
|
|
|
|
|
* |
|
|
|
|
|
* @param normalizedCode 已规范化的完整摄像机编码 |
|
|
|
|
|
* @return 大于 0 的页面逻辑通道号 |
|
|
|
|
|
*/ |
|
|
|
|
|
private int parseChannel(String normalizedCode) { |
|
|
|
|
|
// 拆出 # 前设备编码,末尾三位即通道号。 |
|
|
int separator = normalizedCode.indexOf('#'); |
|
|
int separator = normalizedCode.indexOf('#'); |
|
|
String deviceCode = normalizedCode.substring(0, separator); |
|
|
String deviceCode = normalizedCode.substring(0, separator); |
|
|
String domainCode = normalizedCode.substring(separator + 1); |
|
|
|
|
|
// 设备编码最后三位是 IVS 页面逻辑通道号。 |
|
|
|
|
|
int channel = Integer.parseInt( |
|
|
int channel = Integer.parseInt( |
|
|
deviceCode.substring(deviceCode.length() - CHANNEL_CODE_LENGTH)); |
|
|
deviceCode.substring(deviceCode.length() - CHANNEL_CODE_LENGTH)); |
|
|
// 000 不能作为可寻址通道,避免把无效摄像机编码交给海康 SDK。 |
|
|
// 000 不能作为可寻址通道,避免把无效摄像机编码交给海康 SDK。 |
|
|
@ -69,11 +95,35 @@ public class HikNvrCameraTargetService { |
|
|
throw HikNvrPlaybackException.badRequest( |
|
|
throw HikNvrPlaybackException.badRequest( |
|
|
"cameraCode未包含有效的三位通道号:" + normalizedCode); |
|
|
"cameraCode未包含有效的三位通道号:" + normalizedCode); |
|
|
} |
|
|
} |
|
|
|
|
|
return channel; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 由完整摄像机编码推导所属 NVR 的完整 IVS 编码。 |
|
|
|
|
|
* |
|
|
|
|
|
* @param normalizedCode 已规范化的完整摄像机编码 |
|
|
|
|
|
* @return 设备编码去掉通道位后拼接域编码得到的 NVR 编码 |
|
|
|
|
|
*/ |
|
|
|
|
|
private String buildExpectedNvrCode(String normalizedCode) { |
|
|
|
|
|
// 拆出 # 前设备编码和 # 后域编码,二者共同唯一定位所属 NVR。 |
|
|
|
|
|
int separator = normalizedCode.indexOf('#'); |
|
|
|
|
|
String deviceCode = normalizedCode.substring(0, separator); |
|
|
|
|
|
String domainCode = normalizedCode.substring(separator + 1); |
|
|
// 去掉通道三位后恢复摄像机所属 NVR 的完整 IVS 编码。 |
|
|
// 去掉通道三位后恢复摄像机所属 NVR 的完整 IVS 编码。 |
|
|
String expectedNvrCode = deviceCode.substring( |
|
|
|
|
|
|
|
|
return deviceCode.substring( |
|
|
0, deviceCode.length() - CHANNEL_CODE_LENGTH) |
|
|
0, deviceCode.length() - CHANNEL_CODE_LENGTH) |
|
|
+ "#" + domainCode; |
|
|
+ "#" + domainCode; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 在启用 NVR 中查找编码与推导结果完全匹配且唯一的设备。 |
|
|
|
|
|
* |
|
|
|
|
|
* @param normalizedCode 已规范化的完整摄像机编码,用于错误提示 |
|
|
|
|
|
* @param expectedNvrCode 由摄像机编码推导出的 NVR 完整编码 |
|
|
|
|
|
* @return 唯一匹配的启用 NVR 记录 |
|
|
|
|
|
*/ |
|
|
|
|
|
private IvsNvr findUniqueEnabledNvr( |
|
|
|
|
|
String normalizedCode, String expectedNvrCode) { |
|
|
// 从现有 NVR 数据表中寻找唯一启用且编码完全匹配的设备。 |
|
|
// 从现有 NVR 数据表中寻找唯一启用且编码完全匹配的设备。 |
|
|
List<IvsNvr> nvrs = ivsNvrMapper.selectAll(); |
|
|
List<IvsNvr> nvrs = ivsNvrMapper.selectAll(); |
|
|
IvsNvr matchedNvr = null; |
|
|
IvsNvr matchedNvr = null; |
|
|
@ -102,12 +152,19 @@ public class HikNvrCameraTargetService { |
|
|
throw HikNvrPlaybackException.notFound( |
|
|
throw HikNvrPlaybackException.notFound( |
|
|
"未找到cameraCode对应的启用NVR:" + normalizedCode); |
|
|
"未找到cameraCode对应的启用NVR:" + normalizedCode); |
|
|
} |
|
|
} |
|
|
|
|
|
return matchedNvr; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 按匹配出的数据库 IP 读取完整端口和凭据,供海康回放能力在服务端使用。 |
|
|
|
|
|
NvrInfo nvrInfo = credentialService.resolveByIp( |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 按匹配 NVR 的数据库 IP 读取回放所需的端口与凭据。 |
|
|
|
|
|
* |
|
|
|
|
|
* @param matchedNvr 已唯一匹配的启用 NVR 记录 |
|
|
|
|
|
* @return 包含端口和凭据的 NVR 连接信息 |
|
|
|
|
|
*/ |
|
|
|
|
|
private NvrInfo resolveNvrCredential(IvsNvr matchedNvr) { |
|
|
|
|
|
// IP 已在 isEnabledTarget 中确认非空,这里统一去除首尾空白。 |
|
|
|
|
|
return credentialService.resolveByIp( |
|
|
matchedNvr.getNvrIp().trim()); |
|
|
matchedNvr.getNvrIp().trim()); |
|
|
// 返回统一目标,调用方不再接受浏览器提交的 NVR IP 或通道号。 |
|
|
|
|
|
return new HikNvrCameraTarget(normalizedCode, channel, nvrInfo); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
|