From e531caf6fd642cb26b547bc20e63b64f9cf502b2 Mon Sep 17 00:00:00 2001
From: wangguangyuan
Date: Sun, 20 Sep 2026 15:35:21 +0800
Subject: [PATCH] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E5=A4=9A=E9=95=9C?=
=?UTF-8?q?=E5=A4=B4=E7=9B=B8=E6=9C=BA=E4=B8=A4=E4=B8=AA=E9=80=9A=E9=81=93?=
=?UTF-8?q?=E5=8F=96=E5=88=B0=E5=90=8C=E4=B8=80=E9=95=9C=E5=A4=B4,?=
=?UTF-8?q?=E5=AF=BC=E8=87=B4=E8=A7=86=E9=A2=91=E7=94=BB=E9=9D=A2=E9=87=8D?=
=?UTF-8?q?=E5=A4=8D=EF=BC=8C=E5=B9=B6=E8=A1=A5=E5=85=85=E7=9B=B4=E8=BF=9E?=
=?UTF-8?q?=E5=A4=B1=E8=B4=A5=E5=8E=9F=E5=9B=A0=E6=97=A5=E5=BF=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../hik/service/HikNvrLiveStreamService.java | 207 ++++++++++++++----
.../nvr/hik/vo/HikNvrLiveStreamConfigVo.java | 13 +-
.../mapper/BasedataEqpbookChannelMapper.java | 18 +-
.../nvr/ivs/pojo/BasedataEqpbookChannel.java | 28 +++
.../ivs/BasedataEqpbookChannelMapper.xml | 21 +-
5 files changed, 232 insertions(+), 55 deletions(-)
create mode 100644 src/main/java/com/inspect/nvr/ivs/pojo/BasedataEqpbookChannel.java
diff --git a/src/main/java/com/inspect/nvr/hik/service/HikNvrLiveStreamService.java b/src/main/java/com/inspect/nvr/hik/service/HikNvrLiveStreamService.java
index d41282e6..2a251279 100644
--- a/src/main/java/com/inspect/nvr/hik/service/HikNvrLiveStreamService.java
+++ b/src/main/java/com/inspect/nvr/hik/service/HikNvrLiveStreamService.java
@@ -13,6 +13,7 @@ import com.inspect.nvr.hik.exception.HikNvrPlaybackException;
import com.inspect.nvr.hik.vo.HikNvrLiveStreamConfigVo;
import com.inspect.nvr.ivs.mapper.BasedataEqpbookChannelMapper;
import com.inspect.nvr.ivs.mapper.IvsCameraMapper;
+import com.inspect.nvr.ivs.pojo.BasedataEqpbookChannel;
import com.inspect.nvr.ivs.pojo.IvsCamera;
import com.inspect.nvr.ivs.service.IvsOpenApiService;
import com.inspect.nvr.service.HikLoginService;
@@ -36,6 +37,9 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -1328,15 +1332,21 @@ public class HikNvrLiveStreamService {
}
// 没有设备 IP 时无法直连,保持原有 NVR 取流行为。
if (camera == null || !hasText(camera.getDevIP())) {
+ log.info("[live] 跳过相机直连 cameraCode={} 原因=ivs_camera未配置dev_ip",
+ target.getCameraCode());
return null;
}
String devIp = camera.getDevIP().trim();
// 已确认该相机凭据不可用时不再重复尝试直连。
if (cameraDirectAuthFailed.contains(devIp)) {
+ log.info("[live] 跳过相机直连 cameraCode={} devIp={} 原因=该相机凭据此前鉴权失败",
+ target.getCameraCode(), devIp);
return null;
}
// RTSP 端口不可达(未开放或被网络隔离)时直接回退,避免白等一次尝试。
if (!isCameraRtspReachable(devIp)) {
+ log.info("[live] 跳过相机直连 cameraCode={} devIp={} 原因=RTSP端口{}不可达",
+ target.getCameraCode(), devIp, cameraRtspPort);
return null;
}
NvrInfo nvrInfo = target.getNvrInfo();
@@ -1346,6 +1356,8 @@ public class HikNvrLiveStreamService {
? cameraPassword : nvrInfo.getPassword();
// 账号缺失时无法建立 RTSP 会话,回退 NVR 路径由 IVS 侧给出明确错误。
if (!hasText(username) || password == null) {
+ log.info("[live] 跳过相机直连 cameraCode={} devIp={} 原因=缺少账号或密码",
+ target.getCameraCode(), devIp);
return null;
}
// 复用统一 URL 构造器,保证中文或特殊字符账号的百分号编码规则与 NVR 路径一致。
@@ -1367,19 +1379,25 @@ public class HikNvrLiveStreamService {
* 计算平台通道对应的相机内通道号。
*
* 同一台多镜头相机(双光谱:可见光 + 热成像)在平台上占多个通道,直连 RTSP 时
- * 必须带上相机自己的通道号,否则两个平台通道会拉到同一个镜头。判定顺序:
+ * 必须带上相机自己的通道号,否则两个平台通道会拉到同一个镜头。台账与通道名都只
+ * 描述单个通道的镜头类型,必须先确认它们确实把同一 IP 下的兄弟通道区分开了才可用,
+ * 否则两个平台通道会一起被映射到同一个相机通道。判定顺序:
*
* - 台账 {@code basedata_eqpbook_channel.channel_type},{@code vl} 可见光、
- * {@code ir} 红外热成像。这是镜头类型的权威来源,{@code ir} 用的相机内通道号
- * 由 {@code hik.playback.live.ir-camera-channel} 指定(默认 2,即 1=可见光、
- * 2=热成像);
- * - 台账缺失或类型与镜头无关(ptz/ng 等)时按平台通道名关键字判断;
- * - 名称也判断不出来时按平台通道编码顺序分配 1..N,整站顺序相反时用
- * {@code hik.playback.live.camera-channel-order=desc} 反转。
+ * {@code ir} 红外热成像。{@code ir} 用的相机内通道号由
+ * {@code hik.playback.live.ir-camera-channel} 指定(默认 2,即 1=可见光、
+ * 2=热成像),{@code vl} 用另一个通道。台账只有在确实把同一 IP 下的兄弟通道
+ * 区分成不同镜头时才生效,见 {@link #resolveLedgerChannel};
+ * - 台账缺失、类型与镜头无关(ptz/ng 等)或不足以区分镜头时,按平台通道名
+ * 关键字判断,名称同样要能区分镜头才生效,见 {@link #resolveNamedChannel};
+ * - 上述信号都判断不出镜头类型时按平台通道编码顺序分配 1..N,整站顺序相反时用
+ * {@code hik.playback.live.camera-channel-order=desc} 反转。顺序分配天然让
+ * 兄弟通道落在不同的相机通道上,默认升序即多数现场的 1=可见光、2=热成像。
*
*
* 只有同 IP 多通道(真双光谱机身机)才做镜头判定:单通道设备的相机内通道号
- * 恒为 1,单镜头热成像相机套用镜头映射反而会取到不存在的通道。
+ * 恒为 1,单镜头热成像相机套用镜头映射反而会取到不存在的通道,因此单通道判断
+ * 排在所有镜头映射之前。
*
* @param cameraCode 当前平台通道编码
* @param devIp 摄像机 IP
@@ -1395,32 +1413,28 @@ public class HikNvrLiveStreamService {
devIp, queryFailure.getMessage());
return 1;
}
- // 单通道相机(绝大多数)直接使用相机通道 1,不必再查台账。
+ // 单通道相机(绝大多数)直接使用相机通道 1,不参与任何镜头映射。
if (peers == null || peers.size() <= 1) {
return 1;
}
- // 同 IP 多通道说明是多镜头机身相机,先按台账镜头类型判定,避免依赖通道名。
- String channelType = resolveChannelType(cameraCode);
- int typedChannel = cameraChannelByChannelType(channelType);
- if (typedChannel > 0) {
- log.info("[live] 按台账镜头类型分配相机通道 cameraCode={} channelType={} cameraChannel={}",
- cameraCode, channelType, typedChannel);
- return typedChannel;
- }
+
int index = -1;
- String cameraName = null;
// 通道记录按 code 升序返回,包含当前通道自己。
for (int position = 0; position < peers.size(); position++) {
IvsCamera peer = peers.get(position);
if (peer != null && cameraCode.equalsIgnoreCase(peer.getCode())) {
index = position;
- cameraName = peer.getName();
break;
}
}
- boolean reverse = "desc".equals(cameraChannelOrder);
- // 台账没有可用的镜头类型时退回名称判断。
- int namedChannel = cameraChannelByThermalName(cameraName);
+
+ // 1.同 IP 多通道说明是多镜头机身相机,先按台账镜头类型判定,避免依赖通道名。
+ int ledgerChannel = resolveLedgerChannel(peers, index);
+ if (ledgerChannel > 0) {
+ return ledgerChannel;
+ }
+ // 2.台账不具备区分镜头的能力时退回名称判断。
+ int namedChannel = resolveNamedChannel(peers, index);
if (namedChannel > 0) {
return namedChannel;
}
@@ -1428,33 +1442,147 @@ public class HikNvrLiveStreamService {
// 当前通道不在同 IP 列表中(数据异常)时退回相机通道 1。
return 1;
}
- // 名称也无法区分(例如两个通道都叫“双光谱”)时按平台通道顺序分配。
- int ordered = reverse ? peers.size() - 1 - index : index;
- return ordered + 1;
+ // 3.名称也无法区分(例如两个通道都叫“双光谱”,或都是可见光)时按平台通道顺序分配。
+ boolean reverse = "desc".equals(cameraChannelOrder);
+ int orderedChannel = (reverse ? peers.size() - 1 - index : index) + 1;
+ log.info("[live] 台账与通道名都未区分镜头,按平台通道顺序分配相机通道"
+ + " cameraCode={} index={}/{} order={} cameraChannel={}",
+ cameraCode, index, peers.size(), cameraChannelOrder, orderedChannel);
+ return orderedChannel;
}
/**
- * 读取台账 basedata_eqpbook_channel.channel_type。
+ * 按台账镜头类型判定当前通道的相机内通道号。
*
- * @param cameraCode IVS 完整摄像机编码
- * @return 台账通道类型(vl/ir 等);无记录、字段为空或查询异常时返回 null
+ * 台账能用来判定镜头的前提,是它确实把同一 IP 下的兄弟通道区分成了不同镜头。
+ * 现场既有“1=可见光、2=热成像”的双光谱相机,也有两个镜头都是可见光、台账两个
+ * 通道都记为 {@code vl} 的机身机;后者逐通道套用类型会让两个平台通道解析到同一个
+ * 相机通道,两个窗口拉到同一个镜头。因此这里先统计兄弟通道按台账解析出的相机
+ * 通道,只有一个取值时视为台账不具备区分能力,返回 0 交给通道名与平台通道顺序
+ * 兜底,由顺序分配把两个通道分别落到相机通道 1 和 2:双可见光相机两路各自取到
+ * 一个可见光镜头,双光谱相机则分别取到可见光与热成像镜头。
+ *
+ * @param peers 同一 IP 下的平台通道,按完整编码升序
+ * @param index 当前通道在 {@code peers} 中的下标,-1 表示不在列表中
+ * @return 台账判定的相机内通道号;台账不具备区分能力时返回 0
+ */
+ private int resolveLedgerChannel(List peers, int index) {
+ if (index < 0) {
+ return 0;
+ }
+ // 同一ip下的key:channelCode,value:channelType
+ Map channelTypes = loadChannelTypes(peers);
+ Set resolvedChannels = new HashSet<>(peers.size());
+ for (IvsCamera peer : peers) {
+ if (peer == null) {
+ continue;
+ }
+ int peerChannel = cameraChannelByChannelType(channelTypes.get(peer.getCode()));
+ if (peerChannel > 0) {
+ resolvedChannels.add(peerChannel);
+ }
+ }
+ // 兄弟通道全部落在同一个相机通道上,说明台账没有区分镜头,不能据此分配。
+ if (resolvedChannels.size() <= 1) {
+ return 0;
+ }
+ String cameraCode = peers.get(index).getCode();
+ String channelType = channelTypes.get(cameraCode);
+ int typedChannel = cameraChannelByChannelType(channelType);
+ if (typedChannel > 0) {
+ log.info("[live] 按台账镜头类型分配相机通道 cameraCode={} channelType={} cameraChannel={}",
+ cameraCode, channelType, typedChannel);
+ }
+ return typedChannel;
+ }
+
+ /**
+ * 按平台通道名判定当前通道的相机内通道号。
+ *
+ * 与台账一样必须先确认信号能区分镜头:两个通道都带“可见光”的相机(双可见光
+ * 机身机,或热成像那一路名称没标注)逐个通道套用关键字会让两个平台通道落到同一个
+ * 相机通道。因此兄弟通道名称解析出的相机通道只有一个取值时返回 0,交给平台通道
+ * 顺序兜底。
+ *
+ * @param peers 同一 IP 下的平台通道,按完整编码升序
+ * @param index 当前通道在 {@code peers} 中的下标,-1 表示不在列表中
+ * @return 名称判定的相机内通道号;名称不足以区分镜头时返回 0
*/
- private String resolveChannelType(String cameraCode) {
+ private int resolveNamedChannel(List peers, int index) {
+ if (index < 0) {
+ return 0;
+ }
+ Set resolvedChannels = new HashSet<>(peers.size());
+ for (IvsCamera peer : peers) {
+ if (peer == null) {
+ continue;
+ }
+ int peerChannel = cameraChannelByThermalName(peer.getName());
+ if (peerChannel > 0) {
+ resolvedChannels.add(peerChannel);
+ }
+ }
+ // 各通道名称指向同一个镜头,说明名称没有区分镜头,不能据此分配。
+ if (resolvedChannels.size() <= 1) {
+ return 0;
+ }
+ IvsCamera current = peers.get(index);
+ int namedChannel = cameraChannelByThermalName(current.getName());
+ if (namedChannel > 0) {
+ log.info("[live] 按通道名分配相机通道 cameraCode={} cameraName={} cameraChannel={}",
+ current.getCode(), current.getName(), namedChannel);
+ }
+ return namedChannel;
+ }
+
+ /**
+ * 批量读取同一 IP 下各平台通道的台账镜头类型。
+ *
+ * @param peers 同一 IP 下的平台通道
+ * @return 摄像机编码到台账 channel_type 的映射;同一编码有多条历史台账时取最新一条,
+ * 无记录或查询异常时该编码不出现在映射中
+ */
+ private Map loadChannelTypes(List peers) {
+ List codes = new ArrayList<>(peers.size());
+ for (IvsCamera peer : peers) {
+ if (peer != null && hasText(peer.getCode())) {
+ codes.add(peer.getCode());
+ }
+ }
+ if (codes.isEmpty()) {
+ return Collections.emptyMap();
+ }
+ Map channelTypes = new HashMap<>(codes.size());
try {
- String channelType = eqpbookChannelMapper.selectChannelTypeByChannelCode(cameraCode);
- // 台账没有该编码时保持为空,由调用方回退名称与顺序判定。
- return hasText(channelType) ? channelType.trim() : null;
+ List records =
+ eqpbookChannelMapper.selectChannelTypesByChannelCodes(codes);
+ for (BasedataEqpbookChannel record : records) {
+ if (record == null || !hasText(record.getChannelCode())) {
+ continue;
+ }
+ // 同一编码存在历史重复台账时查询按 channel_id 倒序返回,保留首条即最新记录。
+ if (channelTypes.containsKey(record.getChannelCode())) {
+ continue;
+ }
+ channelTypes.put(record.getChannelCode(),
+ hasText(record.getChannelType()) ? record.getChannelType().trim() : null);
+ }
} catch (RuntimeException queryFailure) {
// 台账不是取流必需数据,查询失败只记录原因,不阻断实时预览。
- log.warn("[live] 读取basedata_eqpbook_channel失败,回退名称判定 cameraCode={}:{}",
- cameraCode, queryFailure.getMessage());
- return null;
+ log.warn("[live] 读取basedata_eqpbook_channel失败,回退通道名判定 同IP通道数={}:{}",
+ codes.size(), queryFailure.getMessage());
+ return Collections.emptyMap();
}
+ return channelTypes;
}
/**
* 按台账通道类型返回可见光/热成像对应的相机内通道号。
*
+ * 只做单点映射,不判断台账是否真的区分了兄弟通道:两个通道都记 {@code vl} 时
+ * 会一起落到同一个相机通道。因此调用方必须先确认台账具备区分能力,见
+ * {@link #resolveLedgerChannel}。
+ *
* @param channelType 台账 channel_type,vl 可见光 / ir 红外热成像
* @return {@code ir} 返回配置的相机内通道号,{@code vl} 返回另一个通道;无法判定返回 0
*/
@@ -1486,8 +1614,9 @@ public class HikNvrLiveStreamService {
/**
* 按通道名判断镜头类型并返回对应的相机内通道号。
*
- * 只在台账 {@code channel_type} 缺失时使用:部分平台通道名带“热成像/可见光”
- * 字样,但名称由台账人员维护、可能两个通道同名,因此不作为首选依据。
+ * 只在台账 {@code channel_type} 缺失或不具备区分能力时使用:部分平台通道名带
+ * “热成像/可见光”字样,但名称由台账人员维护、可能两个通道同名。只做单点映射,
+ * 调用方必须先确认名称能区分兄弟通道,见 {@link #resolveNamedChannel}。
*
* @param cameraName 平台通道名
* @return 热成像/测温返回配置的相机内通道号,可见光返回另一个通道,无法判断返回 0
@@ -1569,9 +1698,9 @@ public class HikNvrLiveStreamService {
command.add("error");
command.add("-nostats");
// 海康 RTSP 不在 SDP 中给出帧率,ffmpeg 会自愿读满默认 5 秒探测窗口来估帧率;
- // 显式压到 3 秒让推流提前约 2 秒开始,否则首次尝试 5 秒预算会在探测完成前杀掉进程。
+ // 压到 1.5 秒让推流提前约 3.5 秒开始:25fps 子码流下约 37 帧已足够估准帧率。
command.add("-analyzeduration");
- command.add("3000000");
+ command.add("1500000");
command.add("-rtsp_transport");
command.add("tcp");
command.add("-i");
diff --git a/src/main/java/com/inspect/nvr/hik/vo/HikNvrLiveStreamConfigVo.java b/src/main/java/com/inspect/nvr/hik/vo/HikNvrLiveStreamConfigVo.java
index 5b1946b4..1343e5c8 100644
--- a/src/main/java/com/inspect/nvr/hik/vo/HikNvrLiveStreamConfigVo.java
+++ b/src/main/java/com/inspect/nvr/hik/vo/HikNvrLiveStreamConfigVo.java
@@ -54,8 +54,8 @@ public class HikNvrLiveStreamConfigVo {
@Value("${hik.playback.live.stop-grace-millis:2000}")
private long stopGraceMillis = 2000L;
/** 单台 NVR 允许同时播放的实时流上限,0 表示不限制。 */
- @Value("${hik.playback.live.max-sessions-per-nvr:8}")
- private int maxSessionsPerNvr = 8;
+ @Value("${hik.playback.live.max-sessions-per-nvr:0}")
+ private int maxSessionsPerNvr = 0;
/** 是否回收长时间无人观看的实时流。 */
@Value("${hik.playback.live.idle-reap-enabled:true}")
private boolean idleReapEnabled = true;
@@ -90,6 +90,9 @@ public class HikNvrLiveStreamConfigVo {
* 是可见光、{@code ir} 是热成像,本值决定 {@code ir} 使用哪个相机内通道:
* 默认 2,即 1=可见光、2=热成像;{@code vl} 自动使用另一个通道。
*
+ * 台账只在该相机的多个平台通道确实被区分成不同镜头时才生效;两个通道都记为
+ * {@code vl} 时台账不具备区分能力,会退回通道名与平台通道顺序判定。
+ *
* 现场若发现两个镜头画面互换,把本值改成 1 即可,台账判定与通道名判定会
* 一起反转,不需要再动 {@code camera-channel-order}。
*/
@@ -99,8 +102,10 @@ public class HikNvrLiveStreamConfigVo {
* 同一 IP 多通道相机的相机内通道分配顺序。
*
* {@code asc}(默认)按平台通道编码升序分配 1..N,{@code desc} 反向。
- * 只在台账 {@code channel_type} 与平台通道名都判断不出可见光/热成像时才生效;
- * 镜头判定结果不由本配置调整,改镜头方向请用 {@code ir-camera-channel}。
+ * 台账与平台通道名都判断不出可见光/热成像,或台账把兄弟通道记成了同一种类型
+ * (例如两个通道都是 {@code vl})时生效,此时升序正好对应多数现场的
+ * 1=可见光、2=热成像;镜头判定结果不由本配置调整,改镜头方向请用
+ * {@code ir-camera-channel}。
*/
@Value("${hik.playback.live.camera-channel-order:asc}")
private String cameraChannelOrder = "asc";
diff --git a/src/main/java/com/inspect/nvr/ivs/mapper/BasedataEqpbookChannelMapper.java b/src/main/java/com/inspect/nvr/ivs/mapper/BasedataEqpbookChannelMapper.java
index fdfb59ef..09d92d2e 100644
--- a/src/main/java/com/inspect/nvr/ivs/mapper/BasedataEqpbookChannelMapper.java
+++ b/src/main/java/com/inspect/nvr/ivs/mapper/BasedataEqpbookChannelMapper.java
@@ -1,8 +1,13 @@
package com.inspect.nvr.ivs.mapper;
+import com.inspect.nvr.ivs.pojo.BasedataEqpbookChannel;
+
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
+import java.util.Collection;
+import java.util.List;
+
/**
* 台账通道表 basedata_eqpbook_channel 的 MyBatis Mapper。
*
@@ -16,10 +21,15 @@ import org.apache.ibatis.annotations.Param;
public interface BasedataEqpbookChannelMapper {
/**
- * 按 IVS 完整摄像机编码读取台账通道类型。
+ * 按 IVS 完整摄像机编码批量读取台账通道类型。
+ *
+ * 判定镜头必须同时看同一 IP 下的兄弟通道,以确认台账是否真的把两个镜头
+ * 区分开了,因此这里一次取回整组编码,避免逐通道查询。
*
- * @param channelCode IVS 完整摄像机编码,与 {@code ivs_camera.code} 同值
- * @return 台账 channel_type(vl/ir 等);无记录或字段为空时返回 null
+ * @param channelCodes IVS 完整摄像机编码集合,与 {@code ivs_camera.code} 同值,不可为空
+ * @return 按 channel_code 升序、同一编码内按 channel_id 倒序排列的记录;调用方
+ * 对每个编码取首条即为最新台账记录,无记录的编码不会出现在结果中
*/
- String selectChannelTypeByChannelCode(@Param("channelCode") String channelCode);
+ List selectChannelTypesByChannelCodes(
+ @Param("channelCodes") Collection channelCodes);
}
diff --git a/src/main/java/com/inspect/nvr/ivs/pojo/BasedataEqpbookChannel.java b/src/main/java/com/inspect/nvr/ivs/pojo/BasedataEqpbookChannel.java
new file mode 100644
index 00000000..acf9692e
--- /dev/null
+++ b/src/main/java/com/inspect/nvr/ivs/pojo/BasedataEqpbookChannel.java
@@ -0,0 +1,28 @@
+package com.inspect.nvr.ivs.pojo;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 台账通道信息,对应 basedata_eqpbook_channel 表的镜头判定投影。
+ *
+ * 实时预览直连摄像机时只关心两列:{@code channel_code} 用于关联平台通道,
+ * {@code channel_type} 用于判定可见光/热成像镜头。台账同一编码存在历史重复
+ * 记录时,查询按 channel_id 倒序返回,调用方保留首条即可取到最新记录。
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class BasedataEqpbookChannel implements Serializable {
+
+ /** 序列化版本号。 */
+ private static final long serialVersionUID = 1L;
+
+ /** 完整摄像机编码,与 ivs_camera.code 同值。 */
+ private String channelCode;
+ /** 通道镜头类型:vl 可见光、ir 红外(热成像),另有 ptz 云台、ng 导航等。 */
+ private String channelType;
+}
diff --git a/src/main/resources/mapper/ivs/BasedataEqpbookChannelMapper.xml b/src/main/resources/mapper/ivs/BasedataEqpbookChannelMapper.xml
index a37224df..20b06ee7 100644
--- a/src/main/resources/mapper/ivs/BasedataEqpbookChannelMapper.xml
+++ b/src/main/resources/mapper/ivs/BasedataEqpbookChannelMapper.xml
@@ -5,17 +5,22 @@
-