| | |
| | | |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import com.alibaba.fastjson2.JSONArray; |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.xiaopeng.api.dto.operation.PointAddDto; |
| | | import com.xiaopeng.api.dto.operation.PointMapQuery; |
| | | import com.xiaopeng.api.dto.operation.PointUpdateDto; |
| | | import com.xiaopeng.api.dto.order.HistoryEndPointsDto; |
| | | import com.xiaopeng.api.dto.order.HistoryStartPointsDto; |
| | | import com.xiaopeng.api.model.operation.XpElectronicFenceArea; |
| | | import com.xiaopeng.api.model.operation.XpPoint; |
| | | import com.xiaopeng.api.model.operation.XpRegion; |
| | | import com.xiaopeng.api.model.operation.XpSystemSetting; |
| | | import com.xiaopeng.api.feignClient.account.XpAppUserCollectAddrClient; |
| | | import com.xiaopeng.api.model.operation.*; |
| | | import com.xiaopeng.api.query.operation.FenceAndStationVo; |
| | | import com.xiaopeng.api.query.operation.PointListQuery; |
| | | import com.xiaopeng.api.query.operation.PointPageListListQuery; |
| | | import com.xiaopeng.api.query.operation.XpStationQuery; |
| | | import com.xiaopeng.api.vo.account.LoginUser; |
| | | import com.xiaopeng.api.vo.order.XpHistoryEndPointsVo; |
| | | import com.xiaopeng.api.vo.order.XpHistoryStartPointsVo; |
| | | import com.xiaopeng.api.vo.order.XpPointsListVo; |
| | | import com.xiaopeng.api.vo.order.XpPointsSearchVo; |
| | | import com.xiaopeng.common.core.constant.CacheConstants; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.common.core.exception.ServiceException; |
| | | import com.xiaopeng.common.core.utils.StringUtils; |
| | | import com.xiaopeng.common.core.web.page.PageInfo; |
| | | import com.xiaopeng.common.redis.service.RedisService; |
| | | import com.xiaopeng.common.security.service.TokenService; |
| | | import com.xiaopeng.evi.oss.service.AliOssService; |
| | | import com.xiaopeng.map.constant.I18nKeyConstant; |
| | | import com.xiaopeng.map.dto.NearestDto; |
| | | import com.xiaopeng.map.dto.StationDetailQueryDto; |
| | | import com.xiaopeng.map.mapper.XpElectronicFenceMapper; |
| | | import com.xiaopeng.map.mapper.XpPointMapper; |
| | | import com.xiaopeng.map.mapper.XpRegionMapper; |
| | | import com.xiaopeng.map.mapper.XpSystemSettingMapper; |
| | | import com.xiaopeng.map.query.PointByNameQuery; |
| | | import com.xiaopeng.map.service.XpElectronicFenceAreaService; |
| | | import com.xiaopeng.map.service.XpElectronicFenceService; |
| | | import com.xiaopeng.map.service.XpPointService; |
| | | import com.xiaopeng.map.utils.TimeRangeChecker; |
| | | import com.xiaopeng.map.service.XpStationService; |
| | | import com.xiaopeng.map.utils.GDMapGeocodingUtil; |
| | | import com.xiaopeng.map.utils.holiday.HolidayUtil; |
| | | import com.xiaopeng.map.utils.map.map.FastDistanceCalculator; |
| | | import com.xiaopeng.map.vo.NearestNextVo; |
| | | import com.xiaopeng.map.vo.NearestVo; |
| | | import com.xiaopeng.map.vo.OrderDistanceVo; |
| | | import com.xiaopeng.map.vo.*; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import net.sourceforge.pinyin4j.PinyinHelper; |
| | | import org.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType; |
| | |
| | | import org.redisson.api.RedissonClient; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.io.UnsupportedEncodingException; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.time.LocalDateTime; |
| | | import java.time.LocalTime; |
| | | import java.util.*; |
| | | import java.util.concurrent.TimeUnit; |
| | |
| | | public class XpPointServiceImpl extends ServiceImpl<XpPointMapper, XpPoint> implements XpPointService { |
| | | |
| | | private static final String POINT_BIZ_TYPE = "PUD"; |
| | | private static final int MAX_POINTS_PER_FENCE = 15; |
| | | private static final int SERIAL_WIDTH = 6; |
| | | private static final int LOCK_WAIT_SECONDS = 5; |
| | | private static final int LOCK_LEASE_SECONDS = 30; |
| | |
| | | private XpElectronicFenceAreaService xpElectronicFenceAreaService; |
| | | |
| | | @Resource |
| | | private XpElectronicFenceMapper xpElectronicFenceMapper; |
| | | |
| | | @Resource |
| | | @Lazy |
| | | private XpStationService xpStationService; |
| | | |
| | | @Resource |
| | | private XpAppUserCollectAddrClient xpAppUserCollectAddrClient; |
| | | |
| | | @Resource |
| | | private XpRegionMapper xpRegionMapper; |
| | | |
| | | @Autowired |
| | |
| | | |
| | | @Autowired |
| | | private AliOssService aliOssService; |
| | | @Lazy |
| | | @Autowired |
| | | private BootCommonService bootCommonService; |
| | | @Lazy |
| | | @Resource |
| | | private XpElectronicFenceService xpElectronicFenceService; |
| | | @Resource |
| | | private GDMapGeocodingUtil gdMapGeocodingUtil; |
| | | |
| | | |
| | | |
| | | @Override |
| | | public void validateAdd(PointAddDto dto) { |
| | | |
| | | XpElectronicFenceArea electronicFenceArea = xpElectronicFenceAreaService.getById(dto.getElectronicAreaId()); |
| | | if(electronicFenceArea == null){ |
| | | throw new ServiceException("所属运营区id错误"); |
| | | } |
| | | XpElectronicFence electronicFence = xpElectronicFenceService.getById(electronicFenceArea.getElectronicId()); |
| | | //场站的自定义时间必须在 围栏运营时间内 |
| | | if(dto.getOperationType() != null && dto.getOperationType() == 2){ |
| | | // ["06:00-12:00", "13:00-21:00"] |
| | | if(StrUtil.isBlank(dto.getEffectiveTimePeriod())){ |
| | | throw new ServiceException("自定义时间段不能为空"); |
| | | } |
| | | boolean stationTimeFenceOperationStrategy = bootCommonService.getStationTime_fenceOperationStrategy(dto.getEffectivePeriod(), dto.getEffectiveTimePeriod(), electronicFence.getOperationStrategy()); |
| | | if(!stationTimeFenceOperationStrategy){ |
| | | throw new ServiceException("自定义时间必须在围栏运营时间内"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void validateUpdate(PointUpdateDto dto) { |
| | | |
| | | XpElectronicFenceArea electronicFenceArea = xpElectronicFenceAreaService.getById(dto.getElectronicAreaId()); |
| | | if(electronicFenceArea == null){ |
| | | throw new ServiceException("所属运营区id错误"); |
| | | } |
| | | XpElectronicFence electronicFence = xpElectronicFenceService.getById(electronicFenceArea.getElectronicId()); |
| | | //场站的自定义时间必须在 围栏运营时间内 |
| | | if(dto.getOperationType() != null && dto.getOperationType() == 2){ |
| | | // ["06:00-12:00", "13:00-21:00"] |
| | | if(StrUtil.isBlank(dto.getEffectiveTimePeriod())){ |
| | | throw new ServiceException("自定义时间段不能为空"); |
| | | } |
| | | boolean stationTimeFenceOperationStrategy = bootCommonService.getStationTime_fenceOperationStrategy(dto.getEffectivePeriod(), dto.getEffectiveTimePeriod(), electronicFence.getOperationStrategy()); |
| | | if(!stationTimeFenceOperationStrategy){ |
| | | throw new ServiceException("自定义时间必须在围栏运营时间内"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | @Transactional |
| | |
| | | if (pointAddDto.getElectronicAreaId() == null) { |
| | | throw new ServiceException("运营区域ID不能为空", I18nKeyConstant.POINT_ELECTRONIC_AREA_ID_EMPTY); |
| | | } |
| | | validatePointNameUnique(pointAddDto.getPointName(), null); |
| | | |
| | | String cityPrefix = buildCityPrefix(pointAddDto.getElectronicAreaId()); |
| | | String pointNumber = getPointNumber(pointAddDto.getElectronicAreaId()); |
| | | |
| | | XpPoint xpPoint = new XpPoint(); |
| | | BeanUtils.copyProperties(pointAddDto, xpPoint); |
| | | xpPoint.setFpElectronicAreaId(pointAddDto.getElectronicAreaId()); |
| | | xpPoint.setPointNumber(pointNumber); |
| | | xpPoint.setStationSortNo(0); |
| | | Map<String, String> geocode = gdMapGeocodingUtil.geocode(xpPoint.getPointLon().toString(), xpPoint.getPointLat().toString()); |
| | | if(null == geocode){ |
| | | throw new ServiceException("点位定位解析失败"); |
| | | } |
| | | xpPoint.setCityCode(geocode.get("cityCode")); |
| | | xpPoint.setCityName(geocode.get("address")); |
| | | this.save(xpPoint); |
| | | |
| | | //场站点位排序 后添加的点位 排第一个。 |
| | | this.setStationSortNo(xpPoint.getStationId(),0); |
| | | |
| | | return xpPoint; |
| | | } |
| | | |
| | | /** |
| | | * 场站 点位排序 |
| | | * @param stationId |
| | | * @param stationSortNo |
| | | */ |
| | | @Transactional |
| | | @Override |
| | | public void setStationSortNo(Long stationId, Integer stationSortNo){ |
| | | if(null != stationId && stationSortNo != null && stationSortNo ==0){ |
| | | // 查询该场站下的所有点位,按 stationSortNo 排序 |
| | | List<XpPoint> points = this.lambdaQuery() |
| | | .eq(XpPoint::getStationId, stationId) |
| | | .orderByAsc(XpPoint::getStationSortNo) |
| | | .list(); |
| | | |
| | | if (CollUtil.isNotEmpty(points)) { |
| | | int newSortNo = 1; |
| | | for (XpPoint point : points) { |
| | | point.setStationSortNo(newSortNo++); |
| | | } |
| | | this.updateBatchById(points); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public String getPointNumber(Long electronicAreaId) { |
| | | String cityPrefix = buildCityPrefix(electronicAreaId); |
| | | String lockKey = buildPointNumberLockKey(cityPrefix, POINT_BIZ_TYPE); |
| | | |
| | | RLock lock = redissonClient.getLock(lockKey); |
| | | boolean locked = false; |
| | | String pointNumber; |
| | |
| | | if (!locked) { |
| | | throw new ServiceException("点位编号生成繁忙,请稍后重试", I18nKeyConstant.POINT_NUMBER_LOCK_FAIL); |
| | | } |
| | | // validateFencePointCountLimit(electronicAreaId); |
| | | String maxPointNumber = this.baseMapper.selectMaxPointNumber(cityPrefix, POINT_BIZ_TYPE); |
| | | int nextSerial = 1; |
| | | if (StringUtils.hasText(maxPointNumber)) { |
| | |
| | | log.warn("释放点位编号生成锁失败 lockKey={}", lockKey, e); |
| | | } |
| | | } |
| | | return pointNumber; |
| | | } |
| | | |
| | | XpPoint xpPoint = new XpPoint(); |
| | | BeanUtils.copyProperties(pointAddDto, xpPoint); |
| | | xpPoint.setFpElectronicAreaId(pointAddDto.getElectronicAreaId()); |
| | | xpPoint.setPointNumber(pointNumber); |
| | | this.save(xpPoint); |
| | | return xpPoint; |
| | | @Override |
| | | public void validatePointNameUnique(String pointName, Long excludeId) { |
| | | if (!StringUtils.hasText(pointName)) { |
| | | return; |
| | | } |
| | | long count = this.lambdaQuery() |
| | | .eq(XpPoint::getPointName, pointName) |
| | | .ne(excludeId != null, XpPoint::getId, excludeId) |
| | | .count(); |
| | | if (count > 0) { |
| | | throw new ServiceException("点位名称已存在", I18nKeyConstant.POINT_NAME_EXISTS); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | |
| | | orderDistance = xpSystemSetting.getNearPointDistance(); |
| | | } |
| | | // 获取所有的站点信息 |
| | | List<XpPoint> list = list(new LambdaQueryWrapper<XpPoint>().eq(XpPoint::getOperationStatus, 2).eq(XpPoint::getPointType,1).isNotNull(XpPoint::getElectronicAreaId) |
| | | // 添加距离计算字段 |
| | | .apply("ST_Distance_Sphere(point({0}, {1}), point(point_lon, point_lat)) <= {2}", |
| | | dto.getLongitude(), dto.getLatitude(), orderDistance)); |
| | | PointPageListListQuery query = new PointPageListListQuery(); |
| | | query.setOperationStatus(2); |
| | | query.setPointType(1); |
| | | query.setElectronicAreaIdNotNull(true); |
| | | query.setLongitude(dto.getLongitude()); |
| | | query.setLatitude(dto.getLatitude()); |
| | | query.setOrderDistance(orderDistance); |
| | | List<XpPoint> list = baseMapper.pageList(query); |
| | | // 添加距离计算字段 |
| | | // List<XpPoint> list = list(new LambdaQueryWrapper<XpPoint>().eq(XpPoint::getOperationStatus, 2).eq(XpPoint::getPointType,1).isNotNull(XpPoint::getElectronicAreaId) |
| | | // .apply("ST_Distance_Sphere(point({0}, {1}), point(point_lon, point_lat)) <= {2}", |
| | | // dto.getLongitude(), dto.getLatitude(), orderDistance)); |
| | | NearestVo nearestVo = new NearestVo(); |
| | | List<NearestNextVo> nearestNextVos = new ArrayList<>(); |
| | | LocalTime now = LocalTime.now(); |
| | |
| | | |
| | | for (XpPoint xpPoint : list) { |
| | | // 判断是否在时间段内 |
| | | boolean intime = getInTime(now, xpPoint.getEffectivePeriod(), xpPoint.getEffectiveTimePeriod()); |
| | | boolean intime = bootCommonService.getPointOperation(now, xpPoint); |
| | | if (!intime) { |
| | | continue; |
| | | } |
| | | Integer parkingTimeLimit = xpPoint.getParkingTimeLimit(); |
| | | //如果点位位于场站内,则使用场站的限停时间 |
| | | if(null != xpPoint.getStationId()){ |
| | | XpStation xpStation = xpStationService.getById(xpPoint.getStationId()); |
| | | parkingTimeLimit = xpStation.getParkingTimeLimit(); |
| | | } |
| | | // 计算距离 小于的站点 |
| | | double v = FastDistanceCalculator.distanceFast(dto.getLatitude(), dto.getLongitude(), xpPoint.getPointLat(), xpPoint.getPointLon()); |
| | |
| | | nearestNextVo.setLongitude(xpPoint.getPointLon()); |
| | | nearestNextVo.setDistance(v); |
| | | nearestNextVo.setAddress(xpPoint.getPointAddress()); |
| | | nearestNextVo.setTime(xpPoint.getParkingTimeLimit()); |
| | | nearestNextVo.setTime(parkingTimeLimit); |
| | | nearestNextVo.setElectronicAreaId(xpPoint.getElectronicAreaId()); |
| | | |
| | | |
| | |
| | | return getPinyinInitials(prefix).toUpperCase(); |
| | | } |
| | | |
| | | private void validateFencePointCountLimit(Long electronicAreaId) { |
| | | if (electronicAreaId == null || electronicAreaId == 0L) { |
| | | return; |
| | | } |
| | | XpElectronicFenceArea currentArea = xpElectronicFenceAreaService.getById(electronicAreaId); |
| | | if (currentArea == null) { |
| | | throw new ServiceException("运营区域不存在", I18nKeyConstant.POINT_ELECTRONIC_AREA_NOT_EXIST); |
| | | } |
| | | Long fenceId = currentArea.getElectronicId(); |
| | | if (fenceId == null) { |
| | | throw new ServiceException("运营区域不存在", I18nKeyConstant.POINT_ELECTRONIC_AREA_NOT_EXIST); |
| | | } |
| | | List<Long> areaIds = xpElectronicFenceAreaService.lambdaQuery() |
| | | .eq(XpElectronicFenceArea::getElectronicId, fenceId) |
| | | .list() |
| | | .stream() |
| | | .map(XpElectronicFenceArea::getId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList()); |
| | | if (areaIds.isEmpty()) { |
| | | return; |
| | | } |
| | | long pointCount = this.lambdaQuery() |
| | | .in(XpPoint::getElectronicAreaId, areaIds) |
| | | .count(); |
| | | if (pointCount >= MAX_POINTS_PER_FENCE) { |
| | | throw new ServiceException("当前围栏最多只能添加15个点位", I18nKeyConstant.POINT_FENCE_POINT_LIMIT_EXCEEDED); |
| | | } |
| | | } |
| | | |
| | | private static String buildPointNumberLockKey(String cityPrefix, String bizType) { |
| | | return "xp:point:number:" + cityPrefix + ":" + bizType; |
| | | } |
| | |
| | | |
| | | |
| | | |
| | | public boolean getInTime(LocalTime now, Integer effectivePeriod, String effectiveTimePeriod){ |
| | | // 2非工作日 0工作日 1节假日 3调休日 |
| | | Integer holidayToday = holidayUtil.isHolidayToday(); |
| | | if(effectivePeriod==1){ |
| | | boolean inTime = TimeRangeChecker.isCurrentTimeInRanges(now,effectiveTimePeriod); |
| | | if (!inTime) { |
| | | return false; |
| | | } |
| | | }else if(effectivePeriod==2){ |
| | | if(holidayToday!=0){ |
| | | return false; |
| | | } |
| | | boolean inTime = TimeRangeChecker.isCurrentTimeInRanges(now,effectiveTimePeriod); |
| | | if (!inTime) { |
| | | return false; |
| | | } |
| | | }else if(effectivePeriod==3){ |
| | | if(holidayToday!=2){ |
| | | return false; |
| | | } |
| | | boolean inTime = TimeRangeChecker.isCurrentTimeInRanges(now,effectiveTimePeriod); |
| | | if (!inTime) { |
| | | return false; |
| | | } |
| | | }else if(effectivePeriod==4){ |
| | | if(holidayToday!=1){ |
| | | return false; |
| | | } |
| | | boolean inTime = TimeRangeChecker.isCurrentTimeInRanges(now,effectiveTimePeriod); |
| | | if (!inTime) { |
| | | return false; |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public List<XpPoint> pointMap(PointMapQuery query) { |
| | | List<XpPoint> list = this.baseMapper.pointMap(query); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public List<FenceAndStationVo> findListFenceAndStation(String cityCode){ |
| | | return baseMapper.findListFenceAndStation(cityCode); |
| | | } |
| | | |
| | | @Override |
| | | public List<XpPoint> findList(PointListQuery query) { |
| | | return this.baseMapper.listAll(query); |
| | | } |
| | | |
| | | @Override |
| | | public List<XpPoint> listAll(PointListQuery query) { |
| | | List<XpPoint> list = this.baseMapper.listAll(query); |
| | | for (XpPoint xpPoint : list) { |
| | | String pointPictures = xpPoint.getPointPictures(); |
| | | log.info("pointPictures:{}", pointPictures); |
| | | if (StringUtils.isNotBlank(pointPictures)) { |
| | | log.info("pointPictures进入处理:{}", pointPictures); |
| | | |
| | | JSONArray jsonArray = JSONArray.parseArray(pointPictures); |
| | | for (int i = 0; i < jsonArray.size(); i++) { |
| | | JSONObject jsonObject = jsonArray.getJSONObject(i); |
| | | if (Objects.isNull(jsonObject)) { |
| | | continue; |
| | | } |
| | | String downloadUrl =""; |
| | | try { |
| | | downloadUrl = aliOssService.getDownloadUrl(jsonObject.getString("id"), jsonObject.getString("name"), true); |
| | | log.info("downloadUrl:{}", downloadUrl); |
| | | } catch (UnsupportedEncodingException e) { |
| | | log.error("获取图片下载地址失败", e); |
| | | } |
| | | jsonObject.put("url", downloadUrl); |
| | | } |
| | | log.info("jsonArray:{}", jsonArray); |
| | | xpPoint.setPointPictures(JSONArray.toJSONString(jsonArray)); |
| | | log.info("xpPoint:{}", xpPoint.getPointPictures()); |
| | | } |
| | | |
| | | } |
| | | |
| | | return list; |
| | | return this.baseMapper.listAll(query); |
| | | } |
| | | |
| | | @Override |
| | |
| | | List<XpPoint> xpPoints = this.baseMapper.selectBatchIds(historyStartPointsDto.getPointIds()); |
| | | ArrayList<XpHistoryStartPointsVo> resultList = new ArrayList<>(10); |
| | | for (XpPoint xpPoint : xpPoints) { |
| | | Integer parkingTimeLimit = xpPoint.getParkingTimeLimit(); |
| | | //如果点位位于场站内,则使用场站的限停时间 |
| | | if(null != xpPoint.getStationId()){ |
| | | XpStation xpStation = xpStationService.getById(xpPoint.getStationId()); |
| | | parkingTimeLimit = xpStation.getParkingTimeLimit(); |
| | | } |
| | | XpHistoryStartPointsVo xpHistoryStartPointsVo = new XpHistoryStartPointsVo(); |
| | | xpHistoryStartPointsVo.setName(xpPoint.getPointName()); |
| | | xpHistoryStartPointsVo.setAddress(xpPoint.getPointAddress()); |
| | |
| | | xpHistoryStartPointsVo.setId(xpPoint.getId()); |
| | | double distanced = FastDistanceCalculator.distanceFastKm(historyStartPointsDto.getLat(), historyStartPointsDto.getLon(), xpPoint.getPointLat(), xpPoint.getPointLon()); |
| | | xpHistoryStartPointsVo.setDistance(distanced); |
| | | xpHistoryStartPointsVo.setTime(xpPoint.getParkingTimeLimit()); |
| | | xpHistoryStartPointsVo.setTime(parkingTimeLimit); |
| | | xpHistoryStartPointsVo.setElectronicAreaId(xpPoint.getElectronicAreaId()); |
| | | resultList.add(xpHistoryStartPointsVo); |
| | | |
| | |
| | | List<XpPoint> xpPoints = this.baseMapper.selectBatchIds(historyEndPointsDto.getPointIds()); |
| | | ArrayList<XpHistoryEndPointsVo> resultList = new ArrayList<>(10); |
| | | for (XpPoint xpPoint : xpPoints) { |
| | | Integer parkingTimeLimit = xpPoint.getParkingTimeLimit(); |
| | | //如果点位位于场站内,则使用场站的限停时间 |
| | | if(null != xpPoint.getStationId()){ |
| | | XpStation xpStation = xpStationService.getById(xpPoint.getStationId()); |
| | | parkingTimeLimit = xpStation.getParkingTimeLimit(); |
| | | } |
| | | XpHistoryEndPointsVo xpHistoryEndPointsVo = new XpHistoryEndPointsVo(); |
| | | xpHistoryEndPointsVo.setName(xpPoint.getPointName()); |
| | | xpHistoryEndPointsVo.setAddress(xpPoint.getPointAddress()); |
| | |
| | | xpHistoryEndPointsVo.setId(xpPoint.getId()); |
| | | double distanced = FastDistanceCalculator.distanceFastKm(historyEndPointsDto.getLat(), historyEndPointsDto.getLon(), xpPoint.getPointLat(), xpPoint.getPointLon()); |
| | | xpHistoryEndPointsVo.setDistance(distanced); |
| | | xpHistoryEndPointsVo.setTime(xpPoint.getParkingTimeLimit()); |
| | | xpHistoryEndPointsVo.setTime(parkingTimeLimit); |
| | | xpHistoryEndPointsVo.setElectronicAreaId(xpPoint.getElectronicAreaId()); |
| | | resultList.add(xpHistoryEndPointsVo); |
| | | } |
| | | |
| | | return resultList; |
| | | } |
| | | |
| | | @Override |
| | | public StationDetailVo queryStationDetail(StationDetailQueryDto dto, String uid) { |
| | | StationDetailVo stationDetailVo = new StationDetailVo(); |
| | | if (dto == null || dto.getStationId() == null) { |
| | | return stationDetailVo; |
| | | } |
| | | |
| | | XpStationQuery query = new XpStationQuery(); |
| | | query.setId(dto.getStationId()); |
| | | query.setStatus(2); |
| | | List<XpStation> list = xpStationService.findList(query); |
| | | if(CollUtil.isEmpty(list)){ |
| | | return stationDetailVo; |
| | | } |
| | | XpStation station = list.get(0); |
| | | LocalTime localTime = LocalTime.now(); |
| | | boolean stationOperation = bootCommonService.getStationOperation(localTime, station.getOperationType(), station.getEffectivePeriod(), station.getEffectiveTimePeriod(), station.getFenceOperationStrategy()); |
| | | if(!stationOperation){ |
| | | return stationDetailVo; |
| | | } |
| | | |
| | | // 仅查询场站下运营点位,最终结果还会叠加时段/围栏有效性过滤。 |
| | | PointPageListListQuery queryPoint = new PointPageListListQuery(); |
| | | queryPoint.setStationId( station.getId()); |
| | | queryPoint.setPointType(1); |
| | | queryPoint.setOperationStatus(2); |
| | | List<XpPoint> stationPoints = this.baseMapper.pageList(queryPoint); |
| | | if (CollUtil.isEmpty(stationPoints)) { |
| | | return stationDetailVo; |
| | | } |
| | | |
| | | // Map<Long, XpElectronicFenceArea> areaMap = buildAreaMap(stationPoints.stream() |
| | | // .map(XpPoint::getElectronicAreaId) |
| | | // .filter(Objects::nonNull) |
| | | // .distinct() |
| | | // .collect(Collectors.toList())); |
| | | // Map<Long, XpElectronicFence> fenceMap = buildFenceMap(areaMap.values().stream() |
| | | // .map(XpElectronicFenceArea::getElectronicId) |
| | | // .filter(Objects::nonNull) |
| | | // .distinct() |
| | | // .collect(Collectors.toList())); |
| | | |
| | | Set<Long> collectStationIds = getCollectStationIds(uid); |
| | | Set<Long> collectPointIds = getCollectPointIds(uid); |
| | | |
| | | // 默认点位优先置顶,其余点位再按场站内排序和距离排序。 |
| | | List<StationPointItemVo> points = stationPoints.stream() |
| | | .filter(point -> isPointAvailable(localTime, point)) |
| | | .map(point -> toStationPointItem(point, station.getDefaultPointId(), dto.getLatitude(), dto.getLongitude(), collectPointIds)) |
| | | .sorted(buildStationPointComparator()) |
| | | .collect(Collectors.toList()); |
| | | if (points.isEmpty()) { |
| | | return new StationDetailVo(); |
| | | } |
| | | |
| | | stationDetailVo.setStationId(station.getId()); |
| | | stationDetailVo.setStationName(station.getStationName()); |
| | | stationDetailVo.setDefaultPointId(station.getDefaultPointId()); |
| | | stationDetailVo.setCenterLon(station.getCenterLon()); |
| | | stationDetailVo.setCenterLat(station.getCenterLat()); |
| | | if (collectStationIds.contains(station.getId())) { |
| | | stationDetailVo.setIsCollect(1); |
| | | } |
| | | stationDetailVo.setPoints(points); |
| | | return stationDetailVo; |
| | | } |
| | | |
| | | @Override |
| | |
| | | if (list.isEmpty()) { |
| | | return searchVo; |
| | | } |
| | | |
| | | LocalTime now = LocalTime.now(); |
| | | for (XpPointsListVo xpPointsListVo : list) { |
| | | boolean inTime = getInTime(now, xpPointsListVo.getEffectivePeriod(), xpPointsListVo.getEffectiveTimePeriod()); |
| | | |
| | | boolean inTime = bootCommonService.getPointOperation_vo(now, xpPointsListVo); |
| | | if (!inTime) { |
| | | // 不在运营段 |
| | | xpPointsListVo.setState(0); |
| | |
| | | return orderDistanceVo; |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public List<XpPoint> pageList(PageInfo<XpPoint> pageInfo, PointPageListListQuery query) { |
| | | List<XpPoint> list = null; |
| | | if (pageInfo == null) { |
| | | list = this.baseMapper.pageList(query); |
| | | }else{ |
| | | list = this.baseMapper.pageList(pageInfo,query); |
| | | } |
| | | |
| | | if(CollUtil.isNotEmpty(list)){ |
| | | /** |
| | | * hover在状态标签时,展示信息 |
| | | * - 已停用:展示停用原因;比如“因所属场站 [白云机场] 已关闭,该点位同步失效” |
| | | * - 测试不通过:展示不通过原因 |
| | | * - 待生效:展示生效时间 |
| | | */ |
| | | for (XpPoint point : list) { |
| | | String hoverStatusDes = ""; |
| | | if(point.getVerificationStatus()==4){//验证状态 1=待验证、2=验证中、3=已验证 4验证失败 5待生效 |
| | | hoverStatusDes = (point.getRemarks() == null ?"":point.getRemarks()); |
| | | } |
| | | if(point.getOperationStatus() == 3){//运营状态 1=待启用 2=启用中 3=已停用 4=已失效 |
| | | hoverStatusDes = (point.getRemarks()==null?"":point.getRemarks()); |
| | | } |
| | | if(point.getVerificationStatus()==5 && point.getOperationStatus() == 1){ |
| | | if(point.getOperationType() == 1){//运营类型 1=与运营区同步 2=自定义时间段 |
| | | ; |
| | | //[{"name": "策略组1", "time": ["06:00-12:00", "13:00-22:59"], "type": 0, "typeStr": []}, |
| | | // {"name": "策略组2", "time": ["05:00-12:00", "12:00-16:00"], "type": 2, "typeStr": ""}, |
| | | // {"name": "策略组3", "time": ["09:00-21:59"], "type": 1, "typeStr": ""}] |
| | | JSONArray electronicOperationStrategys = JSONArray.parseArray(point.getFenceOperationStrategy()); |
| | | if(!electronicOperationStrategys.isEmpty()){ |
| | | StringJoiner joiner = new StringJoiner(";"); |
| | | for (Object electronicOperationStrategy : electronicOperationStrategys) { |
| | | JSONObject obj = (JSONObject) electronicOperationStrategy; |
| | | |
| | | List<String> times = obj.getList("time",String.class); |
| | | joiner.add((String.join(";", times))); |
| | | } |
| | | hoverStatusDes = joiner.toString() ; |
| | | } |
| | | }else{ |
| | | if(StrUtil.isNotBlank(point.getEffectiveTimePeriod()) ){ |
| | | List<String> effectiveTimePeriods = JSONArray.parseArray(point.getEffectiveTimePeriod(), String.class); |
| | | hoverStatusDes = String.join(";", effectiveTimePeriods); |
| | | } |
| | | } |
| | | } |
| | | point.setHoverStatusDes(hoverStatusDes); |
| | | } |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | @Override |
| | | public PageInfo<XpPoint> pageList(PointPageListListQuery query) { |
| | | if(CollUtil.isNotEmpty(query.getFenceAndStationList())){ |
| | | List<String> fenceAreaIds = new ArrayList<>(); |
| | | List<String> stationIds = new ArrayList<>(); |
| | | for (FenceAndStationVo bean : query.getFenceAndStationList()) { |
| | | if(bean.getType()==1){ |
| | | fenceAreaIds.addAll(StrUtil.split(bean.getIds(),",") ); |
| | | }else{ |
| | | stationIds.addAll(StrUtil.split(bean.getIds(),",") ); |
| | | } |
| | | } |
| | | query.setFenceAreaIds(fenceAreaIds); |
| | | query.setStationIds(stationIds); |
| | | } |
| | | |
| | | PageInfo<XpPoint> pageInfo = new PageInfo<>(query.getPageNum(),query.getPageSize()); |
| | | if (StringUtils.isNotBlank(query.getPointLonAndLat())) { |
| | | List<String> lonAndLat = query.getPointLonAndLat().trim().contains(",") ? Arrays.asList(query.getPointLonAndLat().split(",")) : |
| | |
| | | } |
| | | } |
| | | List<XpPoint> list = this.baseMapper.pageList(pageInfo,query); |
| | | list.forEach(s->s.setStrId(s.getId().toString())); |
| | | pageInfo.setRecords(list); |
| | | return pageInfo; |
| | | } |
| | | |
| | | private Map<Long, XpElectronicFenceArea> buildAreaMap(List<Long> areaIds) { |
| | | if (CollUtil.isEmpty(areaIds)) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | return xpElectronicFenceAreaService.listByIds(areaIds).stream() |
| | | .collect(Collectors.toMap(XpElectronicFenceArea::getId, area -> area, (a, b) -> a)); |
| | | } |
| | | |
| | | /** |
| | | * 提前拉取电子围栏,避免逐点位重复查库。 |
| | | */ |
| | | private Map<Long, XpElectronicFence> buildFenceMap(List<Long> fenceIds) { |
| | | if (CollUtil.isEmpty(fenceIds)) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | return xpElectronicFenceMapper.selectBatchIds(fenceIds).stream() |
| | | .collect(Collectors.toMap(XpElectronicFence::getId, fence -> fence, (a, b) -> a)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 子点位是否可展示与场站卡片中。 |
| | | * 规则与现有点位有效性保持一致,避免地图页和其他点位查询出现口径偏差。 |
| | | */ |
| | | private boolean isPointAvailable(LocalTime localTime, XpPoint point) { |
| | | |
| | | return bootCommonService.getPointOperation(localTime, point); |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 组装单个场站子点位返回对象,并在有默认点位配置时打标。 |
| | | */ |
| | | private StationPointItemVo toStationPointItem(XpPoint point, Long defaultPointId, Double latitude, Double longitude, Set<Long> collectPointIds) { |
| | | StationPointItemVo itemVo = new StationPointItemVo(); |
| | | itemVo.setPointId(point.getId()); |
| | | itemVo.setPointName(point.getPointName()); |
| | | itemVo.setPointAddress(point.getPointAddress()); |
| | | itemVo.setLatitude(point.getPointLat()); |
| | | itemVo.setLongitude(point.getPointLon()); |
| | | itemVo.setStationSortNo(point.getStationSortNo()); |
| | | if (collectPointIds.contains(point.getId())) { |
| | | itemVo.setIsCollect(1); |
| | | } |
| | | if (defaultPointId != null && defaultPointId.equals(point.getId())) { |
| | | itemVo.setIsDefault(1); |
| | | } |
| | | if (latitude != null && longitude != null && point.getPointLat() != null && point.getPointLon() != null) { |
| | | double distance = FastDistanceCalculator.distanceFast(latitude, longitude, point.getPointLat(), point.getPointLon()); |
| | | itemVo.setDistance(BigDecimal.valueOf(distance).setScale(2, RoundingMode.HALF_UP).doubleValue()); |
| | | } |
| | | return itemVo; |
| | | } |
| | | |
| | | /** |
| | | * 排序规则: |
| | | * 默认点位优先,其次按场站内排序,再按与当前定位距离排序。 |
| | | */ |
| | | private Comparator<StationPointItemVo> buildStationPointComparator() { |
| | | return Comparator |
| | | .comparing((StationPointItemVo item) -> item.getIsDefault() == null ? 2 : item.getIsDefault()) |
| | | .thenComparing(item -> item.getStationSortNo() == null ? Integer.MAX_VALUE : item.getStationSortNo()) |
| | | .thenComparing(item -> item.getDistance() == null ? Double.MAX_VALUE : item.getDistance()); |
| | | } |
| | | |
| | | /** |
| | | * 查询用户收藏的场站ID。 |
| | | * 收藏服务异常时降级为空集合,避免影响场站详情主流程。 |
| | | */ |
| | | @Override |
| | | public Set<Long> getCollectStationIds(String uid) { |
| | | if (!StringUtils.hasText(uid)) { |
| | | return Collections.emptySet(); |
| | | } |
| | | try { |
| | | R<List<Long>> result = xpAppUserCollectAddrClient.getStationIdsByUserId(Long.valueOf(uid)); |
| | | if (result == null || CollUtil.isEmpty(result.getData())) { |
| | | return Collections.emptySet(); |
| | | } |
| | | return new HashSet<>(result.getData()); |
| | | } catch (Exception e) { |
| | | log.warn("查询用户收藏场站失败 uid={}", uid, e); |
| | | return Collections.emptySet(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public List<XpPoint> listByStationId(Long stationId) { |
| | | if (stationId == null) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return this.lambdaQuery() |
| | | .eq(XpPoint::getStationId, stationId) |
| | | .list(); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @Override |
| | | public void clearStationInfoByStationId(Long stationId) { |
| | | if (stationId == null) { |
| | | return; |
| | | } |
| | | this.lambdaUpdate() |
| | | .eq(XpPoint::getStationId, stationId) |
| | | .set(XpPoint::getStationId, null) |
| | | .set(XpPoint::getStationSortNo, 0) |
| | | .update(); |
| | | } |
| | | |
| | | /** |
| | | * 查询用户收藏的点位ID。 |
| | | * 子点位收藏态只用于回显,不影响场站筛选和排序主逻辑。 |
| | | */ |
| | | private Set<Long> getCollectPointIds(String uid) { |
| | | if (!StringUtils.hasText(uid)) { |
| | | return Collections.emptySet(); |
| | | } |
| | | try { |
| | | R<List<Long>> result = xpAppUserCollectAddrClient.getPointIdsByUserId(Long.valueOf(uid)); |
| | | if (result == null || CollUtil.isEmpty(result.getData())) { |
| | | return Collections.emptySet(); |
| | | } |
| | | return new HashSet<>(result.getData()); |
| | | } catch (Exception e) { |
| | | log.warn("查询用户收藏点位失败 uid={}", uid, e); |
| | | return Collections.emptySet(); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 场站停用 |
| | | * - 操作项展示为 [停用]。点击后弹出二次确认框,“当前场站包含12个点位,确认一起停用? |
| | | * - 是:确认后场站以及子点位状态流转至“已停用”。 |
| | | * - 否:子点位维持当前状态,但自动退回为“独立点位”。 |
| | | */ |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @Override |
| | | public void statusStopByStationId(Long stationId, Boolean pointStop) { |
| | | if (stationId == null) { |
| | | return; |
| | | } |
| | | if(pointStop){ |
| | | this.lambdaUpdate() |
| | | .eq(XpPoint::getStationId, stationId) |
| | | .set(XpPoint::getOperationStatus, 3)//运营状态 1=待启用 2=启用中 3=已停用 4=已失效 |
| | | .update(); |
| | | }else{ |
| | | clearStationInfoByStationId(stationId); |
| | | } |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @Override |
| | | public void statusEnableByStationId(Long stationId) { |
| | | if (stationId == null) { |
| | | return; |
| | | } |
| | | this.lambdaUpdate() |
| | | .eq(XpPoint::getStationId, stationId) |
| | | .set(XpPoint::getOperationStatus, 2)//运营状态 1=待启用 2=启用中 3=已停用 4=已失效 |
| | | .update(); |
| | | } |
| | | |
| | | /** |
| | | * 场站删除 |
| | | * - 删除拦截逻辑: |
| | | * - 测试阶段(待测试、测试中等):点击直接删除。 |
| | | * - 历史运营阶段(已停用、失效):点击弹出二次确认框“当前场站包含12个点位,确认一起删除?” |
| | | * - 是:点位统一一起删除 |
| | | * - 否:不自动删除点位,点位自动退回为[待测试]的“独立点位”,并自动继承父级运营区的运营时段 |
| | | */ |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @Override |
| | | public void pointDeleteByStationId(Long stationId, Boolean pointDelete) { |
| | | if (stationId == null) { |
| | | return; |
| | | } |
| | | if(pointDelete){ |
| | | this.lambdaUpdate() |
| | | .eq(XpPoint::getStationId, stationId) |
| | | .set(XpPoint::getDeleted, 1) |
| | | .update(); |
| | | }else{ |
| | | clearStationInfoByStationId(stationId); |
| | | } |
| | | } |
| | | |
| | | @Resource |
| | | private TokenService tokenService; |
| | | |
| | | |
| | | /** |
| | | * 场站 失效-围栏失效 场站失效 |
| | | * @param electronicAreaIds |
| | | */ |
| | | @Override |
| | | public void setStatusFailure(List<Long> electronicAreaIds){ |
| | | if(CollUtil.isNotEmpty(electronicAreaIds)){ |
| | | LambdaQueryWrapper<XpPoint> query = new LambdaQueryWrapper<>(); |
| | | query.in(XpPoint::getElectronicAreaId, electronicAreaIds); |
| | | query.eq(XpPoint::getDeleted, 0); |
| | | |
| | | List<XpPoint> list = super.list(query); |
| | | if(!list.isEmpty()){ |
| | | LoginUser loginUser = tokenService.getLoginUser(); |
| | | for (XpPoint bean : list) { |
| | | |
| | | bean.setUpdateBy(loginUser.getUsername()); |
| | | bean.setUpdateTime(LocalDateTime.now()); |
| | | bean.setOperationStatus(4);//场站状态 0=草稿 1=待启用 2=启用中 3=已停用 4=失效 |
| | | boolean b = updateById(bean); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |