|
@@ -0,0 +1,128 @@
|
|
|
+package com.sf.service.impl;
|
|
|
+
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+import com.alibaba.fastjson2.JSON;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import com.google.common.collect.Lists;
|
|
|
+import com.sf.dto.SeatClassDTO;
|
|
|
+import com.sf.dto.TicketListDTO;
|
|
|
+import com.sf.dto.req.TicketPageQueryReqDTO;
|
|
|
+import com.sf.dto.resp.TicketPageQueryRespDTO;
|
|
|
+import com.sf.entity.TicketDO;
|
|
|
+import com.sf.entity.TrainStationPriceDO;
|
|
|
+import com.sf.mapper.TicketMapper;
|
|
|
+import com.sf.service.TicketService;
|
|
|
+import com.sf.util.TimeStringComparator;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import org.opengoofy.index12306.framework.starter.cache.DistributedCache;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.data.redis.core.StringRedisTemplate;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.math.RoundingMode;
|
|
|
+import java.util.*;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+import static com.sf.constant.Index12306Constant.ADVANCE_TICKET_DAY;
|
|
|
+import static com.sf.constant.RedisKeyConstant.*;
|
|
|
+
|
|
|
+@RequiredArgsConstructor
|
|
|
+@Service
|
|
|
+public class TicketServiceImpl extends ServiceImpl<TicketMapper, TicketDO> implements TicketService {
|
|
|
+
|
|
|
+ private final DistributedCache distributedCache;
|
|
|
+
|
|
|
+ // http://localhost:9000/api/ticket-service/ticket/query?fromStation=BJP&toStation=HZH&departureDate=2024-05-15
|
|
|
+ @Override
|
|
|
+ public TicketPageQueryRespDTO pageListTicketQueryV1(TicketPageQueryReqDTO requestParam) {
|
|
|
+
|
|
|
+ StringRedisTemplate stringRedisTemplate = (StringRedisTemplate) distributedCache.getInstance();
|
|
|
+
|
|
|
+ // 先获取 index12306-ticket-service:region_train_station_mapping 中的数据
|
|
|
+ // 得到出发站和目的站的名字
|
|
|
+ List<Object> stationDetails = stringRedisTemplate.opsForHash()
|
|
|
+ .multiGet(REGION_TRAIN_STATION_MAPPING, Lists.newArrayList(requestParam.getFromStation(), requestParam.getToStation()));
|
|
|
+
|
|
|
+ // index12306-ticket-service:region_train_station:%s_%s
|
|
|
+ // 拼接出key index12306-ticket-service:region_train_station:北京_杭州
|
|
|
+ String buildRegionTrainStationHashKey = String.format(REGION_TRAIN_STATION, stationDetails.get(0), stationDetails.get(1));
|
|
|
+ // 从redis中 取出key存储的value值 对应一个map结果
|
|
|
+ Map<Object, Object> regionTrainStationAllMap = stringRedisTemplate.opsForHash().entries(buildRegionTrainStationHashKey);
|
|
|
+ // 遍历其中每个元素 转化为多个TicketListDTO对象组成的list
|
|
|
+ List<TicketListDTO> seatResults = regionTrainStationAllMap.values().stream().map(
|
|
|
+ each -> JSON.parseObject(each.toString(), TicketListDTO.class)).toList();
|
|
|
+ // 按照出发时间排序
|
|
|
+ seatResults = seatResults.stream().sorted(new TimeStringComparator()).toList();
|
|
|
+ for (TicketListDTO each : seatResults) {
|
|
|
+ // index12306-ticket-service:train_station_price:%s_%s_%s
|
|
|
+ // 拼接成key index12306-ticket-service:train_station_price:1_北京南_杭州东
|
|
|
+ String trainStationPriceStr = distributedCache.safeGet(
|
|
|
+ String.format(TRAIN_STATION_PRICE, each.getTrainId(), each.getDeparture(), each.getArrival()),
|
|
|
+ String.class,
|
|
|
+ null,
|
|
|
+ ADVANCE_TICKET_DAY,
|
|
|
+ TimeUnit.DAYS
|
|
|
+ );
|
|
|
+ // 返回结果是由 TrainStationPriceDO对象组成的list 所转化的json数据
|
|
|
+ List<TrainStationPriceDO> trainStationPriceDOList = JSON.parseArray(trainStationPriceStr, TrainStationPriceDO.class);
|
|
|
+ List<SeatClassDTO> seatClassList = new ArrayList<>();
|
|
|
+ // 遍历每一个TrainStationPriceDO
|
|
|
+ trainStationPriceDOList.forEach(item -> {
|
|
|
+ String seatType = String.valueOf(item.getSeatType());
|
|
|
+ // 拼接成key index12306-ticket-service:train_station_remaining_ticket:1_北京南_杭州东
|
|
|
+ String keySuffix = StrUtil.join("_", each.getTrainId(), item.getDeparture(), item.getArrival());
|
|
|
+ // 获取每个席别对应的票数
|
|
|
+ Object quantityObj = stringRedisTemplate.opsForHash().get(TRAIN_STATION_REMAINING_TICKET + keySuffix, seatType);
|
|
|
+ int quantity = Optional.ofNullable(quantityObj).map(Object::toString).map(Integer::parseInt).get();
|
|
|
+ seatClassList.add(new SeatClassDTO(
|
|
|
+ item.getSeatType(), quantity,
|
|
|
+ new BigDecimal(item.getPrice()).divide(new BigDecimal("100"), 1, RoundingMode.HALF_UP),
|
|
|
+ false));
|
|
|
+ });
|
|
|
+ each.setSeatClassList(seatClassList);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 组装数据
|
|
|
+ return TicketPageQueryRespDTO.builder()
|
|
|
+ .trainList(seatResults)
|
|
|
+ .departureStationList(buildDepartureStationList(seatResults))
|
|
|
+ .arrivalStationList(buildArrivalStationList(seatResults))
|
|
|
+ .trainBrandList(buildTrainBrandList(seatResults))
|
|
|
+ .seatClassTypeList(buildSeatClassList(seatResults))
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private List<String> buildDepartureStationList(List<TicketListDTO> seatResults) {
|
|
|
+ return seatResults.stream().map(TicketListDTO::getDeparture).distinct().collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<String> buildArrivalStationList(List<TicketListDTO> seatResults) {
|
|
|
+ return seatResults.stream().map(TicketListDTO::getArrival).distinct().collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<Integer> buildSeatClassList(List<TicketListDTO> seatResults) {
|
|
|
+ Set<Integer> resultSeatClassList = new HashSet<>();
|
|
|
+ for (TicketListDTO each : seatResults) {
|
|
|
+ for (SeatClassDTO item : each.getSeatClassList()) {
|
|
|
+ resultSeatClassList.add(item.getType());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return resultSeatClassList.stream().toList();
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<Integer> buildTrainBrandList(List<TicketListDTO> seatResults) {
|
|
|
+ Set<Integer> trainBrandSet = new HashSet<>();
|
|
|
+ for (TicketListDTO each : seatResults) {
|
|
|
+ if (StrUtil.isNotBlank(each.getTrainBrand())) {
|
|
|
+ trainBrandSet.addAll(StrUtil.split(each.getTrainBrand(), ",").stream().map(Integer::parseInt).toList());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return trainBrandSet.stream().toList();
|
|
|
+ }
|
|
|
+}
|