业务讲解-详解用户选择节目座位的流程
前置知识讲解
- 建议小伙伴先跳转到相应文档学习什么是缓存击穿?
- 学习如何利用双重检测锁思想优化查询节目详情?
流程讲解
在查询节目详情后,就开始了订票的流程,这时要分为两个分支,节目分为可选择座位和不可选择座位,根据节目表中的字段permit_choose_seat来判断
如果节目可以选择座位的话,在节目详情页面会有可选座的说明

可以点击选座购买,接着会跳转到选择座位的页面

控制层
com.damai.controller.SeatController#relateInfo
@ApiOperation(value = "查询座位相关信息")
@PostMapping(value = "/relate/info")
public ApiResponse<SeatRelateInfoVo> relateInfo(@Valid @RequestBody SeatListDto seatListDto) {
return ApiResponse.ok(seatService.relateInfo(seatListDto));
}service层
com.damai.service.SeatService#relateInfo
public SeatRelateInfoVo relateInfo(SeatListDto seatListDto) {
SeatRelateInfoVo seatRelateInfoVo = new SeatRelateInfoVo();
//从redis中查询节目
ProgramVo programVo =
redisCache.get(RedisKeyBuild.createRedisKey(RedisKeyManage.PROGRAM,seatListDto.getProgramId()),ProgramVo.class);
//如果查询不到,则去数据库中查询,再放入redis中
if (Objects.isNull(programVo)){
ProgramGetDto programGetDto = new ProgramGetDto();
programGetDto.setId(seatListDto.getProgramId());
programVo = programService.detail(programGetDto);
}
//查询演出时间
ProgramShowTime programShowTime = programShowTimeService.selectProgramShowTimeByProgramId(seatListDto.getProgramId());
//查询该节目下的票档集合
List<TicketCategoryVo> ticketCategoryVoList = ticketCategoryService
.selectTicketCategoryListByProgramIdMultipleCache(programVo.getId(),programShowTime.getShowTime());
List<SeatVo> seatVos = new ArrayList<>();
//遍历票档集合
for (TicketCategoryVo ticketCategoryVo : ticketCategoryVoList) {
//根据节目id和票档id来查询出座位集合,汇总到seatVos中
seatVos.addAll(selectSeatResolution(seatListDto.getProgramId(),ticketCategoryVo.getId(),
DateUtils.countBetweenSecond(DateUtils.now(), programShowTime.getShowTime()), TimeUnit.SECONDS));
}
//如果节目不允许选择座位,直接抛出异常
if (programVo.getPermitChooseSeat().equals(BusinessStatus.NO.getCode())) {
throw new DaMaiFrameException(BaseCode.PROGRAM_NOT_ALLOW_CHOOSE_SEAT);
}
Map<String, List<SeatVo>> seatVoMap =
seatVos.stream().collect(Collectors.groupingBy(seatVo -> seatVo.getPrice().toString()));
//节目id
seatRelateInfoVo.setProgramId(programVo.getId());
//节目地点
seatRelateInfoVo.setPlace(programVo.getPlace());
//节目演出时间
seatRelateInfoVo.setShowTime(programShowTime.getShowTime());
//节目演出时间对应的星期
seatRelateInfoVo.setShowWeekTime(programShowTime.getShowWeekTime());
//节目的价格列表
seatRelateInfoVo.setPriceList(seatVoMap.keySet().stream().sorted().collect(Collectors.toList()));
//节目的座位列表
seatRelateInfoVo.setSeatVoMap(seatVoMap);
return seatRelateInfoVo;
}下面开始逐步的进行讲解
查询节目信息
//从redis中查询节目
ProgramVo programVo =
redisCache.get(RedisKeyBuild.createRedisKey(RedisKeyManage.PROGRAM,seatListDto.getProgramId()),ProgramVo.class);
//如果查询不到,则去数据库中查询,在放入redis中
if (Objects.isNull(programVo)){
ProgramGetDto programGetDto = new ProgramGetDto();
programGetDto.setId(seatListDto.getProgramId());
//查询节目详情
programVo = programService.detail(programGetDto);
}先从redis查询节目信息,查询不到的话,调用节目详情方法
其实查询座位信息这步之前是要查询节目详情的,在查询节目详情中已经节目信息放到redis了中,这里再查询一遍也是为了保险,说白了,还是怕有意外将请求直接落到数据库上,造成数据库的压力过大
关于节目详情的具体流程,在查询节目详情的文档中有细致的讲解,本文就不再赘述
接下来的流程就不是很复杂了,查询座位信息、查询演出时间,然后将信息进行组装后返回给前端
查询演出时间
ProgramShowTime programShowTime = programShowTimeService.selectProgramShowTimeByProgramId(seatListDto.getProgramId());查询演出时间的详细逻辑在如何应对突发性热点数据暴增导致系统压力过大问题?章节中已经详细讲解过,这里就不再赘述
查询座位
//遍历票档集合
for (TicketCategoryVo ticketCategoryVo : ticketCategoryVoList) {
//根据节目id和票档id来查询出座位集合,汇总到seatVos中
seatVos.addAll(selectSeatResolution(seatListDto.getProgramId(),ticketCategoryVo.getId(),
DateUtils.countBetweenSecond(DateUtils.now(), programShowTime.getShowTime()), TimeUnit.SECONDS));
}@ServiceLock(lockType= LockType.Read,name = SEAT_LOCK,keys = {"#programId","#ticketCategoryId"})
public List<SeatVo> selectSeatResolution(Long programId,Long ticketCategoryId,Long expireTime,TimeUnit timeUnit) {
//从缓存中查询所有状态的座位
List<SeatVo> seatVoList = getSeatVoListByCacheResolution(programId,ticketCategoryId);
if (CollectionUtil.isNotEmpty(seatVoList)) {
return seatVoList;
}
//如果redis中三种状态的座位都没有,说明根本就没有往redis中存放过
//加锁
RLock lock = serviceLockTool.getLock(LockType.Reentrant, GET_SEAT_LOCK, new String[]{String.valueOf(programId),
String.valueOf(ticketCategoryId)});
lock.lock();
try {
//再从缓存中查询座位数据
seatVoList = getSeatVoListByCacheResolution(programId,ticketCategoryId);
if (CollectionUtil.isNotEmpty(seatVoList)) {
return seatVoList;
}
//如果缓存中还是没有,则从数据库中查询
LambdaQueryWrapper<Seat> seatLambdaQueryWrapper =
Wrappers.lambdaQuery(Seat.class).eq(Seat::getProgramId, programId)
.eq(Seat::getTicketCategoryId,ticketCategoryId);
List<Seat> seats = seatMapper.selectList(seatLambdaQueryWrapper);
for (Seat seat : seats) {
SeatVo seatVo = new SeatVo();
BeanUtil.copyProperties(seat, seatVo);
seatVo.setSeatTypeName(SeatType.getMsg(seat.getSeatType()));
seatVoList.add(seatVo);
}
//将座位按照状态进行分类
Map<Integer, List<SeatVo>> seatMap = seatVoList.stream().collect(Collectors.groupingBy(SeatVo::getSellStatus));
//没有售卖的座位
List<SeatVo> noSoldSeatVoList = seatMap.get(SellStatus.NO_SOLD.getCode());
//正在锁定的座位
List<SeatVo> lockSeatVoList = seatMap.get(SellStatus.LOCK.getCode());
//已经售卖的座位
List<SeatVo> soldSeatVoList = seatMap.get(SellStatus.SOLD.getCode());
if (CollectionUtil.isNotEmpty(noSoldSeatVoList)) {
//将没有售卖的座位放入redis中
redisCache.putHash(RedisKeyBuild.createRedisKey(RedisKeyManage.PROGRAM_SEAT_NO_SOLD_RESOLUTION_HASH,
programId,ticketCategoryId),noSoldSeatVoList.stream()
.collect(Collectors.toMap(s -> String.valueOf(s.getId()),s -> s,(v1,v2) -> v2))
,expireTime, timeUnit);
}
if (CollectionUtil.isNotEmpty(lockSeatVoList)) {
//将正在锁定的座位放入redis中
redisCache.putHash(RedisKeyBuild.createRedisKey(RedisKeyManage.PROGRAM_SEAT_LOCK_RESOLUTION_HASH,
programId,ticketCategoryId),lockSeatVoList.stream()
.collect(Collectors.toMap(s -> String.valueOf(s.getId()),s -> s,(v1,v2) -> v2))
,expireTime, timeUnit);
}
if (CollectionUtil.isNotEmpty(soldSeatVoList)) {
//将已经售卖的座位放入redis中
redisCache.putHash(RedisKeyBuild.createRedisKey(RedisKeyManage.PROGRAM_SEAT_SOLD_RESOLUTION_HASH,
programId,ticketCategoryId)
,soldSeatVoList.stream()
.collect(Collectors.toMap(s -> String.valueOf(s.getId()),s -> s,(v1,v2) -> v2))
,expireTime, timeUnit);
}
//将座位集合,先按座位行号排序,再接着按座位列号排序
seatVoList = seatVoList.stream().sorted(Comparator.comparingInt(SeatVo::getRowCode)
.thenComparingInt(SeatVo::getColCode)).collect(Collectors.toList());
return seatVoList;
}finally {
lock.unlock();
}
}分别从redis中查询没有售卖、锁定中、已售卖所有状态的座位列表,如果都没有查到,说明redis中没有存放过,则去数据库中查询座位,再把这些座位按照售卖状态分组后,再放入redis中
获得了座位集合后,将这些所有的座位先按座位行号排序,再接着按座位列号排序后,将列表返回
验证节目是否选座
if (programVo.getPermitChooseSeat().equals(BusinessStatus.NO.getCode())) {
throw new DaMaiFrameException(BaseCode.PROGRAM_NOT_ALLOW_CHOOSE_SEAT);
}如果节目的规定是不允许进行手动选择座位的,则要抛出异常,不再往下执行
这里可能有小伙伴会想为什么不在查询座位之前来验证节目是否选座?而是要在查询座位后再验证?
因为不论节目是否允许选座,等到用户下单后,都是要选择相应的座位进行锁定的,把座位加载出来放到缓存中,这样别的用户查询这个节目的座位时,就可以直接从缓存中查询了。
将座位列表分组
Map<String, List<SeatVo>> seatVoMap =
seatVos.stream().collect(Collectors.groupingBy(seatVo -> seatVo.getPrice().toString()));这里是将座位集合,按照座位的价格进行分组成Map结构。key:座位价格 value:座位集合
组装数据
到这步需要展示的数据就已经全部查询出来了,接着就是组装数据
//节目id
seatRelateInfoVo.setProgramId(programVo.getId());
//节目地点
seatRelateInfoVo.setPlace(programVo.getPlace());
//节目演出时间
seatRelateInfoVo.setShowTime(programShowTime.getShowTime());
//节目演出时间对应的星期
seatRelateInfoVo.setShowWeekTime(programShowTime.getShowWeekTime());
//节目的价格列表
seatRelateInfoVo.setPriceList(seatVoMap.keySet().stream().sorted().collect(Collectors.toList()));
//节目的座位列表
seatRelateInfoVo.setSeatVoMap(seatVoMap);
return seatRelateInfoVo;**seatVoMap.keySet().stream().sorted().collect(Collectors.toList())**就是价格列表
用户在选择价格时,需要将该价格对应的座位列表进行高亮显示,前端可根据返回的seatVoMap也就是Map结构来实现此功能
返回前端的实体Vo对象
com.damai.vo.SeatRelateInfoVo
@Data
@ApiModel(value="SeatRelateInfoVo", description ="座位相关信息")
public class SeatRelateInfoVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(name ="programId", dataType ="Long", value ="节目表id")
private Long programId;
@ApiModelProperty(name ="place", dataType ="String", value ="地点")
private String place;
@ApiModelProperty(name ="showTime", dataType ="Date", value ="演出时间")
private Date showTime;
@ApiModelProperty(name ="showWeekTime", dataType ="String", value ="演出时间所在的星期")
private String showWeekTime;
@ApiModelProperty(name ="priceList", dataType ="List<String>", value ="价格集合")
private List<String> priceList;
@ApiModelProperty(name ="seatVoMap", dataType ="Map<String,List<SeatVo>>", value ="座位集合")
private Map<String,List<SeatVo>> seatVoMap;
}返回给前端的示例数据结构
{
"code": "0",
"message": "",
"data": {
"programId": "1",
"place": "本土王牌LIVE",
"showTime": "2024-03-21 20:00:00",
"showDayTime": "",
"showWeekTime": "周四",
"priceList": [
"180"
],
"seatVoMap": {
"180": [
{
"id": "1",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "1",
"colCode": "1",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "2",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "1",
"colCode": "2",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "3",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "1",
"colCode": "3",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "4",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "1",
"colCode": "4",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "5",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "1",
"colCode": "5",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "6",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "1",
"colCode": "6",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "7",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "1",
"colCode": "7",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "8",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "1",
"colCode": "8",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "9",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "1",
"colCode": "9",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "10",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "1",
"colCode": "10",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "11",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "2",
"colCode": "1",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "12",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "2",
"colCode": "2",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "13",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "2",
"colCode": "3",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "14",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "2",
"colCode": "4",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "15",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "2",
"colCode": "5",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "16",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "2",
"colCode": "6",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "17",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "2",
"colCode": "7",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "18",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "2",
"colCode": "8",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "19",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "2",
"colCode": "9",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "20",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "2",
"colCode": "10",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "21",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "3",
"colCode": "1",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "22",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "3",
"colCode": "2",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "23",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "3",
"colCode": "3",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "24",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "3",
"colCode": "4",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "25",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "3",
"colCode": "5",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "26",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "3",
"colCode": "6",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "27",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "3",
"colCode": "7",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "28",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "3",
"colCode": "8",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "29",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "3",
"colCode": "9",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "30",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "3",
"colCode": "10",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "31",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "4",
"colCode": "1",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "32",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "4",
"colCode": "2",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "33",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "4",
"colCode": "3",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "34",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "4",
"colCode": "4",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "35",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "4",
"colCode": "5",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "36",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "4",
"colCode": "6",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "37",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "4",
"colCode": "7",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "38",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "4",
"colCode": "8",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "39",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "4",
"colCode": "9",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "40",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "4",
"colCode": "10",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "41",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "5",
"colCode": "1",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "42",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "5",
"colCode": "2",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "43",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "5",
"colCode": "3",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "44",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "5",
"colCode": "4",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "45",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "5",
"colCode": "5",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "46",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "5",
"colCode": "6",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "47",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "5",
"colCode": "7",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "48",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "5",
"colCode": "8",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "49",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "5",
"colCode": "9",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "50",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "5",
"colCode": "10",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "51",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "6",
"colCode": "1",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "52",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "6",
"colCode": "2",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "53",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "6",
"colCode": "3",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "54",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "6",
"colCode": "4",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "55",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "6",
"colCode": "5",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "56",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "6",
"colCode": "6",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "57",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "6",
"colCode": "7",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "58",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "6",
"colCode": "8",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "59",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "6",
"colCode": "9",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
},
{
"id": "60",
"programId": "1",
"ticketCategoryId": "2",
"rowCode": "6",
"colCode": "10",
"seatType": "1",
"seatTypeName": "通用座位",
"price": "180",
"sellStatus": "1"
}
]
}
}
}更新: 2025-11-21 10:28:18
原文: https://www.yuque.com/u22210564/ykdrdh/gln3ytflw2keiuvw