|
@@ -0,0 +1,496 @@
|
|
|
+package com.sf.service.handler;
|
|
|
+
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
+import cn.hutool.core.collection.ListUtil;
|
|
|
+import cn.hutool.core.lang.Pair;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
+import com.google.common.collect.Lists;
|
|
|
+import com.sf.dto.domain.SelectSeatDTO;
|
|
|
+import com.sf.dto.domain.TrainSeatBaseDTO;
|
|
|
+import com.sf.dto.req.PurchaseTicketPassengerDetailDTO;
|
|
|
+import com.sf.dto.resp.TrainPurchaseTicketRespDTO;
|
|
|
+import com.sf.entity.SeatDO;
|
|
|
+import com.sf.mapper.SeatMapper;
|
|
|
+import com.sf.util.CarriageVacantSeatCalculateUtil;
|
|
|
+import com.sf.util.SeatNumberUtil;
|
|
|
+import com.sf.util.checkseat.BitMapCheckSeat;
|
|
|
+import com.sf.util.checkseat.SeatSelection;
|
|
|
+import com.sf.util.checkseat.TrainBusinessCheckSeat;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import org.opengoofy.index12306.framework.starter.convention.exception.ServiceException;
|
|
|
+import org.springframework.data.redis.core.StringRedisTemplate;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import java.util.*;
|
|
|
+import java.util.concurrent.atomic.AtomicInteger;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+import static com.sf.constant.RedisKeyConstant.TRAIN_STATION_CARRIAGE_REMAINING_TICKET;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 高铁商务座购票组件
|
|
|
+ * 公众号:马丁玩编程,回复:加群,添加马哥微信(备注:12306)获取项目资料
|
|
|
+ */
|
|
|
+@Component
|
|
|
+@RequiredArgsConstructor
|
|
|
+public class TrainBusinessClassPurchaseTicketHandler {
|
|
|
+
|
|
|
+ // seatService -> seatMapper
|
|
|
+ private final SeatMapper seatMapper;
|
|
|
+
|
|
|
+ private static final Map<Character, Integer> SEAT_Y_INT = Map.of('A', 0, 'C', 1, 'F', 2);
|
|
|
+
|
|
|
+ public List<TrainPurchaseTicketRespDTO> selectSeats(SelectSeatDTO requestParam) {
|
|
|
+ String trainId = requestParam.getRequestParam().getTrainId();
|
|
|
+ String departure = requestParam.getRequestParam().getDeparture();
|
|
|
+ String arrival = requestParam.getRequestParam().getArrival();
|
|
|
+ List<PurchaseTicketPassengerDetailDTO> passengerSeatDetails = requestParam.getPassengerSeatDetails();
|
|
|
+ List<String> trainCarriageList = listUsableCarriageNumber(trainId, requestParam.getSeatType(), departure, arrival);
|
|
|
+ List<Integer> trainStationCarriageRemainingTicket = listSeatRemainingTicket(trainId, departure, arrival, trainCarriageList);
|
|
|
+ int remainingTicketSum = trainStationCarriageRemainingTicket.stream().mapToInt(Integer::intValue).sum();
|
|
|
+ if (remainingTicketSum < passengerSeatDetails.size()) {
|
|
|
+ throw new ServiceException("站点余票不足,请尝试更换座位类型或选择其它站点");
|
|
|
+ }
|
|
|
+ if (passengerSeatDetails.size() < 3) {
|
|
|
+ if (CollUtil.isNotEmpty(requestParam.getRequestParam().getChooseSeats())) {
|
|
|
+ Pair<List<TrainPurchaseTicketRespDTO>, Boolean> actualSeatPair = findMatchSeats(requestParam, trainCarriageList, trainStationCarriageRemainingTicket);
|
|
|
+ return actualSeatPair.getKey();
|
|
|
+ }
|
|
|
+ return selectSeats(requestParam, trainCarriageList, trainStationCarriageRemainingTicket);
|
|
|
+ } else {
|
|
|
+ if (CollUtil.isNotEmpty(requestParam.getRequestParam().getChooseSeats())) {
|
|
|
+ Pair<List<TrainPurchaseTicketRespDTO>, Boolean> actualSeatPair = findMatchSeats(requestParam, trainCarriageList, trainStationCarriageRemainingTicket);
|
|
|
+ return actualSeatPair.getKey();
|
|
|
+ }
|
|
|
+ return selectComplexSeats(requestParam, trainCarriageList, trainStationCarriageRemainingTicket);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<String> listUsableCarriageNumber(String trainId, Integer carriageType, String departure, String arrival) {
|
|
|
+ LambdaQueryWrapper<SeatDO> queryWrapper = Wrappers.lambdaQuery(SeatDO.class)
|
|
|
+ .eq(SeatDO::getTrainId, trainId)
|
|
|
+ .eq(SeatDO::getSeatType, carriageType)
|
|
|
+ .eq(SeatDO::getStartStation, departure)
|
|
|
+ .eq(SeatDO::getEndStation, arrival)
|
|
|
+ .eq(SeatDO::getSeatStatus, 0) // 座位可售状态
|
|
|
+ .groupBy(SeatDO::getCarriageNumber)
|
|
|
+ .select(SeatDO::getCarriageNumber);
|
|
|
+ List<SeatDO> seatDOList = seatMapper.selectList(queryWrapper);
|
|
|
+ return seatDOList.stream().map(SeatDO::getCarriageNumber).collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<Integer> listSeatRemainingTicket(String trainId, String departure, String arrival, List<String> trainCarriageList) {
|
|
|
+ SeatDO seatDO = SeatDO.builder()
|
|
|
+ .trainId(Long.parseLong(trainId))
|
|
|
+ .startStation(departure)
|
|
|
+ .endStation(arrival)
|
|
|
+ .build();
|
|
|
+ return seatMapper.listSeatRemainingTicket(seatDO, trainCarriageList);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Pair<List<TrainPurchaseTicketRespDTO>, Boolean> findMatchSeats(SelectSeatDTO requestParam, List<String> trainCarriageList, List<Integer> trainStationCarriageRemainingTicket) {
|
|
|
+ TrainSeatBaseDTO trainSeatBaseDTO = buildTrainSeatBaseDTO(requestParam);
|
|
|
+ int chooseSeatSize = trainSeatBaseDTO.getChooseSeatList().size();
|
|
|
+ List<TrainPurchaseTicketRespDTO> actualResult = Lists.newArrayListWithCapacity(trainSeatBaseDTO.getPassengerSeatDetails().size());
|
|
|
+ BitMapCheckSeat instance = new TrainBusinessCheckSeat();
|
|
|
+ HashMap<String, List<Pair<Integer, Integer>>> carriagesSeatMap = new HashMap<>(4);
|
|
|
+ int passengersNumber = trainSeatBaseDTO.getPassengerSeatDetails().size();
|
|
|
+ for (int i = 0; i < trainStationCarriageRemainingTicket.size(); i++) {
|
|
|
+ String carriagesNumber = trainCarriageList.get(i);
|
|
|
+ List<String> listAvailableSeat = listAvailableSeat(trainSeatBaseDTO.getTrainId(), carriagesNumber, requestParam.getSeatType(), trainSeatBaseDTO.getDeparture(), trainSeatBaseDTO.getArrival());
|
|
|
+ int[][] actualSeats = new int[2][3];
|
|
|
+ for (int j = 1; j < 3; j++) {
|
|
|
+ for (int k = 1; k < 4; k++) {
|
|
|
+ actualSeats[j - 1][k - 1] = listAvailableSeat.contains("0" + j + SeatNumberUtil.convert(0, k)) ? 0 : 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ List<Pair<Integer, Integer>> vacantSeatList = CarriageVacantSeatCalculateUtil.buildCarriageVacantSeatList2(actualSeats, 2, 3);
|
|
|
+ boolean isExists = instance.checkChooseSeat(trainSeatBaseDTO.getChooseSeatList(), actualSeats, SEAT_Y_INT);
|
|
|
+ long vacantSeatCount = vacantSeatList.size();
|
|
|
+ List<Pair<Integer, Integer>> sureSeatList = new ArrayList<>();
|
|
|
+ List<String> selectSeats = Lists.newArrayListWithCapacity(passengersNumber);
|
|
|
+ boolean flag = false;
|
|
|
+ if (isExists && vacantSeatCount >= passengersNumber) {
|
|
|
+ Iterator<Pair<Integer, Integer>> pairIterator = vacantSeatList.iterator();
|
|
|
+ for (int i1 = 0; i1 < chooseSeatSize; i1++) {
|
|
|
+ if (chooseSeatSize == 1) {
|
|
|
+ String chooseSeat = trainSeatBaseDTO.getChooseSeatList().get(i1);
|
|
|
+ int seatX = Integer.parseInt(chooseSeat.substring(1));
|
|
|
+ int seatY = SEAT_Y_INT.get(chooseSeat.charAt(0));
|
|
|
+ if (actualSeats[seatX][seatY] == 0) {
|
|
|
+ sureSeatList.add(new Pair<>(seatX, seatY));
|
|
|
+ while (pairIterator.hasNext()) {
|
|
|
+ Pair<Integer, Integer> pair = pairIterator.next();
|
|
|
+ if (pair.getKey() == seatX && pair.getValue() == seatY) {
|
|
|
+ pairIterator.remove();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (actualSeats[1][seatY] == 0) {
|
|
|
+ sureSeatList.add(new Pair<>(1, seatY));
|
|
|
+ while (pairIterator.hasNext()) {
|
|
|
+ Pair<Integer, Integer> pair = pairIterator.next();
|
|
|
+ if (pair.getKey() == 1 && pair.getValue() == seatY) {
|
|
|
+ pairIterator.remove();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ flag = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ String chooseSeat = trainSeatBaseDTO.getChooseSeatList().get(i1);
|
|
|
+ int seatX = Integer.parseInt(chooseSeat.substring(1));
|
|
|
+ int seatY = SEAT_Y_INT.get(chooseSeat.charAt(0));
|
|
|
+ if (actualSeats[seatX][seatY] == 0) {
|
|
|
+ sureSeatList.add(new Pair<>(seatX, seatY));
|
|
|
+ while (pairIterator.hasNext()) {
|
|
|
+ Pair<Integer, Integer> pair = pairIterator.next();
|
|
|
+ if (pair.getKey() == seatX && pair.getValue() == seatY) {
|
|
|
+ pairIterator.remove();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (flag && i < trainStationCarriageRemainingTicket.size() - 1) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (sureSeatList.size() != passengersNumber) {
|
|
|
+ int needSeatSize = passengersNumber - sureSeatList.size();
|
|
|
+ sureSeatList.addAll(vacantSeatList.subList(0, needSeatSize));
|
|
|
+ }
|
|
|
+ for (Pair<Integer, Integer> each : sureSeatList) {
|
|
|
+ selectSeats.add("0" + (each.getKey() + 1) + SeatNumberUtil.convert(0, (each.getValue() + 1)));
|
|
|
+ }
|
|
|
+ AtomicInteger countNum = new AtomicInteger(0);
|
|
|
+ for (String selectSeat : selectSeats) {
|
|
|
+ TrainPurchaseTicketRespDTO result = new TrainPurchaseTicketRespDTO();
|
|
|
+ PurchaseTicketPassengerDetailDTO currentTicketPassenger = trainSeatBaseDTO.getPassengerSeatDetails().get(countNum.getAndIncrement());
|
|
|
+ result.setSeatNumber(selectSeat);
|
|
|
+ result.setSeatType(currentTicketPassenger.getSeatType());
|
|
|
+ result.setCarriageNumber(carriagesNumber);
|
|
|
+ result.setPassengerId(currentTicketPassenger.getPassengerId());
|
|
|
+ actualResult.add(result);
|
|
|
+ }
|
|
|
+ return new Pair<>(actualResult, Boolean.TRUE);
|
|
|
+ } else {
|
|
|
+ if (i < trainStationCarriageRemainingTicket.size()) {
|
|
|
+ if (vacantSeatCount > 0) {
|
|
|
+ carriagesSeatMap.put(carriagesNumber, vacantSeatList);
|
|
|
+ }
|
|
|
+ if (i == trainStationCarriageRemainingTicket.size() - 1) {
|
|
|
+ Pair<String, List<Pair<Integer, Integer>>> findSureCarriage = null;
|
|
|
+ for (Map.Entry<String, List<Pair<Integer, Integer>>> entry : carriagesSeatMap.entrySet()) {
|
|
|
+ if (entry.getValue().size() >= passengersNumber) {
|
|
|
+ findSureCarriage = new Pair<>(entry.getKey(), entry.getValue().subList(0, passengersNumber));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (null != findSureCarriage) {
|
|
|
+ sureSeatList = findSureCarriage.getValue().subList(0, passengersNumber);
|
|
|
+ for (Pair<Integer, Integer> each : sureSeatList) {
|
|
|
+ selectSeats.add("0" + (each.getKey() + 1) + SeatNumberUtil.convert(0, each.getValue() + 1));
|
|
|
+ }
|
|
|
+ AtomicInteger countNum = new AtomicInteger(0);
|
|
|
+ for (String selectSeat : selectSeats) {
|
|
|
+ TrainPurchaseTicketRespDTO result = new TrainPurchaseTicketRespDTO();
|
|
|
+ PurchaseTicketPassengerDetailDTO currentTicketPassenger = trainSeatBaseDTO.getPassengerSeatDetails().get(countNum.getAndIncrement());
|
|
|
+ result.setSeatNumber(selectSeat);
|
|
|
+ result.setSeatType(currentTicketPassenger.getSeatType());
|
|
|
+ result.setCarriageNumber(findSureCarriage.getKey());
|
|
|
+ result.setPassengerId(currentTicketPassenger.getPassengerId());
|
|
|
+ actualResult.add(result);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ int sureSeatListSize = 0;
|
|
|
+ AtomicInteger countNum = new AtomicInteger(0);
|
|
|
+ for (Map.Entry<String, List<Pair<Integer, Integer>>> entry : carriagesSeatMap.entrySet()) {
|
|
|
+ if (sureSeatListSize < passengersNumber) {
|
|
|
+ if (sureSeatListSize + entry.getValue().size() < passengersNumber) {
|
|
|
+ sureSeatListSize = sureSeatListSize + entry.getValue().size();
|
|
|
+ List<String> actualSelectSeats = new ArrayList<>();
|
|
|
+ for (Pair<Integer, Integer> each : entry.getValue()) {
|
|
|
+ actualSelectSeats.add("0" + (each.getKey() + 1) + SeatNumberUtil.convert(0, each.getValue() + 1));
|
|
|
+ }
|
|
|
+ for (String selectSeat : actualSelectSeats) {
|
|
|
+ TrainPurchaseTicketRespDTO result = new TrainPurchaseTicketRespDTO();
|
|
|
+ PurchaseTicketPassengerDetailDTO currentTicketPassenger = trainSeatBaseDTO.getPassengerSeatDetails().get(countNum.getAndIncrement());
|
|
|
+ result.setSeatNumber(selectSeat);
|
|
|
+ result.setSeatType(currentTicketPassenger.getSeatType());
|
|
|
+ result.setCarriageNumber(entry.getKey());
|
|
|
+ result.setPassengerId(currentTicketPassenger.getPassengerId());
|
|
|
+ actualResult.add(result);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ int needSeatSize = entry.getValue().size() - (sureSeatListSize + entry.getValue().size() - passengersNumber);
|
|
|
+ sureSeatListSize = sureSeatListSize + needSeatSize;
|
|
|
+ if (sureSeatListSize >= passengersNumber) {
|
|
|
+ List<String> actualSelectSeats = new ArrayList<>();
|
|
|
+ for (Pair<Integer, Integer> each : entry.getValue().subList(0, needSeatSize)) {
|
|
|
+ actualSelectSeats.add("0" + (each.getKey() + 1) + SeatNumberUtil.convert(0, each.getValue() + 1));
|
|
|
+ }
|
|
|
+ for (String selectSeat : actualSelectSeats) {
|
|
|
+ TrainPurchaseTicketRespDTO result = new TrainPurchaseTicketRespDTO();
|
|
|
+ PurchaseTicketPassengerDetailDTO currentTicketPassenger = trainSeatBaseDTO.getPassengerSeatDetails().get(countNum.getAndIncrement());
|
|
|
+ result.setSeatNumber(selectSeat);
|
|
|
+ result.setSeatType(currentTicketPassenger.getSeatType());
|
|
|
+ result.setCarriageNumber(entry.getKey());
|
|
|
+ result.setPassengerId(currentTicketPassenger.getPassengerId());
|
|
|
+ actualResult.add(result);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return new Pair<>(actualResult, Boolean.TRUE);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return new Pair<>(null, Boolean.FALSE);
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<TrainPurchaseTicketRespDTO> selectSeats(SelectSeatDTO requestParam, List<String> trainCarriageList, List<Integer> trainStationCarriageRemainingTicket) {
|
|
|
+ String trainId = requestParam.getRequestParam().getTrainId();
|
|
|
+ String departure = requestParam.getRequestParam().getDeparture();
|
|
|
+ String arrival = requestParam.getRequestParam().getArrival();
|
|
|
+ List<PurchaseTicketPassengerDetailDTO> passengerSeatDetails = requestParam.getPassengerSeatDetails();
|
|
|
+ List<TrainPurchaseTicketRespDTO> actualResult = new ArrayList<>();
|
|
|
+ Map<String, Integer> demotionStockNumMap = new LinkedHashMap<>();
|
|
|
+ Map<String, int[][]> actualSeatsMap = new HashMap<>();
|
|
|
+ Map<String, int[][]> carriagesNumberSeatsMap = new HashMap<>();
|
|
|
+ String carriagesNumber;
|
|
|
+ for (int i = 0; i < trainStationCarriageRemainingTicket.size(); i++) {
|
|
|
+ carriagesNumber = trainCarriageList.get(i);
|
|
|
+ List<String> listAvailableSeat = listAvailableSeat(trainId, carriagesNumber, requestParam.getSeatType(), departure, arrival);
|
|
|
+ int[][] actualSeats = new int[2][3];
|
|
|
+ for (int j = 1; j < 3; j++) {
|
|
|
+ for (int k = 1; k < 4; k++) {
|
|
|
+ // 当前默认按照复兴号商务座排序,后续这里需要按照简单工厂对车类型进行获取 y 轴
|
|
|
+ actualSeats[j - 1][k - 1] = listAvailableSeat.contains("0" + j + SeatNumberUtil.convert(0, k)) ? 0 : 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ int[][] select = SeatSelection.adjacent(passengerSeatDetails.size(), actualSeats);
|
|
|
+ if (select != null) {
|
|
|
+ carriagesNumberSeatsMap.put(carriagesNumber, select);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ int demotionStockNum = 0;
|
|
|
+ for (int[] actualSeat : actualSeats) {
|
|
|
+ for (int i1 : actualSeat) {
|
|
|
+ if (i1 == 0) {
|
|
|
+ demotionStockNum++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ demotionStockNumMap.putIfAbsent(carriagesNumber, demotionStockNum);
|
|
|
+ actualSeatsMap.putIfAbsent(carriagesNumber, actualSeats);
|
|
|
+ if (i < trainStationCarriageRemainingTicket.size() - 1) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ // 如果邻座算法无法匹配,尝试对用户进行降级分配:同车厢不邻座
|
|
|
+ for (Map.Entry<String, Integer> entry : demotionStockNumMap.entrySet()) {
|
|
|
+ String carriagesNumberBack = entry.getKey();
|
|
|
+ int demotionStockNumBack = entry.getValue();
|
|
|
+ if (demotionStockNumBack > passengerSeatDetails.size()) {
|
|
|
+ int[][] seats = actualSeatsMap.get(carriagesNumberBack);
|
|
|
+ int[][] nonAdjacentSeats = SeatSelection.nonAdjacent(passengerSeatDetails.size(), seats);
|
|
|
+ if (Objects.equals(nonAdjacentSeats.length, passengerSeatDetails.size())) {
|
|
|
+ select = nonAdjacentSeats;
|
|
|
+ carriagesNumberSeatsMap.put(carriagesNumberBack, select);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 如果同车厢也已无法匹配,则对用户座位再次降级:不同车厢不邻座
|
|
|
+ if (Objects.isNull(select)) {
|
|
|
+ for (Map.Entry<String, Integer> entry : demotionStockNumMap.entrySet()) {
|
|
|
+ String carriagesNumberBack = entry.getKey();
|
|
|
+ int demotionStockNumBack = entry.getValue();
|
|
|
+ int[][] seats = actualSeatsMap.get(carriagesNumberBack);
|
|
|
+ int[][] nonAdjacentSeats = SeatSelection.nonAdjacent(demotionStockNumBack, seats);
|
|
|
+ carriagesNumberSeatsMap.put(entry.getKey(), nonAdjacentSeats);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 乘车人员在单一车厢座位不满足,触发乘车人元分布在不同车厢
|
|
|
+ int count = (int) carriagesNumberSeatsMap.values().stream()
|
|
|
+ .flatMap(Arrays::stream)
|
|
|
+ .count();
|
|
|
+ if (CollUtil.isNotEmpty(carriagesNumberSeatsMap) && passengerSeatDetails.size() == count) {
|
|
|
+ int countNum = 0;
|
|
|
+ for (Map.Entry<String, int[][]> entry : carriagesNumberSeatsMap.entrySet()) {
|
|
|
+ List<String> selectSeats = new ArrayList<>();
|
|
|
+ for (int[] ints : entry.getValue()) {
|
|
|
+ selectSeats.add("0" + ints[0] + SeatNumberUtil.convert(0, ints[1]));
|
|
|
+ }
|
|
|
+ for (String selectSeat : selectSeats) {
|
|
|
+ TrainPurchaseTicketRespDTO result = new TrainPurchaseTicketRespDTO();
|
|
|
+ PurchaseTicketPassengerDetailDTO currentTicketPassenger = passengerSeatDetails.get(countNum++);
|
|
|
+ result.setSeatNumber(selectSeat);
|
|
|
+ result.setSeatType(currentTicketPassenger.getSeatType());
|
|
|
+ result.setCarriageNumber(entry.getKey());
|
|
|
+ result.setPassengerId(currentTicketPassenger.getPassengerId());
|
|
|
+ actualResult.add(result);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return actualResult;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<TrainPurchaseTicketRespDTO> selectComplexSeats(SelectSeatDTO requestParam, List<String> trainCarriageList, List<Integer> trainStationCarriageRemainingTicket) {
|
|
|
+ String trainId = requestParam.getRequestParam().getTrainId();
|
|
|
+ String departure = requestParam.getRequestParam().getDeparture();
|
|
|
+ String arrival = requestParam.getRequestParam().getArrival();
|
|
|
+ List<PurchaseTicketPassengerDetailDTO> passengerSeatDetails = requestParam.getPassengerSeatDetails();
|
|
|
+ List<TrainPurchaseTicketRespDTO> actualResult = new ArrayList<>();
|
|
|
+ Map<String, Integer> demotionStockNumMap = new LinkedHashMap<>();
|
|
|
+ Map<String, int[][]> actualSeatsMap = new HashMap<>();
|
|
|
+ Map<String, int[][]> carriagesNumberSeatsMap = new HashMap<>();
|
|
|
+ String carriagesNumber;
|
|
|
+ // 多人分配同一车厢邻座
|
|
|
+ for (int i = 0; i < trainStationCarriageRemainingTicket.size(); i++) {
|
|
|
+ carriagesNumber = trainCarriageList.get(i);
|
|
|
+ List<String> listAvailableSeat = listAvailableSeat(trainId, carriagesNumber, requestParam.getSeatType(), departure, arrival);
|
|
|
+ int[][] actualSeats = new int[2][3];
|
|
|
+ for (int j = 1; j < 3; j++) {
|
|
|
+ for (int k = 1; k < 4; k++) {
|
|
|
+ // 当前默认按照复兴号商务座排序,后续这里需要按照简单工厂对车类型进行获取 y 轴
|
|
|
+ actualSeats[j - 1][k - 1] = listAvailableSeat.contains("0" + j + SeatNumberUtil.convert(0, k)) ? 0 : 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ int[][] actualSeatsTranscript = deepCopy(actualSeats);
|
|
|
+ List<int[][]> actualSelects = new ArrayList<>();
|
|
|
+ List<List<PurchaseTicketPassengerDetailDTO>> splitPassengerSeatDetails = ListUtil.split(passengerSeatDetails, 2);
|
|
|
+ for (List<PurchaseTicketPassengerDetailDTO> each : splitPassengerSeatDetails) {
|
|
|
+ int[][] select = SeatSelection.adjacent(each.size(), actualSeatsTranscript);
|
|
|
+ if (select != null) {
|
|
|
+ for (int[] ints : select) {
|
|
|
+ actualSeatsTranscript[ints[0] - 1][ints[1] - 1] = 1;
|
|
|
+ }
|
|
|
+ actualSelects.add(select);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (actualSelects.size() == splitPassengerSeatDetails.size()) {
|
|
|
+ int[][] actualSelect = null;
|
|
|
+ for (int j = 0; j < actualSelects.size(); j++) {
|
|
|
+ if (j == 0) {
|
|
|
+ actualSelect = mergeArrays(actualSelects.get(j), actualSelects.get(j + 1));
|
|
|
+ }
|
|
|
+ if (j != 0 && actualSelects.size() > 2) {
|
|
|
+ actualSelect = mergeArrays(actualSelect, actualSelects.get(j + 1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ carriagesNumberSeatsMap.put(carriagesNumber, actualSelect);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ int demotionStockNum = 0;
|
|
|
+ for (int[] actualSeat : actualSeats) {
|
|
|
+ for (int i1 : actualSeat) {
|
|
|
+ if (i1 == 0) {
|
|
|
+ demotionStockNum++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ demotionStockNumMap.putIfAbsent(carriagesNumber, demotionStockNum);
|
|
|
+ actualSeatsMap.putIfAbsent(carriagesNumber, actualSeats);
|
|
|
+ }
|
|
|
+ // 如果邻座算法无法匹配,尝试对用户进行降级分配:同车厢不邻座
|
|
|
+ if (CollUtil.isEmpty(carriagesNumberSeatsMap)) {
|
|
|
+ for (Map.Entry<String, Integer> entry : demotionStockNumMap.entrySet()) {
|
|
|
+ String carriagesNumberBack = entry.getKey();
|
|
|
+ int demotionStockNumBack = entry.getValue();
|
|
|
+ if (demotionStockNumBack > passengerSeatDetails.size()) {
|
|
|
+ int[][] seats = actualSeatsMap.get(carriagesNumberBack);
|
|
|
+ int[][] nonAdjacentSeats = SeatSelection.nonAdjacent(passengerSeatDetails.size(), seats);
|
|
|
+ if (Objects.equals(nonAdjacentSeats.length, passengerSeatDetails.size())) {
|
|
|
+ carriagesNumberSeatsMap.put(carriagesNumberBack, nonAdjacentSeats);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 如果同车厢也已无法匹配,则对用户座位再次降级:不同车厢不邻座
|
|
|
+ if (CollUtil.isEmpty(carriagesNumberSeatsMap)) {
|
|
|
+ int undistributedPassengerSize = passengerSeatDetails.size();
|
|
|
+ for (Map.Entry<String, Integer> entry : demotionStockNumMap.entrySet()) {
|
|
|
+ String carriagesNumberBack = entry.getKey();
|
|
|
+ int demotionStockNumBack = entry.getValue();
|
|
|
+ int[][] seats = actualSeatsMap.get(carriagesNumberBack);
|
|
|
+ int[][] nonAdjacentSeats = SeatSelection.nonAdjacent(Math.min(undistributedPassengerSize, demotionStockNumBack), seats);
|
|
|
+ undistributedPassengerSize = undistributedPassengerSize - demotionStockNumBack;
|
|
|
+ carriagesNumberSeatsMap.put(entry.getKey(), nonAdjacentSeats);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 乘车人员在单一车厢座位不满足,触发乘车人元分布在不同车厢
|
|
|
+ int count = (int) carriagesNumberSeatsMap.values().stream()
|
|
|
+ .flatMap(Arrays::stream)
|
|
|
+ .count();
|
|
|
+ if (CollUtil.isNotEmpty(carriagesNumberSeatsMap) && passengerSeatDetails.size() == count) {
|
|
|
+ int countNum = 0;
|
|
|
+ for (Map.Entry<String, int[][]> entry : carriagesNumberSeatsMap.entrySet()) {
|
|
|
+ List<String> selectSeats = new ArrayList<>();
|
|
|
+ for (int[] ints : entry.getValue()) {
|
|
|
+ selectSeats.add("0" + ints[0] + SeatNumberUtil.convert(0, ints[1]));
|
|
|
+ }
|
|
|
+ for (String selectSeat : selectSeats) {
|
|
|
+ TrainPurchaseTicketRespDTO result = new TrainPurchaseTicketRespDTO();
|
|
|
+ PurchaseTicketPassengerDetailDTO currentTicketPassenger = passengerSeatDetails.get(countNum++);
|
|
|
+ result.setSeatNumber(selectSeat);
|
|
|
+ result.setSeatType(currentTicketPassenger.getSeatType());
|
|
|
+ result.setCarriageNumber(entry.getKey());
|
|
|
+ result.setPassengerId(currentTicketPassenger.getPassengerId());
|
|
|
+ actualResult.add(result);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return actualResult;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public List<String> listAvailableSeat(String trainId, String carriageNumber, Integer seatType, String departure, String arrival) {
|
|
|
+ LambdaQueryWrapper<SeatDO> queryWrapper = Wrappers.lambdaQuery(SeatDO.class)
|
|
|
+ .eq(SeatDO::getTrainId, trainId)
|
|
|
+ .eq(SeatDO::getCarriageNumber, carriageNumber)
|
|
|
+ .eq(SeatDO::getSeatType, seatType)
|
|
|
+ .eq(SeatDO::getStartStation, departure)
|
|
|
+ .eq(SeatDO::getEndStation, arrival)
|
|
|
+ .eq(SeatDO::getSeatStatus, 0)
|
|
|
+ .select(SeatDO::getSeatNumber);
|
|
|
+ List<SeatDO> seatDOList = seatMapper.selectList(queryWrapper);
|
|
|
+ return seatDOList.stream().map(SeatDO::getSeatNumber).collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ public static int[][] mergeArrays(int[][] array1, int[][] array2) {
|
|
|
+ List<int[]> list = new ArrayList<>(Arrays.asList(array1));
|
|
|
+ list.addAll(Arrays.asList(array2));
|
|
|
+ return list.toArray(new int[0][]);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static int[][] deepCopy(int[][] originalArray) {
|
|
|
+ int[][] copy = new int[originalArray.length][originalArray[0].length];
|
|
|
+ for (int i = 0; i < originalArray.length; i++) {
|
|
|
+ System.arraycopy(originalArray[i], 0, copy[i], 0, originalArray[i].length);
|
|
|
+ }
|
|
|
+ return copy;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected TrainSeatBaseDTO buildTrainSeatBaseDTO(SelectSeatDTO requestParam) {
|
|
|
+ return TrainSeatBaseDTO.builder()
|
|
|
+ .trainId(requestParam.getRequestParam().getTrainId())
|
|
|
+ .departure(requestParam.getRequestParam().getDeparture())
|
|
|
+ .arrival(requestParam.getRequestParam().getArrival())
|
|
|
+ .chooseSeatList(requestParam.getRequestParam().getChooseSeats())
|
|
|
+ .passengerSeatDetails(requestParam.getPassengerSeatDetails())
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+
|
|
|
+}
|