Commit e53b4b8472dce0c63e83f9d697c1c2b728aedb76

Authored by xp.Huang
1 parent c14ce761

feat: 流媒体配置增加协议字段,支持RTSP RTMP HLS视频拉流

@@ -14,6 +14,11 @@ COMMENT ON COLUMN "public"."tk_things_model"."extension_desc" IS 'TCP/UDP属性æ @@ -14,6 +14,11 @@ COMMENT ON COLUMN "public"."tk_things_model"."extension_desc" IS 'TCP/UDP属性æ
14 ALTER TABLE "public"."tk_video_platform" ADD COLUMN "remark" varchar(255) COLLATE "pg_catalog"."default"; 14 ALTER TABLE "public"."tk_video_platform" ADD COLUMN "remark" varchar(255) COLLATE "pg_catalog"."default";
15 COMMENT ON COLUMN "public"."tk_video_platform"."remark" IS '备注'; 15 COMMENT ON COLUMN "public"."tk_video_platform"."remark" IS '备注';
16 16
  17 +ALTER TABLE "public"."tk_video_platform" ADD COLUMN "protocol_type" varchar(10) COLLATE "pg_catalog"."default";
  18 +COMMENT ON COLUMN "public"."tk_video_platform"."protocol_type" IS '视频协议:HLS RTSP RTMP';
  19 +
  20 +ALTER TABLE "public"."tk_device_camera" ADD COLUMN "remark" varchar(255) COLLATE "pg_catalog"."default";
  21 +COMMENT ON COLUMN "public"."tk_device_camera"."remark" IS '备注';
17 22
18 23
19 24
@@ -12,6 +12,7 @@ import org.thingsboard.server.common.data.yunteng.common.DeleteGroup; @@ -12,6 +12,7 @@ import org.thingsboard.server.common.data.yunteng.common.DeleteGroup;
12 import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO; 12 import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO;
13 import org.thingsboard.server.common.data.yunteng.dto.TkVideoDTO; 13 import org.thingsboard.server.common.data.yunteng.dto.TkVideoDTO;
14 import org.thingsboard.server.common.data.yunteng.enums.OrderTypeEnum; 14 import org.thingsboard.server.common.data.yunteng.enums.OrderTypeEnum;
  15 +import org.thingsboard.server.common.data.yunteng.utils.tools.ProtocolType;
15 import org.thingsboard.server.common.data.yunteng.utils.tools.ResponseResult; 16 import org.thingsboard.server.common.data.yunteng.utils.tools.ResponseResult;
16 import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; 17 import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData;
17 import org.thingsboard.server.controller.BaseController; 18 import org.thingsboard.server.controller.BaseController;
@@ -31,71 +32,101 @@ import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant. @@ -31,71 +32,101 @@ import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.
31 @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{})") 32 @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{})")
32 public class TkVideoController extends BaseController { 33 public class TkVideoController extends BaseController {
33 34
34 - private final TkVideoService videoService; 35 + private final TkVideoService videoService;
35 36
36 - @GetMapping(params = {PAGE_SIZE, PAGE})  
37 - @ApiOperation("分页")  
38 - public TkPageData<TkVideoDTO> pageAlarmProfile(  
39 - @RequestParam(PAGE_SIZE) int pageSize,  
40 - @RequestParam(PAGE) int page,  
41 - @RequestParam(value = "status", required = false) Boolean status,  
42 - @RequestParam(value = "name", required = false) String name,  
43 - @RequestParam(value = "organizationId", required = false) String organizationId,  
44 - @RequestParam(value = ORDER_FILED, required = false) String orderBy,  
45 - @RequestParam(value = ORDER_TYPE, required = false) OrderTypeEnum orderType)  
46 - throws ThingsboardException { 37 + @GetMapping(params = {PAGE_SIZE, PAGE})
  38 + @ApiOperation("分页")
  39 + public TkPageData<TkVideoDTO> pageAlarmProfile(
  40 + @RequestParam(PAGE_SIZE) int pageSize,
  41 + @RequestParam(PAGE) int page,
  42 + @RequestParam(value = "status", required = false) Boolean status,
  43 + @RequestParam(value = "name", required = false) String name,
  44 + @RequestParam(value = "organizationId", required = false) String organizationId,
  45 + @RequestParam(value = ORDER_FILED, required = false) String orderBy,
  46 + @RequestParam(value = ORDER_TYPE, required = false) OrderTypeEnum orderType)
  47 + throws ThingsboardException {
47 48
  49 + IPage<TkVideoEntity> pageInfrom = videoService.getPage(page, pageSize, orderBy, orderType);
  50 + return videoService.pageDatas(
  51 + pageInfrom,
  52 + getCurrentUser().isPtTenantAdmin(),
  53 + getCurrentUser().getCurrentTenantId(),
  54 + getCurrentUser().getCurrentUserId(),
  55 + organizationId,
  56 + name,
  57 + status);
  58 + }
48 59
49 - IPage<TkVideoEntity> pageInfrom = videoService.getPage(page, pageSize, orderBy, orderType);  
50 - return videoService.pageDatas(pageInfrom,  
51 - getCurrentUser().isPtTenantAdmin(),  
52 - getCurrentUser().getCurrentTenantId(),  
53 - getCurrentUser().getCurrentUserId()  
54 - , organizationId, name, status);  
55 - } 60 + @PostMapping
  61 + @ApiOperation("新增|编辑")
  62 + @PreAuthorize(
  63 + "@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:video:post','api:yt:video:update'})")
  64 + public TkVideoDTO saveOrUpdateAlarmProfile(@Validated @RequestBody TkVideoDTO dto)
  65 + throws ThingsboardException {
  66 + dto.setTenantId(getCurrentUser().getCurrentTenantId());
  67 + return videoService.saveOrUpdate(dto);
  68 + }
56 69
57 - @PostMapping  
58 - @ApiOperation("新增|编辑")  
59 - @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:video:post','api:yt:video:update'})")  
60 - public TkVideoDTO saveOrUpdateAlarmProfile(  
61 - @Validated @RequestBody TkVideoDTO dto) throws ThingsboardException {  
62 - dto.setTenantId(getCurrentUser().getCurrentTenantId());  
63 - return videoService.saveOrUpdate(dto);  
64 - } 70 + @DeleteMapping
  71 + @ApiOperation("删除")
  72 + @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:video:delete'})")
  73 + public boolean deleteAlarmProfile(@Validated(DeleteGroup.class) @RequestBody DeleteDTO deleteDTO)
  74 + throws ThingsboardException {
  75 + deleteDTO.setTenantId(getCurrentUser().getCurrentTenantId());
  76 + return videoService.deleteDataByIds(deleteDTO);
  77 + }
65 78
66 - @DeleteMapping  
67 - @ApiOperation("删除")  
68 - @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:video:delete'})")  
69 - public boolean deleteAlarmProfile(@Validated(DeleteGroup.class) @RequestBody DeleteDTO deleteDTO)  
70 - throws ThingsboardException {  
71 - deleteDTO.setTenantId(getCurrentUser().getCurrentTenantId());  
72 - return videoService.deleteDataByIds(deleteDTO);  
73 - } 79 + @GetMapping("{entityId}")
  80 + @ApiOperation("详情")
  81 + @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:video:get'})")
  82 + public TkVideoDTO detail(@PathVariable("entityId") String entityId) throws ThingsboardException {
  83 + return videoService.detail(getCurrentUser().getCurrentTenantId(), entityId);
  84 + }
74 85
75 - @GetMapping("{entityId}")  
76 - @ApiOperation("详情")  
77 - @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:video:get'})")  
78 - public TkVideoDTO detail(@PathVariable("entityId") String entityId)  
79 - throws ThingsboardException {  
80 - return videoService.detail(getCurrentUser().getCurrentTenantId(), entityId);  
81 - } 86 + @GetMapping("url/{entityId}")
  87 + @ApiOperation("获取平台视频流播放地址")
  88 + public ResponseResult<Map<String, String>> getCameraPreviewURL(
  89 + @PathVariable("entityId") String entityId) throws ThingsboardException {
  90 + return getCameraURL(entityId, null);
  91 + }
82 92
83 - @GetMapping("url/{entityId}")  
84 - @ApiOperation("获取平台视频流播放地址")  
85 - public ResponseResult<Map<String,String>> getCameraPreviewURL(@PathVariable("entityId") String entityId)  
86 - throws ThingsboardException {  
87 - String url = videoService.getCameraPreviewURL(getCurrentUser().isTenantAdmin(), getCurrentUser().getCurrentUserId(),  
88 - getCurrentUser().getCurrentTenantId(), entityId);  
89 - Map<String,String> map = new HashMap<>();  
90 - map.put("url",url);  
91 - return ResponseResult.success(map); 93 + @GetMapping("url/{entityId}/{protocolType}")
  94 + @ApiOperation("获取平台视频流播放地址")
  95 + public ResponseResult<Map<String, String>> getCameraPreviewURL(
  96 + @PathVariable("entityId") String entityId,
  97 + @PathVariable("protocolType") ProtocolType protocolType)
  98 + throws ThingsboardException {
  99 + if (!protocolType.equals(ProtocolType.RTSP)
  100 + || !protocolType.equals(ProtocolType.RTMP)
  101 + || !protocolType.equals(ProtocolType.HLS)) {
  102 + protocolType = ProtocolType.HLS;
92 } 103 }
93 - @GetMapping("list")  
94 - @ApiOperation("获取视频列表")  
95 - public ResponseResult<List<TkVideoDTO>> getVideoList(  
96 - @RequestParam(value = "organizationId", required = false) String organizationId) throws ThingsboardException  
97 - {  
98 - return ResponseResult.success(videoService.getVideoList(organizationId,getCurrentUser().getCurrentTenantId())); 104 + return getCameraURL(entityId, protocolType);
  105 + }
  106 +
  107 + @GetMapping("list")
  108 + @ApiOperation("获取视频列表")
  109 + public ResponseResult<List<TkVideoDTO>> getVideoList(
  110 + @RequestParam(value = "organizationId", required = false) String organizationId)
  111 + throws ThingsboardException {
  112 + return ResponseResult.success(
  113 + videoService.getVideoList(organizationId, getCurrentUser().getCurrentTenantId()));
  114 + }
  115 +
  116 + private ResponseResult<Map<String, String>> getCameraURL(
  117 + String entityId, ProtocolType protocolType) throws ThingsboardException {
  118 + if (protocolType == null) {
  119 + protocolType = ProtocolType.HLS;
99 } 120 }
  121 + String url =
  122 + videoService.getCameraPreviewURL(
  123 + getCurrentUser().isTenantAdmin(),
  124 + getCurrentUser().getCurrentUserId(),
  125 + getCurrentUser().getCurrentTenantId(),
  126 + entityId,
  127 + protocolType);
  128 + Map<String, String> map = new HashMap<>();
  129 + map.put("url", url);
  130 + return ResponseResult.success(map);
  131 + }
100 } 132 }
101 -  
@@ -3,6 +3,7 @@ package org.thingsboard.server.common.data.yunteng.dto; @@ -3,6 +3,7 @@ package org.thingsboard.server.common.data.yunteng.dto;
3 import io.swagger.annotations.ApiModelProperty; 3 import io.swagger.annotations.ApiModelProperty;
4 import lombok.Data; 4 import lombok.Data;
5 import lombok.EqualsAndHashCode; 5 import lombok.EqualsAndHashCode;
  6 +import org.thingsboard.server.common.data.yunteng.utils.tools.ProtocolType;
6 7
7 import javax.validation.constraints.Max; 8 import javax.validation.constraints.Max;
8 import javax.validation.constraints.NotEmpty; 9 import javax.validation.constraints.NotEmpty;
@@ -36,4 +37,11 @@ public class TkVideoPlatformDTO extends TenantDTO { @@ -36,4 +37,11 @@ public class TkVideoPlatformDTO extends TenantDTO {
36 @NotNull(message = "流媒体部署环境不能为空") 37 @NotNull(message = "流媒体部署环境不能为空")
37 @Max(value = 2, message = "平台类型最大长度2") 38 @Max(value = 2, message = "平台类型最大长度2")
38 private Integer ssl; 39 private Integer ssl;
  40 +
  41 + @ApiModelProperty(value = "视频流协议:HLS RTSP RTMP", required = true)
  42 + @NotNull(message = "视频流协议不能为空")
  43 + private ProtocolType protocolType;
  44 +
  45 + @ApiModelProperty(value = "备注")
  46 + private String remark;
39 } 47 }
1 package org.thingsboard.server.dao.yunteng.entities; 1 package org.thingsboard.server.dao.yunteng.entities;
2 2
  3 +import com.baomidou.mybatisplus.annotation.FieldStrategy;
  4 +import com.baomidou.mybatisplus.annotation.TableField;
3 import com.baomidou.mybatisplus.annotation.TableName; 5 import com.baomidou.mybatisplus.annotation.TableName;
4 import lombok.Data; 6 import lombok.Data;
5 import lombok.EqualsAndHashCode; 7 import lombok.EqualsAndHashCode;
  8 +import org.apache.ibatis.type.EnumTypeHandler;
6 import org.thingsboard.server.common.data.yunteng.constant.ModelConstants; 9 import org.thingsboard.server.common.data.yunteng.constant.ModelConstants;
  10 +import org.thingsboard.server.common.data.yunteng.utils.tools.ProtocolType;
7 11
8 /** 12 /**
9 * @author Administrator 13 * @author Administrator
@@ -20,4 +24,10 @@ public class TkVideoPlatformEntity extends TenantBaseEntity { @@ -20,4 +24,10 @@ public class TkVideoPlatformEntity extends TenantBaseEntity {
20 private String appKey; 24 private String appKey;
21 private String appSecret; 25 private String appSecret;
22 private Integer ssl; 26 private Integer ssl;
  27 +
  28 + @TableField(typeHandler = EnumTypeHandler.class)
  29 + private ProtocolType protocolType;
  30 +
  31 + @TableField(updateStrategy = FieldStrategy.IGNORED)
  32 + private String remark;
23 } 33 }
@@ -92,7 +92,8 @@ public class TkVideoServiceImpl extends AbstractBaseService<TkVideoMapper, TkVid @@ -92,7 +92,8 @@ public class TkVideoServiceImpl extends AbstractBaseService<TkVideoMapper, TkVid
92 } 92 }
93 93
94 @Override 94 @Override
95 - public String getCameraPreviewURL(boolean isTenantAdmin, String customerUserId, String tenantId, String entityId) { 95 + public String getCameraPreviewURL(boolean isTenantAdmin, String customerUserId, String tenantId, String entityId,
  96 + ProtocolType protocolType) {
96 if (isTenantAdmin) { 97 if (isTenantAdmin) {
97 customerUserId = null; 98 customerUserId = null;
98 } 99 }
@@ -103,7 +104,9 @@ public class TkVideoServiceImpl extends AbstractBaseService<TkVideoMapper, TkVid @@ -103,7 +104,9 @@ public class TkVideoServiceImpl extends AbstractBaseService<TkVideoMapper, TkVid
103 throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); 104 throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage());
104 } 105 }
105 TkVideoDTO videoDTO = videoDTOList.get(0); 106 TkVideoDTO videoDTO = videoDTOList.get(0);
106 - ProtocolType protocolType = videoDTO.getPlayProtocol() == 0 ? ProtocolType.HLS : ProtocolType.HLSS; 107 + if(protocolType!=null && protocolType.equals(ProtocolType.HLS)){
  108 + protocolType = videoDTO.getPlayProtocol() == 0 ? ProtocolType.HLS : ProtocolType.HLSS;
  109 + }
107 return HikVisionArtemisPostUtils.getCameraPreviewURL(videoDTO.getVideoPlatformDTO().getHost(), 110 return HikVisionArtemisPostUtils.getCameraPreviewURL(videoDTO.getVideoPlatformDTO().getHost(),
108 videoDTO.getVideoPlatformDTO().getAppKey(), videoDTO.getVideoPlatformDTO().getAppSecret(), 111 videoDTO.getVideoPlatformDTO().getAppKey(), videoDTO.getVideoPlatformDTO().getAppSecret(),
109 videoDTO.getVideoPlatformDTO().getSsl(), videoDTO.getSn(), protocolType, videoDTO.getStreamType()); 112 videoDTO.getVideoPlatformDTO().getSsl(), videoDTO.getSn(), protocolType, videoDTO.getStreamType());
@@ -3,6 +3,7 @@ package org.thingsboard.server.dao.yunteng.service; @@ -3,6 +3,7 @@ package org.thingsboard.server.dao.yunteng.service;
3 import com.baomidou.mybatisplus.core.metadata.IPage; 3 import com.baomidou.mybatisplus.core.metadata.IPage;
4 import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO; 4 import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO;
5 import org.thingsboard.server.common.data.yunteng.dto.TkVideoDTO; 5 import org.thingsboard.server.common.data.yunteng.dto.TkVideoDTO;
  6 +import org.thingsboard.server.common.data.yunteng.utils.tools.ProtocolType;
6 import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; 7 import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData;
7 import org.thingsboard.server.dao.yunteng.entities.TkVideoEntity; 8 import org.thingsboard.server.dao.yunteng.entities.TkVideoEntity;
8 9
@@ -11,61 +12,74 @@ import java.util.Set; @@ -11,61 +12,74 @@ import java.util.Set;
11 12
12 public interface TkVideoService extends BaseService<TkVideoEntity> { 13 public interface TkVideoService extends BaseService<TkVideoEntity> {
13 14
14 - /**  
15 - * @param pageInfrom 分页配置信息  
16 - * @param isPtTenantAdmin 是否平台管理员  
17 - * @param tenantId 租户ID  
18 - * @param currentUserId 用户ID  
19 - * @param name 名字包含的关键字  
20 - * @param status 状态  
21 - * @return  
22 - */  
23 - TkPageData<TkVideoDTO> pageDatas(IPage<TkVideoEntity> pageInfrom,  
24 - boolean isPtTenantAdmin, String tenantId, String currentUserId, String organizationId, String name, Boolean status); 15 + /**
  16 + * @param pageInfrom 分页配置信息
  17 + * @param isPtTenantAdmin 是否平台管理员
  18 + * @param tenantId 租户ID
  19 + * @param currentUserId 用户ID
  20 + * @param name 名字包含的关键字
  21 + * @param status 状态
  22 + * @return
  23 + */
  24 + TkPageData<TkVideoDTO> pageDatas(
  25 + IPage<TkVideoEntity> pageInfrom,
  26 + boolean isPtTenantAdmin,
  27 + String tenantId,
  28 + String currentUserId,
  29 + String organizationId,
  30 + String name,
  31 + Boolean status);
25 32
26 - /**  
27 - * @param tenantId  
28 - * @param entityId  
29 - * @return  
30 - */  
31 - TkVideoDTO detail(String tenantId, String entityId); 33 + /**
  34 + * @param tenantId
  35 + * @param entityId
  36 + * @return
  37 + */
  38 + TkVideoDTO detail(String tenantId, String entityId);
32 39
33 - /**  
34 - * @param dto  
35 - * @return  
36 - */  
37 - TkVideoDTO saveOrUpdate(TkVideoDTO dto); 40 + /**
  41 + * @param dto
  42 + * @return
  43 + */
  44 + TkVideoDTO saveOrUpdate(TkVideoDTO dto);
38 45
39 - /**  
40 - * @param deleteDTO  
41 - * @return  
42 - */  
43 - boolean deleteDataByIds(DeleteDTO deleteDTO); 46 + /**
  47 + * @param deleteDTO
  48 + * @return
  49 + */
  50 + boolean deleteDataByIds(DeleteDTO deleteDTO);
44 51
  52 + /**
  53 + * 根据租户ID、客户ID、视频ID获取视屏流播放地址
  54 + *
  55 + * @param isTenantAdmin 是否租户管理员
  56 + * @param customerUserId 客户ID
  57 + * @param tenantId 租户ID
  58 + * @param entityId 视频ID
  59 + * @param protocolType 视频协议
  60 + * @return 视屏流播放地址
  61 + */
  62 + String getCameraPreviewURL(
  63 + boolean isTenantAdmin,
  64 + String customerUserId,
  65 + String tenantId,
  66 + String entityId,
  67 + ProtocolType protocolType);
45 68
46 - /**  
47 - * 根据租户ID、客户ID、视频ID获取视屏流播放地址  
48 - * @param isTenantAdmin 是否租户管理员  
49 - * @param customerUserId 客户ID  
50 - * @param tenantId 租户ID  
51 - * @param entityId 视频ID  
52 - * @return 视屏流播放地址  
53 - */  
54 - String getCameraPreviewURL(boolean isTenantAdmin, String customerUserId, String tenantId,String entityId);  
55 -  
56 - /**  
57 - * 检查选中的流媒体ID是否被视频配置使用  
58 - * @param ids 流媒体ID  
59 - * @return 检查结果 true or false  
60 - */  
61 - boolean checkConfigIdsIsUseForVideo(Set<String> ids);  
62 -  
63 - /**  
64 - * 获取视频列表  
65 - * @param organizationId 组织ID  
66 - * @param tenantId 租户ID  
67 - * @return 视频列表  
68 - */  
69 - List<TkVideoDTO> getVideoList(String organizationId, String tenantId); 69 + /**
  70 + * 检查选中的流媒体ID是否被视频配置使用
  71 + *
  72 + * @param ids 流媒体ID
  73 + * @return 检查结果 true or false
  74 + */
  75 + boolean checkConfigIdsIsUseForVideo(Set<String> ids);
70 76
  77 + /**
  78 + * 获取视频列表
  79 + *
  80 + * @param organizationId 组织ID
  81 + * @param tenantId 租户ID
  82 + * @return 视频列表
  83 + */
  84 + List<TkVideoDTO> getVideoList(String organizationId, String tenantId);
71 } 85 }
@@ -9,6 +9,8 @@ @@ -9,6 +9,8 @@
9 <result property="appKey" column="app_key"/> 9 <result property="appKey" column="app_key"/>
10 <result property="appSecret" column="app_secret"/> 10 <result property="appSecret" column="app_secret"/>
11 <result property="ssl" column="ssl"/> 11 <result property="ssl" column="ssl"/>
  12 + <result property="protocolType" column="protocol_type" typeHandler="org.apache.ibatis.type.EnumTypeHandler"/>
  13 + <result property="remark" column="remark"/>
12 <result property="tenantId" column="tenant_id"/> 14 <result property="tenantId" column="tenant_id"/>
13 <result property="createTime" column="create_time"/> 15 <result property="createTime" column="create_time"/>
14 <result property="updateTime" column="update_time"/> 16 <result property="updateTime" column="update_time"/>
@@ -17,7 +19,7 @@ @@ -17,7 +19,7 @@
17 </resultMap> 19 </resultMap>
18 20
19 <sql id="basicColumns"> 21 <sql id="basicColumns">
20 - base.id,base.type,base.host,base.app_key,base.app_secret,base.ssl,base.tenant_id,base.create_time, 22 + base.id,base.type,base.host,base.app_key,base.app_secret,base.ssl,protocol_type,remark,base.tenant_id,base.create_time,
21 base.update_time,base.creator, base.updater 23 base.update_time,base.creator, base.updater
22 </sql> 24 </sql>
23 <select id="getVideoPlatformPage" resultMap="videoPlatformMap"> 25 <select id="getVideoPlatformPage" resultMap="videoPlatformMap">