--- title: "12-业务讲解-如何实现高效的主页节目列表显示" created: 2025-12-15 aliases: - 业务讲解-如何实现高效的主页节目列表显示 tags: - 项目 --- # 业务讲解-如何实现高效的主页节目列表显示 ## 主页样式 ![[image-063adb22.webp]] 接下来我们来详细的讲解主页数据是如何进行显示的 ## damai-program-service ### 接口 #### 入参实体 ```java @Data @ApiModel(value="ProgramListDto", description ="主页节目列表") public class ProgramListDto { @ApiModelProperty(name ="areaId", dataType ="Long", value ="所在区域id") private Long areaId; @ApiModelProperty(name ="parentProgramCategoryIds", dataType ="Long[]", value ="父节目类型id集合",required = true) @NotNull @Size(max = 4) private List parentProgramCategoryIds; } ``` 这里使用了jakarta.validation的注解来做参数验证,区域id不能为空,父节目类型id集合不能为空并且长度最大为4 #### 控制层 com.damai.controller.ProgramController#selectHomeList ```java @ApiOperation(value = "查询主页列表") @PostMapping(value = "/home/list") public ApiResponse> selectHomeList(@Valid @RequestBody ProgramListDto programPageListDto) { return ApiResponse.ok(programService.selectHomeList(programPageListDto)); } ``` 问题思考 > 到这里我们先不急着往下看,先思考个问题,主页列表的查询条件有区域id和父节目类型id集合。而节目表进行了分库分表,分片键是主键id。这不就造成了读扩散导致全路由查询了,那效率绝对慢啊。这怎么处理? > > 首先采取用户表那种额外加路由表的方案行不通了,以用户手机表为例,先用手机号去用户手机表查询到用户id,再用用户id查询用户表。这个去用户表查询的条件只有一个条件,就是用户id。而在此业务中,需要同时传入地区id和父节目类型id这两个条件,要是设计了节目地区路由表和父节目类型路由表,这两个表之间独立,建立不了关系,也就查询不到两者的交集。所以路由表的方案解决不了 > > 路由表 A:region\_id → [list of program\_id] > 路由表 B:parent\_category\_id → [list of program\_id] > 想要同时满足 (region\_id=3 AND parent\_category\_id IN (10,11,12)),就要取出: > > ``` > -- 区域路由表查节目ID > setA = programs_in_region_3 > -- 父类型路由表查节目ID > setB = programs_in_parentCat_10 ∪ parentCat_11 ∪ parentCat_12 > -- 求ID交集 > intersection = setA ∩ setB > ``` > > 然后再对 intersection 里的每个 program\_id 去各个分片表扫一次。 > 问题 1:路由表间「独立」 > > route\_by\_region 和 route\_by\_category 本身是两个完全独立的映射表,它们之间没有外键,也无法在数据库层面或索引层面高效地做「交集」查询 —— 你得把 A、B 全部拉出来在应用层算交集,成本巨大。 > > 问题 2:读扩散严重 > > 交集出来可能还是成千上万个 program\_id,哪怕只有 20 条要返回,你也要把这些 ID 全部路由、scatter/gather 到若干库表,再过滤、再分页,效率依然惨不忍睹。 > > 至于用基因法更是不行,基因法只能允许订单号和用户id一起充当分片键,但是查询还只能用一个条件,要么用订单号查询、要么用用户id查询 > > 似乎陷入了死局中,但真的解决不了了吗?我们完全可以借助分布式的查询引擎啊,也就是大名鼎鼎的elasticsearch,将节目表的数据存放到elasticsearch中不就可以了吗 > > elasticsearch中只存放节目表的数据,余票数量还是放到redis中,这样就不能在余票数据不一致的问题了 > > 至于内存压力的问题,我们可以这样解决,我们可以设置定时任务,将过期的节目从elasticsearch中删除掉,elasticsearch只保存可以订票的节目不就可以了吗 但只使用elasticsearch是不行的,应该还有个兜底策略,当从elasticsearch查询不到的话,再从数据库中查询,就算全路由查询了,那也比什么都查询不到强 com.damai.service.ProgramService#selectHomeList ```java public List selectHomeList(ProgramListDto programPageListDto) { //先从elasticsearch查询 List programHomeVoList = programEs.selectHomeList(programPageListDto); if (CollectionUtil.isNotEmpty(programHomeVoList)) { return programHomeVoList; } //elasticsearch中查询不到,再去数据库中查询 return dbSelectHomeList(programPageListDto); } ``` com.damai.service.es.ProgramEs#selectHomeList ```java @Slf4j @Component public class ProgramEs { @Autowired private BusinessEsHandle businessEsHandle; public List selectHomeList(ProgramListDto programPageListDto) { List programHomeVoList = new ArrayList<>(); try { //按照父节目id集合来进行循环从es中查询,主页页面是4个父节目,也就是循环4次 for (Long parentProgramCategoryId : programPageListDto.getParentProgramCategoryIds()) { List programEsQueryDto = new ArrayList<>(); if (Objects.nonNull(programPageListDto.getAreaId())) { //地区id EsDataQueryDto areaIdQueryDto = new EsDataQueryDto(); areaIdQueryDto.setParamName(ProgramDocumentParamName.AREA_ID); areaIdQueryDto.setParamValue(programPageListDto.getAreaId()); programEsQueryDto.add(areaIdQueryDto); }else { //如果查全部地区则需要查询同一个节目分组内的主要节目 EsDataQueryDto primeQueryDto = new EsDataQueryDto(); primeQueryDto.setParamName(ProgramDocumentParamName.PRIME); primeQueryDto.setParamValue(BusinessStatus.YES.getCode()); programEsQueryDto.add(primeQueryDto); } //父节目类型id集合 EsDataQueryDto parentProgramCategoryIdQueryDto = new EsDataQueryDto(); parentProgramCategoryIdQueryDto.setParamName(ProgramDocumentParamName.PARENT_PROGRAM_CATEGORY_ID); parentProgramCategoryIdQueryDto.setParamValue(parentProgramCategoryId); programEsQueryDto.add(parentProgramCategoryIdQueryDto); //查询前7条 PageInfo pageInfo = businessEsHandle.queryPage( SpringUtil.getPrefixDistinctionName() + "-" + ProgramDocumentParamName.INDEX_NAME, ProgramDocumentParamName.INDEX_TYPE, programEsQueryDto, 1, 7, ProgramListVo.class); if (!pageInfo.getList().isEmpty()) { ProgramHomeVo programHomeVo = new ProgramHomeVo(); programHomeVo.setCategoryName(pageInfo.getList().get(0).getParentProgramCategoryName()); programHomeVo.setCategoryId(pageInfo.getList().get(0).getParentProgramCategoryId()); programHomeVo.setProgramListVoList(pageInfo.getList()); programHomeVoList.add(programHomeVo); } } }catch (Exception e) { log.error("businessEsHandle.queryPage error",e); } return programHomeVoList; } } ``` 这里使用了对elasticsearch进行封装的组件damai-elasticsearch-framework,关于此组件的详细介绍,可跳转到 [组件讲解-如何对Elasticsearch进行高效封装](https://www.yuque.com/u22210564/ykdrdh/ti6u5ovgfuhftlum) 这里将传入的父类型的节目id集合进行查询,每个循环内根据地区id和父节目类型id来查询出前7条,接着放入programListVoMap中,key为类型名,value为节目列表 #### 注意 主页的列表数据要显示的并不是只有节目表中的数据,还有programCategoryName、showTime、showDayTime、showWeekTime、minPrice、maxPrice这些都是要从其他表中获取的,至于为什么从elasticsearch中可以直接查询出来,是因为在项目初始化时,将这些数据都已经组装好了,才放入到elasticsearch中的, 关于是如何将数据组装好后放入elasticsearch中的,可跳转到相应介绍部分详细查看 [业务讲解-节目服务的数据初始化统一管理](https://www.yuque.com/u22210564/ykdrdh/vmvabhdvn1a30mi5) elasticsearch中的存储结构: ```json { "_index": "damai-program", "_id": "Ckf98Y8BBcQ0HdIVE_u8", "_version": 1, "_score": 0, "_source": { "prime": 1, "showWeekTime": "周五", "parentProgramCategoryId": 1, "showDayTime": 1720108800000, "parentProgramCategoryName": "演唱会", "showTime": 1720182600000, "title": "「万朵」—春日玫瑰2024新专辑巡演", "actor": "春日玫瑰", "programGroupId": 6, "areaId": 2, "areaName": "北京", "itemPicture": "https://s21.ax1x.com/2024/06/06/pkYEDAI.webp", "minPrice": 156, "programCategoryId": 1, "id": 6, "place": "疆进酒·OMNI SPACE", "maxPrice": 299, "programCategoryName": "演唱会" }, "fields": { "actor.keyword": [ "春日玫瑰" ], "title": [ "「万朵」—春日玫瑰2024新专辑巡演" ], "title.keyword": [ "「万朵」—春日玫瑰2024新专辑巡演" ], "itemPicture.keyword": [ "https://s21.ax1x.com/2024/06/06/pkYEDAI.webp" ], "areaName": [ "北京" ], "itemPicture": [ "https://s21.ax1x.com/2024/06/06/pkYEDAI.webp" ], "programCategoryId": [ 1 ], "id": [ 6 ], "place": [ "疆进酒·OMNI SPACE" ], "programCategoryName": [ "演唱会" ], "showWeekTime": [ "周五" ], "prime": [ 1 ], "parentProgramCategoryId": [ 1 ], "showDayTime": [ "2024-07-04T16:00:00.000Z" ], "parentProgramCategoryName": [ "演唱会" ], "showTime": [ "2024-07-05T12:30:00.000Z" ], "parentProgramCategoryName.keyword": [ "演唱会" ], "showWeekTime.keyword": [ "周五" ], "areaName.keyword": [ "北京" ], "actor": [ "春日玫瑰" ], "programCategoryName.keyword": [ "演唱会" ], "programGroupId": [ 6 ], "areaId": [ 2 ], "minPrice": [ 156 ], "maxPrice": [ 299 ], "place.keyword": [ "疆进酒·OMNI SPACE" ] } } ``` com.damai.service.ProgramService#dbSelectHomeList 当从elasticsearch中查询不到的话,就只有从数据库中查询了,全路由查询也比没有数据要好 ```java private List dbSelectHomeList(ProgramListDto programPageListDto){ List programHomeVoList = new ArrayList<>(); //根据父节目类型id来查询节目类型map,key:节目类型id,value:节目类型名 Map programCategoryMap = selectProgramCategoryMap(programPageListDto.getParentProgramCategoryIds()); //查询节目列表 List programList = programMapper.selectHomeList(programPageListDto); //如果节目列表为空,那么直接返回空map if (CollectionUtil.isEmpty(programList)) { return programHomeVoList; } //节目id集合 List programIdList = programList.stream().map(Program::getId).collect(Collectors.toList()); //根据节目id集合查询节目演出时间集合 LambdaQueryWrapper programShowTimeLambdaQueryWrapper = Wrappers.lambdaQuery(ProgramShowTime.class) .in(ProgramShowTime::getProgramId, programIdList); List programShowTimeList = programShowTimeMapper.selectList(programShowTimeLambdaQueryWrapper); //将节目演出集合根据节目id进行分组成map,key:节目id,value:节目演出时间集合 Map> programShowTimeMap = programShowTimeList.stream().collect(Collectors.groupingBy(ProgramShowTime::getProgramId)); //根据节目id统计出票档的最低价和最高价的集合map, key:节目id,value:票档 Map ticketCategorieMap = selectTicketCategorieMap(programIdList); //将节目结合按照父节目类型id进行分组成map,key:父节目类型id,value:节目集合 Map> programMap = programList.stream() .collect(Collectors.groupingBy(Program::getParentProgramCategoryId)); //循环此map for (Entry> programEntry : programMap.entrySet()) { //父节目类型id Long key = programEntry.getKey(); //节目集合 List value = programEntry.getValue(); List programListVoList = new ArrayList<>(); //循环节目集合 for (Program program : value) { ProgramListVo programListVo = new ProgramListVo(); BeanUtil.copyProperties(program,programListVo); //演出时间 programListVo.setShowTime(Optional.ofNullable(programShowTimeMap.get(program.getId())) .filter(list -> !list.isEmpty()) .map(list -> list.get(0)) .map(ProgramShowTime::getShowTime) .orElse(null)); //演出时间(精确到天) programListVo.setShowDayTime(Optional.ofNullable(programShowTimeMap.get(program.getId())) .filter(list -> !list.isEmpty()) .map(list -> list.get(0)) .map(ProgramShowTime::getShowDayTime) .orElse(null)); //演出时间所在的星期 programListVo.setShowWeekTime(Optional.ofNullable(programShowTimeMap.get(program.getId())) .filter(list -> !list.isEmpty()) .map(list -> list.get(0)) .map(ProgramShowTime::getShowWeekTime) .orElse(null)); //节目最高价 programListVo.setMaxPrice(Optional.ofNullable(ticketCategorieMap.get(program.getId())) .map(TicketCategoryAggregate::getMaxPrice).orElse(null)); //节目最低价 programListVo.setMinPrice(Optional.ofNullable(ticketCategorieMap.get(program.getId())) .map(TicketCategoryAggregate::getMinPrice).orElse(null)); programListVoList.add(programListVo); } ProgramHomeVo programHomeVo = new ProgramHomeVo(); //节目类型名 programHomeVo.setCategoryName(programCategoryMap.get(key)); //节目类型id programHomeVo.setCategoryId(key); programHomeVo.setProgramListVoList(programListVoList); //节目列表 programHomeVoList.add(programHomeVo); } return programHomeVoList; } ``` 从数据库中查询没有办法像从elasticsearch中直接查询的数据都是全的,programCategoryName、showTime、showDayTime、showWeekTime、minPrice、maxPrice这些仍然要从其他表中获取的,这些查询中都带有分片键,所以并不需要担心全路由的问题 查询节目类型名字 ```java //根据父节目类型id来查询节目类型map,key:节目类型id,value:节目类型名 Map programCategoryMap = selectProgramCategoryMap(programPageListDto.getParentProgramCategoryIds()); ``` ```java /** * 根据节目类型id集合查询节目类型名字 * @param programCategoryIdList 节目类型id集合 * @return Map key:节目类型id value:节目类型名字 * */ public Map selectProgramCategoryMap(Collection programCategoryIdList){ LambdaQueryWrapper pcLambdaQueryWrapper = Wrappers.lambdaQuery(ProgramCategory.class) .in(ProgramCategory::getId, programCategoryIdList); List programCategoryList = programCategoryMapper.selectList(pcLambdaQueryWrapper); return programCategoryList .stream() .collect(Collectors.toMap(ProgramCategory::getId, ProgramCategory::getName, (v1, v2) -> v2)); } ``` 根据传入的parentProgramCategoryIds父节目类型id集合,获取相关数据 查询节目列表 ```java List programList = programMapper.selectHomeList(programPageListDto); ``` 这个查询的要求是查询根据每个parentProgramCategoryId查询出节目列表,并且取前7条,要用sql实现的话,就要这么做,前端传入参数以此为例: ```json { "areaId":"2", "parentProgramCategoryIds":["1","2"] } ``` 形成的sql为 ```sql select dp.id,dp.area_id,dp.program_category_id,dp.parent_program_category_id,dp.title,dp.actor, dp.place,dp.item_picture from d_program dp where dp.program_status = 1 and dp.area_id = 2 and dp.parent_program_category_id = 1 limit 7 ) as tmp union all select * from ( select dp.id,dp.area_id,dp.program_category_id,dp.parent_program_category_id,dp.title,dp.actor, dp.place,dp.item_picture from d_program dp where dp.program_status = 1 and dp.area_id = 2 and dp.parent_program_category_id = 2 limit 7 ) as tmp ``` 每个根据父节目id的查询都是一个个独立的sql,然后靠union all 来拼接起来,要想在代码中实现这种效果,就需要使用Mybatis来动态拼接sql,自定义写我们的sql查询逻辑 com.damai.mapper.ProgramMapper#selectHomeList ```text /** * 主页查询 * @param programListDto 参数 * @return 结果 * */ List selectHomeList(@Param("programListDto")ProgramListDto programListDto); ``` 对应的xml文件 ```sql ``` 使用了foreach循环标签,来循环parentProgramCategoryIds,并使用标签的separator属性,来将union all进行分隔,这样就能查询出节目列表了 接下来就是接着去别的表中查询需要的数据 查询节目演出时间 ```java //节目id集合 List programIdList = programList.stream().map(Program::getId).collect(Collectors.toList()); //根据节目id集合查询节目演出时间集合 LambdaQueryWrapper programShowTimeLambdaQueryWrapper = Wrappers.lambdaQuery(ProgramShowTime.class) .in(ProgramShowTime::getProgramId, programIdList); List programShowTimeList = programShowTimeMapper.selectList(programShowTimeLambdaQueryWrapper); //将节目演出集合根据节目id进行分组成map,key:节目id,value:节目演出时间集合 Map> programShowTimeMap = programShowTimeList.stream().collect(Collectors.groupingBy(ProgramShowTime::getProgramId)); ``` 根据节目id统计出票档的最低价和最高价的集合 ```java //根据节目id统计出票档的最低价和最高价的集合map, key:节目id,value:票档 Map ticketCategorieMap = selectTicketCategorieMap(programIdList); ``` ```java public Map selectTicketCategorieMap(List programIdList){ //根据节目id统计出票档的最低价和最高价的集合 List ticketCategorieList = ticketCategoryMapper.selectAggregateList(programIdList); //将集合转换为map,key:节目id,value:票档 return ticketCategorieList .stream() .collect(Collectors.toMap(TicketCategoryAggregate::getProgramId, ticketCategory -> ticketCategory, (v1, v2) -> v2)); } ``` selectAggregateList也是自定义的sql,来统计出每个节目对应的票档最低价和最高价 ```sql ``` 当这些数据都查询出来后,接下来就是进行组装了 组装主页列表需要的数据 ```java //将节目结合按照父节目类型id进行分组成map,key:父节目类型id,value:节目集合 Map> programMap = programList.stream() .collect(Collectors.groupingBy(Program::getParentProgramCategoryId)); //循环此map for (Entry> programEntry : programMap.entrySet()) { //父节目类型id Long key = programEntry.getKey(); //节目集合 List value = programEntry.getValue(); List programListVoList = new ArrayList<>(); //循环节目集合 for (Program program : value) { ProgramListVo programListVo = new ProgramListVo(); BeanUtil.copyProperties(program,programListVo); //演出时间 programListVo.setShowTime(Optional.ofNullable(programShowTimeMap.get(program.getId())) .filter(list -> !list.isEmpty()) .map(list -> list.get(0)) .map(ProgramShowTime::getShowTime) .orElse(null)); //演出时间(精确到天) programListVo.setShowDayTime(Optional.ofNullable(programShowTimeMap.get(program.getId())) .filter(list -> !list.isEmpty()) .map(list -> list.get(0)) .map(ProgramShowTime::getShowDayTime) .orElse(null)); //演出时间所在的星期 programListVo.setShowWeekTime(Optional.ofNullable(programShowTimeMap.get(program.getId())) .filter(list -> !list.isEmpty()) .map(list -> list.get(0)) .map(ProgramShowTime::getShowWeekTime) .orElse(null)); //节目最高价 programListVo.setMaxPrice(Optional.ofNullable(ticketCategorieMap.get(program.getId())) .map(TicketCategoryAggregate::getMaxPrice).orElse(null)); //节目最低价 programListVo.setMinPrice(Optional.ofNullable(ticketCategorieMap.get(program.getId())) .map(TicketCategoryAggregate::getMinPrice).orElse(null)); programListVoList.add(programListVo); } ProgramHomeVo programHomeVo = new ProgramHomeVo(); //节目类型名 programHomeVo.setCategoryName(programCategoryMap.get(key)); programHomeVo.setProgramListVoList(programListVoList); //节目列表 programHomeVoList.add(programHomeVo); } return programHomeVoList; ``` 将节目列表按照父节目id进行分组,组装成programMap,接着循环此map,再循环map的value的集合 在循环中,将这些数据组装上去,组装后放在programHomeVoList中,泛型为ProgramHomeVo ProgramHomeVo结构 ```java @Data @ApiModel(value="ProgramHomeVo", description ="节目主页列表") public class ProgramHomeVo implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty(name ="categoryName", dataType ="String", value ="类型名字") private String categoryName; @ApiModelProperty(name ="categoryId", dataType ="Long", value ="类型id") private Long categoryId; @ApiModelProperty(name ="programListVoList", dataType ="array", value ="节目列表") private List programListVoList; } ``` 可以看一下组装后给前端返回的数据: ```json { "code": "0", "message": "", "data": [ { "categoryName": "演唱会", "categoryId": "1", "programListVoList": [ { "id": "4", "title": "葡萄不愤怒乐队十周年远走高飞 2024巡演", "actor": "葡萄不愤怒乐队", "place": "MAO Livehouse北京", "itemPicture": "https://s21.ax1x.com/2024/06/06/pkYENcD.webp", "areaId": "2", "areaName": "北京", "programCategoryId": "1", "programCategoryName": "演唱会", "parentProgramCategoryId": "1", "parentProgramCategoryName": "演唱会", "showTime": "2024-07-20 19:00:00", "showDayTime": "2024-07-20 00:00:00", "showWeekTime": "周六", "minPrice": "199", "maxPrice": "299" }, { "id": "10", "title": "【乐队的春天】超燃乐队现场滚烫开燥|蹦迪开火车|室内音乐节式LIVE| 乐队迷大聚会!HOT乐队歌单", "actor": "", "place": "CLUB HEAVEN (天堂超市世茂工三店)", "itemPicture": "https://s21.ax1x.com/2024/06/06/pkYE5En.jpg", "areaId": "2", "areaName": "北京", "programCategoryId": "1", "programCategoryName": "演唱会", "parentProgramCategoryId": "1", "parentProgramCategoryName": "演唱会", "showTime": "2024-08-08 20:00:00", "showDayTime": "2024-08-08 00:00:00", "showWeekTime": "周四", "minPrice": "159", "maxPrice": "199" }, { "id": "14", "title": "2024【真的爱你】致敬BEYOND·黄家驹演唱会重现91殿堂级演唱会", "actor": "", "place": "MAO Livehouse北京", "itemPicture": "https://s21.ax1x.com/2024/06/07/pktiAc4.jpg", "areaId": "2", "areaName": "北京", "programCategoryId": "1", "programCategoryName": "演唱会", "parentProgramCategoryId": "1", "parentProgramCategoryName": "演唱会", "showTime": "2024-07-20 18:30:00", "showDayTime": "2024-07-20 00:00:00", "showWeekTime": "周六", "minPrice": "139", "maxPrice": "189" }, { "id": "22", "title": "暗杠「走歌人2024」巡演", "actor": "", "place": "WeShow Live", "itemPicture": "https://s21.ax1x.com/2024/04/30/pkF5T54.jpg", "areaId": "2", "areaName": "北京", "programCategoryId": "1", "programCategoryName": "演唱会", "parentProgramCategoryId": "1", "parentProgramCategoryName": "演唱会", "showTime": "2024-07-24 20:00:00", "showDayTime": "2024-07-24 00:00:00", "showWeekTime": "周三", "minPrice": "499", "maxPrice": "599" }, { "id": "36", "title": "张学友60+巡回演唱会", "actor": "张学友", "place": "国家体育馆", "itemPicture": "https://s21.ax1x.com/2024/06/07/pkYz139.jpg", "areaId": "2", "areaName": "北京", "programCategoryId": "1", "programCategoryName": "演唱会", "parentProgramCategoryId": "1", "parentProgramCategoryName": "演唱会", "showTime": "2024-07-22 20:30:00", "showDayTime": "2024-07-22 00:00:00", "showWeekTime": "周一", "minPrice": "480", "maxPrice": "2280" }, { "id": "2", "title": "韦礼安「一直都在」音乐会", "actor": "韦礼安", "place": "JDG英特尔电子竞技中心", "itemPicture": "https://s21.ax1x.com/2024/06/06/pkYEY9K.png", "areaId": "2", "areaName": "北京", "programCategoryId": "1", "programCategoryName": "演唱会", "parentProgramCategoryId": "1", "parentProgramCategoryName": "演唱会", "showTime": "2024-08-02 20:00:00", "showDayTime": "2024-08-02 00:00:00", "showWeekTime": "周五", "minPrice": "288", "maxPrice": "488" }, { "id": "6", "title": "「万朵」—春日玫瑰2024新专辑巡演", "actor": "春日玫瑰", "place": "疆进酒·OMNI SPACE", "itemPicture": "https://s21.ax1x.com/2024/06/06/pkYEDAI.webp", "areaId": "2", "areaName": "北京", "programCategoryId": "1", "programCategoryName": "演唱会", "parentProgramCategoryId": "1", "parentProgramCategoryName": "演唱会", "showTime": "2024-08-05 20:30:00", "showDayTime": "2024-08-05 00:00:00", "showWeekTime": "周一", "minPrice": "156", "maxPrice": "299" } ] }, { "categoryName": "话剧歌剧", "categoryId": "2", "programListVoList": [ { "id": "15", "title": "俄罗斯芭蕾国家剧院《天鹅湖》", "actor": "", "place": "天桥剧场", "itemPicture": "https://s21.ax1x.com/2024/03/20/pFWozc9.webp", "areaId": "2", "areaName": "北京", "programCategoryId": "2", "programCategoryName": "话剧歌剧", "parentProgramCategoryId": "2", "parentProgramCategoryName": "话剧歌剧", "showTime": "2024-07-16 20:30:00", "showDayTime": "2024-07-16 00:00:00", "showWeekTime": "周二", "minPrice": "375", "maxPrice": "415" }, { "id": "17", "title": "泱泱国风 民族经典 中国歌剧舞剧院鸿篇巨制舞剧《李白》", "actor": "", "place": "天桥艺术中心-大剧场 ", "itemPicture": "https://s21.ax1x.com/2024/03/20/pFWoXhF.webp", "areaId": "2", "areaName": "北京", "programCategoryId": "2", "programCategoryName": "话剧歌剧", "parentProgramCategoryId": "2", "parentProgramCategoryName": "话剧歌剧", "showTime": "2024-07-20 18:30:00", "showDayTime": "2024-07-20 00:00:00", "showWeekTime": "周六", "minPrice": "158", "maxPrice": "358" }, { "id": "5", "title": "话剧《苏堤春晓》田沁鑫导演", "actor": "辛柏青", "place": "国家话剧院-剧场", "itemPicture": "https://s21.ax1x.com/2024/03/20/pFW69mQ.webp", "areaId": "2", "areaName": "北京", "programCategoryId": "2", "programCategoryName": "话剧歌剧", "parentProgramCategoryId": "2", "parentProgramCategoryName": "话剧歌剧", "showTime": "2024-08-02 20:00:00", "showDayTime": "2024-08-02 00:00:00", "showWeekTime": "周五", "minPrice": "258", "maxPrice": "458" }, { "id": "7", "title": "话剧《夜行者》", "actor": "李乃文&唐旭", "place": "首都剧场", "itemPicture": "https://s21.ax1x.com/2024/03/20/pFWRugx.webp", "areaId": "2", "areaName": "北京", "programCategoryId": "2", "programCategoryName": "话剧歌剧", "parentProgramCategoryId": "2", "parentProgramCategoryName": "话剧歌剧", "showTime": "2024-07-20 19:00:00", "showDayTime": "2024-07-20 00:00:00", "showWeekTime": "周六", "minPrice": "288", "maxPrice": "388" }, { "id": "9", "title": "杨丽萍导演作品 新东方美学舞剧《平潭映象》", "actor": "", "place": "北京展览馆剧场", "itemPicture": "https://s21.ax1x.com/2024/03/20/pFWRIZF.webp", "areaId": "2", "areaName": "北京", "programCategoryId": "2", "programCategoryName": "话剧歌剧", "parentProgramCategoryId": "2", "parentProgramCategoryName": "话剧歌剧", "showTime": "2024-08-05 20:30:00", "showDayTime": "2024-08-05 00:00:00", "showWeekTime": "周一", "minPrice": "258", "maxPrice": "458" }, { "id": "11", "title": "【沉浸式互动戏剧】新年大戏|侦探悬疑喜剧《完美犯罪》环境式爱笑—法庭剧场演出", "actor": "", "place": "雍和宫喜剧剧场", "itemPicture": "https://s21.ax1x.com/2024/03/20/pFWRjsK.webp", "areaId": "2", "areaName": "北京", "programCategoryId": "2", "programCategoryName": "话剧歌剧", "parentProgramCategoryId": "2", "parentProgramCategoryName": "话剧歌剧", "showTime": "2024-07-26 18:30:00", "showDayTime": "2024-07-26 00:00:00", "showWeekTime": "周五", "minPrice": "580", "maxPrice": "780" }, { "id": "13", "title": "法语原版音乐剧《唐璜》", "actor": "", "place": "天桥艺术中心-大剧场", "itemPicture": "https://s21.ax1x.com/2024/03/20/pFWWPJA.webp", "areaId": "2", "areaName": "北京", "programCategoryId": "2", "programCategoryName": "话剧歌剧", "parentProgramCategoryId": "2", "parentProgramCategoryName": "话剧歌剧", "showTime": "2024-08-08 20:00:00", "showDayTime": "2024-08-08 00:00:00", "showWeekTime": "周四", "minPrice": "225", "maxPrice": "325" } ] }, { "categoryName": "体育", "categoryId": "3", "programListVoList": [ { "id": "41", "title": "利雅得胜利中国行【C罗来啦】", "actor": "", "place": "体育场", "itemPicture": "https://s21.ax1x.com/2024/05/21/pkMug76.png", "areaId": "2", "areaName": "北京", "programCategoryId": "3", "programCategoryName": "体育", "parentProgramCategoryId": "3", "parentProgramCategoryName": "体育", "showTime": "2024-07-20 20:00:00", "showDayTime": "2024-07-20 00:00:00", "showWeekTime": "周六", "minPrice": "480", "maxPrice": "880" }, { "id": "45", "title": "世界摩托车越野锦标赛", "actor": "", "place": "国际越野赛车场 ", "itemPicture": "https://s21.ax1x.com/2024/06/12/pkUIgv8.jpg", "areaId": "2", "areaName": "北京", "programCategoryId": "3", "programCategoryName": "体育", "parentProgramCategoryId": "3", "parentProgramCategoryName": "体育", "showTime": "2024-08-06 13:00:00", "showDayTime": "2024-08-06 00:00:00", "showWeekTime": "周二", "minPrice": "1500", "maxPrice": "1500" }, { "id": "21", "title": "乒乓球大赛", "actor": "", "place": "海淀体育场", "itemPicture": "https://s21.ax1x.com/2024/07/07/pkWN3Yn.jpg", "areaId": "2", "areaName": "北京", "programCategoryId": "3", "programCategoryName": "体育", "parentProgramCategoryId": "3", "parentProgramCategoryName": "体育", "showTime": "2024-07-21 19:30:00", "showDayTime": "2024-07-21 00:00:00", "showWeekTime": "周日", "minPrice": "300", "maxPrice": "300" }, { "id": "33", "title": "羽毛球大赛", "actor": "", "place": "北京体育馆", "itemPicture": "https://s21.ax1x.com/2024/07/07/pkWtUIA.jpg", "areaId": "2", "areaName": "北京", "programCategoryId": "3", "programCategoryName": "体育", "parentProgramCategoryId": "3", "parentProgramCategoryName": "体育", "showTime": "2024-08-01 20:00:00", "showDayTime": "2024-08-01 00:00:00", "showWeekTime": "周四", "minPrice": "700", "maxPrice": "700" }, { "id": "35", "title": "世界斯诺克大师赛", "actor": "", "place": "北京体育馆", "itemPicture": "https://s21.ax1x.com/2024/07/06/pkWSCEq.jpg", "areaId": "2", "areaName": "北京", "programCategoryId": "3", "programCategoryName": "体育", "parentProgramCategoryId": "3", "parentProgramCategoryName": "体育", "showTime": "2024-07-11 20:00:00", "showDayTime": "2024-07-11 00:00:00", "showWeekTime": "周四", "minPrice": "900", "maxPrice": "900" }, { "id": "37", "title": "中国男子五人制足球超级赛", "actor": "", "place": "体育场", "itemPicture": "https://s21.ax1x.com/2024/05/21/pkMCoXF.png", "areaId": "2", "areaName": "北京", "programCategoryId": "3", "programCategoryName": "体育", "parentProgramCategoryId": "3", "parentProgramCategoryName": "体育", "showTime": "2024-07-15 20:00:00", "showDayTime": "2024-07-15 00:00:00", "showWeekTime": "周一", "minPrice": "258", "maxPrice": "458" }, { "id": "39", "title": "2024中超联赛北京国安主场赛事", "actor": "", "place": "工人体育场", "itemPicture": "https://s21.ax1x.com/2024/05/21/pkMPC0H.png", "areaId": "2", "areaName": "北京", "programCategoryId": "3", "programCategoryName": "体育", "parentProgramCategoryId": "3", "parentProgramCategoryName": "体育", "showTime": "2024-07-16 20:00:00", "showDayTime": "2024-07-16 00:00:00", "showWeekTime": "周二", "minPrice": "580", "maxPrice": "780" } ] }, { "categoryName": "儿童亲子", "categoryId": "4", "programListVoList": [ { "id": "23", "title": "大型全景多媒体舞台剧《冰雪女王Ⅰ艾莎之星梦奇缘》", "actor": "", "place": "北京展览馆剧场", "itemPicture": "https://s21.ax1x.com/2024/03/21/pFfrfII.jpg", "areaId": "2", "areaName": "北京", "programCategoryId": "4", "programCategoryName": "儿童亲子", "parentProgramCategoryId": "4", "parentProgramCategoryName": "儿童亲子", "showTime": "2024-07-27 20:00:00", "showDayTime": "2024-07-27 00:00:00", "showWeekTime": "周六", "minPrice": "410", "maxPrice": "410" }, { "id": "27", "title": "百老汇正版互动科学剧《物理秀》", "actor": "", "place": "北京展览馆剧场", "itemPicture": "https://s21.ax1x.com/2024/03/21/pFfrdaR.jpg", "areaId": "2", "areaName": "北京", "programCategoryId": "4", "programCategoryName": "儿童亲子", "parentProgramCategoryId": "4", "parentProgramCategoryName": "儿童亲子", "showTime": "2024-07-29 19:30:00", "showDayTime": "2024-07-29 00:00:00", "showWeekTime": "周一", "minPrice": "350", "maxPrice": "350" }, { "id": "31", "title": "儿童音乐舞台剧《神探艾小坡》", "actor": "", "place": "未来剧院", "itemPicture": "https://s21.ax1x.com/2024/03/22/pFfTpqI.jpg", "areaId": "2", "areaName": "北京", "programCategoryId": "4", "programCategoryName": "儿童亲子", "parentProgramCategoryId": "4", "parentProgramCategoryName": "儿童亲子", "showTime": "2024-08-01 20:00:00", "showDayTime": "2024-08-01 00:00:00", "showWeekTime": "周四", "minPrice": "258", "maxPrice": "258" }, { "id": "25", "title": "沉浸互动益智儿童剧神探爆梗岛之【小小福尔摩斯】全景互动|悬疑推理", "actor": "", "place": "雍和宫喜剧剧场", "itemPicture": "https://s21.ax1x.com/2024/03/21/pFfrsxO.jpg", "areaId": "2", "areaName": "北京", "programCategoryId": "4", "programCategoryName": "儿童亲子", "parentProgramCategoryId": "4", "parentProgramCategoryName": "儿童亲子", "showTime": "2024-07-28 19:00:00", "showDayTime": "2024-07-28 00:00:00", "showWeekTime": "周日", "minPrice": "430", "maxPrice": "430" }, { "id": "43", "title": "童艺出品沉浸式体验小剧场儿童剧《丑小鸭》", "actor": "", "place": "海淀剧场", "itemPicture": "https://s21.ax1x.com/2024/07/06/pkRzf3D.jpg", "areaId": "2", "areaName": "北京", "programCategoryId": "4", "programCategoryName": "儿童亲子", "parentProgramCategoryId": "4", "parentProgramCategoryName": "儿童亲子", "showTime": "2024-07-21 15:00:00", "showDayTime": "2024-07-21 00:00:00", "showWeekTime": "周日", "minPrice": "2000", "maxPrice": "2000" }, { "id": "19", "title": "东映动漫大型舞台剧《屁屁侦探之怪盗U对怪盗U》", "actor": "", "place": "北京展览馆剧场", "itemPicture": "https://s21.ax1x.com/2024/03/20/pFWTFAK.webp", "areaId": "2", "areaName": "北京", "programCategoryId": "4", "programCategoryName": "儿童亲子", "parentProgramCategoryId": "4", "parentProgramCategoryName": "儿童亲子", "showTime": "2024-07-20 19:30:00", "showDayTime": "2024-07-20 00:00:00", "showWeekTime": "周六", "minPrice": "358", "maxPrice": "358" }, { "id": "29", "title": "小猪佩奇舞台剧《完美的下雨天》", "actor": "", "place": "未来剧院", "itemPicture": "https://s21.ax1x.com/2024/03/22/pFfTPdP.webp", "areaId": "2", "areaName": "北京", "programCategoryId": "4", "programCategoryName": "儿童亲子", "parentProgramCategoryId": "4", "parentProgramCategoryName": "儿童亲子", "showTime": "2024-07-30 20:00:00", "showDayTime": "2024-07-30 00:00:00", "showWeekTime": "周二", "minPrice": "230", "maxPrice": "230" } ] } ] } ``` --- **企业级项目导航**:⬅️ [[11-技术精华-完全解读布隆过滤器|11-技术精华-完全解读布隆过滤器]] | 12-业务讲解-如何实现高效的主页节目列表显示 | ➡️ [[13-节目服务|13-节目服务]]