业务讲解-如何实现高效的主页节目列表显示
主页样式

接下来我们来详细的讲解主页数据是如何进行显示的
damai-program-service
接口
入参实体
@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<Long> parentProgramCategoryIds;
}这里使用了jakarta.validation的注解来做参数验证,区域id不能为空,父节目类型id集合不能为空并且长度最大为4
控制层
com.damai.controller.ProgramController#selectHomeList
@ApiOperation(value = "查询主页列表")
@PostMapping(value = "/home/list")
public ApiResponse<List<ProgramHomeVo>> selectHomeList(@Valid @RequestBody ProgramListDto programPageListDto) {
return ApiResponse.ok(programService.selectHomeList(programPageListDto));
}问题思考
到这里我们先不急着往下看,先思考个问题,主页列表的查询条件有区域id和父节目类型id集合。而节目表进行了分库分表,分片键是主键id。这不就造成了读扩散导致全路由查询了,那效率绝对慢啊。这怎么处理?
首先采取用户表那种额外加路由表的方案行不通了,以用户手机表为例,先用手机号去用户手机表查询到用户id,再用用户id查询用户表。这个去用户表查询的条件只有一个条件,就是用户id。而在此业务中,需要同时传入地区id和父节目类型id这两个条件,要是设计了节目地区路由表和父节目类型路由表,这两个表之间独立,建立不了关系,也就查询不到两者的交集。所以路由表的方案解决不了
至于用基因法更是不行,基因法只能允许订单号和用户id一起充当分片键,但是查询还只能用一个条件,要么用订单号查询、要么用用户id查询
似乎陷入了死局中,但真的解决不了了吗?我们完全可以借助分布式的查询引擎啊,也就是大名鼎鼎的elasticsearch,将节目表的数据存放到elasticsearch中不就可以了吗
elasticsearch中只存放节目表的数据,余票数量还是放到redis中,这样就不能在余票数据不一致的问题了
至于内存压力的问题,我们可以这样解决,我们可以设置定时任务,将过期的节目从elasticsearch中删除掉,elasticsearch只保存可以订票的节目不就可以了吗
但只使用elasticsearch是不行的,应该还有个兜底策略,当从elasticsearch查询不到的话,再从数据库中查询,就算全路由查询了,那也比什么都查询不到强
com.damai.service.ProgramService#selectHomeList
public List<ProgramHomeVo> selectHomeList(ProgramListDto programPageListDto) {
//先从elasticsearch查询
List<ProgramHomeVo> programHomeVoList = programEs.selectHomeList(programPageListDto);
if (CollectionUtil.isNotEmpty(programHomeVoList)) {
return programHomeVoList;
}
//elasticsearch中查询不到,再去数据库中查询
return dbSelectHomeList(programPageListDto);
}com.damai.service.es.ProgramEs#selectHomeList
@Slf4j
@Component
public class ProgramEs {
@Autowired
private BusinessEsHandle businessEsHandle;
public List<ProgramHomeVo> selectHomeList(ProgramListDto programPageListDto) {
List<ProgramHomeVo> programHomeVoList = new ArrayList<>();
try {
//按照父节目id集合来进行循环从es中查询,主页页面是4个父节目,也就是循环4次
for (Long parentProgramCategoryId : programPageListDto.getParentProgramCategoryIds()) {
List<EsDataQueryDto> 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<ProgramListVo> 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,关于此组件的详细介绍,可跳转到
这里将传入的父类型的节目id集合进行查询,每个循环内根据地区id和父节目类型id来查询出前7条,接着放入programListVoMap中,key为类型名,value为节目列表
注意
主页的列表数据要显示的并不是只有节目表中的数据,还有programCategoryName、showTime、showDayTime、showWeekTime、minPrice、maxPrice这些都是要从其他表中获取的,至于为什么从elasticsearch中可以直接查询出来,是因为在项目初始化时,将这些数据都已经组装好了,才放入到elasticsearch中的,
关于是如何将数据组装好后放入elasticsearch中的,可跳转到相应介绍部分详细查看
elasticsearch中的存储结构:
{
"_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中查询不到的话,就只有从数据库中查询了,全路由查询也比没有数据要好
private List<ProgramHomeVo> dbSelectHomeList(ProgramListDto programPageListDto){
List<ProgramHomeVo> programHomeVoList = new ArrayList<>();
//根据父节目类型id来查询节目类型map,key:节目类型id,value:节目类型名
Map<Long, String> programCategoryMap = selectProgramCategoryMap(programPageListDto.getParentProgramCategoryIds());
//查询节目列表
List<Program> programList = programMapper.selectHomeList(programPageListDto);
//如果节目列表为空,那么直接返回空map
if (CollectionUtil.isEmpty(programList)) {
return programHomeVoList;
}
//节目id集合
List<Long> programIdList = programList.stream().map(Program::getId).collect(Collectors.toList());
//根据节目id集合查询节目演出时间集合
LambdaQueryWrapper<ProgramShowTime> programShowTimeLambdaQueryWrapper = Wrappers.lambdaQuery(ProgramShowTime.class)
.in(ProgramShowTime::getProgramId, programIdList);
List<ProgramShowTime> programShowTimeList = programShowTimeMapper.selectList(programShowTimeLambdaQueryWrapper);
//将节目演出集合根据节目id进行分组成map,key:节目id,value:节目演出时间集合
Map<Long, List<ProgramShowTime>> programShowTimeMap =
programShowTimeList.stream().collect(Collectors.groupingBy(ProgramShowTime::getProgramId));
//根据节目id统计出票档的最低价和最高价的集合map, key:节目id,value:票档
Map<Long, TicketCategoryAggregate> ticketCategorieMap = selectTicketCategorieMap(programIdList);
//将节目结合按照父节目类型id进行分组成map,key:父节目类型id,value:节目集合
Map<Long, List<Program>> programMap = programList.stream()
.collect(Collectors.groupingBy(Program::getParentProgramCategoryId));
//循环此map
for (Entry<Long, List<Program>> programEntry : programMap.entrySet()) {
//父节目类型id
Long key = programEntry.getKey();
//节目集合
List<Program> value = programEntry.getValue();
List<ProgramListVo> 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这些仍然要从其他表中获取的,这些查询中都带有分片键,所以并不需要担心全路由的问题
查询节目类型名字
//根据父节目类型id来查询节目类型map,key:节目类型id,value:节目类型名
Map<Long, String> programCategoryMap = selectProgramCategoryMap(programPageListDto.getParentProgramCategoryIds());/**
* 根据节目类型id集合查询节目类型名字
* @param programCategoryIdList 节目类型id集合
* @return Map<Long, String> key:节目类型id value:节目类型名字
* */
public Map<Long, String> selectProgramCategoryMap(Collection<Long> programCategoryIdList){
LambdaQueryWrapper<ProgramCategory> pcLambdaQueryWrapper = Wrappers.lambdaQuery(ProgramCategory.class)
.in(ProgramCategory::getId, programCategoryIdList);
List<ProgramCategory> programCategoryList = programCategoryMapper.selectList(pcLambdaQueryWrapper);
return programCategoryList
.stream()
.collect(Collectors.toMap(ProgramCategory::getId, ProgramCategory::getName, (v1, v2) -> v2));
}根据传入的parentProgramCategoryIds父节目类型id集合,获取相关数据
查询节目列表
List<Program> programList = programMapper.selectHomeList(programPageListDto);这个查询的要求是查询根据每个parentProgramCategoryId查询出节目列表,并且取前7条,要用sql实现的话,就要这么做,前端传入参数以此为例:
{
"areaId":"2",
"parentProgramCategoryIds":["1","2"]
}形成的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
/**
* 主页查询
* @param programListDto 参数
* @return 结果
* */
List<Program> selectHomeList(@Param("programListDto")ProgramListDto programListDto);对应的xml文件
<select id="selectHomeList" parameterType="com.damai.dto.ProgramListDto" resultType="com.damai.entity.Program">
<if test = 'programListDto.parentProgramCategoryIds != null and programListDto.parentProgramCategoryIds.size>0'>
<foreach collection='programListDto.parentProgramCategoryIds' item='parentProgramCategoryId' index='index' separator=' 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
<if test ='programListDto.areaId == null'>
and dp.prime = 1
</if>
<if test ='programListDto.areaId != null'>
and dp.area_id = #{programListDto.areaId,jdbcType=BIGINT}
</if>
and
dp.parent_program_category_id = #{parentProgramCategoryId,jdbcType=BIGINT} limit 7
) as tmp
</foreach>
</if>
</select>使用了foreach循环标签,来循环parentProgramCategoryIds,并使用标签的separator属性,来将union all进行分隔,这样就能查询出节目列表了
接下来就是接着去别的表中查询需要的数据
查询节目演出时间
//节目id集合
List<Long> programIdList = programList.stream().map(Program::getId).collect(Collectors.toList());
//根据节目id集合查询节目演出时间集合
LambdaQueryWrapper<ProgramShowTime> programShowTimeLambdaQueryWrapper = Wrappers.lambdaQuery(ProgramShowTime.class)
.in(ProgramShowTime::getProgramId, programIdList);
List<ProgramShowTime> programShowTimeList = programShowTimeMapper.selectList(programShowTimeLambdaQueryWrapper);
//将节目演出集合根据节目id进行分组成map,key:节目id,value:节目演出时间集合
Map<Long, List<ProgramShowTime>> programShowTimeMap =
programShowTimeList.stream().collect(Collectors.groupingBy(ProgramShowTime::getProgramId));根据节目id统计出票档的最低价和最高价的集合
//根据节目id统计出票档的最低价和最高价的集合map, key:节目id,value:票档
Map<Long, TicketCategoryAggregate> ticketCategorieMap = selectTicketCategorieMap(programIdList);public Map<Long, TicketCategoryAggregate> selectTicketCategorieMap(List<Long> programIdList){
//根据节目id统计出票档的最低价和最高价的集合
List<TicketCategoryAggregate> ticketCategorieList = ticketCategoryMapper.selectAggregateList(programIdList);
//将集合转换为map,key:节目id,value:票档
return ticketCategorieList
.stream()
.collect(Collectors.toMap(TicketCategoryAggregate::getProgramId, ticketCategory -> ticketCategory, (v1, v2) -> v2));
}selectAggregateList也是自定义的sql,来统计出每个节目对应的票档最低价和最高价
<select id="selectAggregateList" resultType="com.damai.entity.TicketCategoryAggregate">
select
program_id,min(price) as min_price,max(price) as max_price
from d_ticket_category
where program_id in
<foreach collection='programIdList' item='programId' index='index' open='(' close=')' separator=','>
#{programId,jdbcType=BIGINT}
</foreach>
group by program_id
</select>当这些数据都查询出来后,接下来就是进行组装了
组装主页列表需要的数据
//将节目结合按照父节目类型id进行分组成map,key:父节目类型id,value:节目集合
Map<Long, List<Program>> programMap = programList.stream()
.collect(Collectors.groupingBy(Program::getParentProgramCategoryId));
//循环此map
for (Entry<Long, List<Program>> programEntry : programMap.entrySet()) {
//父节目类型id
Long key = programEntry.getKey();
//节目集合
List<Program> value = programEntry.getValue();
List<ProgramListVo> 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结构
@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<ProgramListVo> programListVoList;
}可以看一下组装后给前端返回的数据:
{
"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"
}
]
}
]
}更新: 2025-10-13 11:08:33
原文: https://www.yuque.com/u22210564/ykdrdh/zy3uubx1vfh31d1t