2个文件已删除
2 文件已重命名
22个文件已添加
20个文件已修改
| New file |
| | |
| | | package com.xiaopeng.api.dto.map; |
| | | |
| | | import com.xiaopeng.api.dto.operation.LonLatDto; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | |
| | | @lombok.Data |
| | | @ApiModel(description = "线路和运费Dto") |
| | | public class RouteAndFareDto { |
| | | @ApiModelProperty(value = "出发地") |
| | | @NotNull(message = "出发地不能为空") |
| | | private LonLatDto origin; |
| | | |
| | | |
| | | @ApiModelProperty(value = "目的地") |
| | | @NotNull(message = "目的地不能为空") |
| | | private LonLatDto destination; |
| | | |
| | | |
| | | @ApiModelProperty(value = "开通城市id") |
| | | @NotNull(message = "开通城市id不能为空") |
| | | private Long openCityId; |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.api.dto.operation; |
| | | |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | |
| | | @Data |
| | | @ApiModel(value = "经纬度DTO") |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class LonLatDto { |
| | | |
| | | @ApiModelProperty(value = "经度") |
| | | @NotNull(message = "经度不能为空") |
| | | private Double longitude; |
| | | |
| | | @ApiModelProperty(value = "纬度") |
| | | @NotNull(message = "纬度不能为空") |
| | | private Double latitude; |
| | | } |
| | |
| | | return new RemoteMenuService(){ |
| | | |
| | | @Override |
| | | public R<List<SysMenu>> getMenusByMenuIds(List<Long> menuIds, String source) { |
| | | public R<List<SysMenu>> getMenusByMenuIds(List<Long> menuIds) { |
| | | log.error("获取菜单列表失败,menuIds: {}, 原因: {}", menuIds, cause.getMessage()); |
| | | // 降级返回空列表,避免登录失败 |
| | | return R.ok(new ArrayList<>()); |
| New file |
| | |
| | | package com.xiaopeng.api.factory.map; |
| | | |
| | | import com.xiaopeng.api.dto.map.RouteAndFareDto; |
| | | import com.xiaopeng.api.feignClient.map.XpMapClient; |
| | | import com.xiaopeng.api.feignClient.operation.XpSystemSettingClient; |
| | | import com.xiaopeng.api.vo.map.XpDistanceAndDurationVo; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.cloud.openfeign.FallbackFactory; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | @Slf4j |
| | | @Component |
| | | public class XpMapClientFallbackFactory implements FallbackFactory<XpMapClient> { |
| | | @Override |
| | | public XpMapClient create(Throwable cause) { |
| | | return new XpMapClient(){ |
| | | @Override |
| | | public R<XpDistanceAndDurationVo> getDistanceAndDuration(RouteAndFareDto dto) { |
| | | log.error("获取距离和时长失败:{}", cause.getMessage()); |
| | | return R.fail("获取距离和时长失败"); |
| | | } |
| | | |
| | | @Override |
| | | public R<Boolean> diagnosisArea(RouteAndFareDto dto) { |
| | | log.error("判断是否同一经营区域失败:{}", cause.getMessage()); |
| | | return R.fail("判断是否同一经营区域失败"); |
| | | } |
| | | }; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.api.factory.operation; |
| | | |
| | | import com.xiaopeng.api.feignClient.operation.SysLogClient; |
| | | import com.xiaopeng.api.feignClient.operation.XpSystemSettingClient; |
| | | import com.xiaopeng.api.model.operation.SysOperLog; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.cloud.openfeign.FallbackFactory; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | @Slf4j |
| | | @Component |
| | | public class XpSystemSettingClientFallbackFactory implements FallbackFactory<XpSystemSettingClient> { |
| | | @Override |
| | | public XpSystemSettingClient create(Throwable cause) { |
| | | return new XpSystemSettingClient(){ |
| | | @Override |
| | | public R getOrderDistance() { |
| | | log.error("获取下单距离失败:{}", cause.getMessage()); |
| | | return R.fail("获取下单距离失败"); |
| | | } |
| | | }; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.api.feignClient.map; |
| | | |
| | | import com.xiaopeng.api.dto.map.RouteAndFareDto; |
| | | import com.xiaopeng.api.factory.map.XpMapClientFallbackFactory; |
| | | import com.xiaopeng.api.factory.operation.XpSystemSettingClientFallbackFactory; |
| | | import com.xiaopeng.api.vo.map.XpDistanceAndDurationVo; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum; |
| | | import org.springframework.cloud.openfeign.FeignClient; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | |
| | | @FeignClient(contextId = "XpMapClient", value = ServiceNameConstantsEnum.XP_MAP_SERVICE, fallbackFactory = XpMapClientFallbackFactory.class) |
| | | public interface XpMapClient { |
| | | |
| | | /** |
| | | * 获取距离和时长 |
| | | * @param dto |
| | | * @return |
| | | */ |
| | | @PostMapping("/base/v1/mini-program/map/getDistanceAndDuration") |
| | | R<XpDistanceAndDurationVo> getDistanceAndDuration(@RequestBody RouteAndFareDto dto); |
| | | |
| | | |
| | | /** |
| | | * 判断是否同一经营区域 |
| | | * @param dto |
| | | * @return |
| | | */ |
| | | @PostMapping("/base/v1/mini-program/fence/diagnosisArea") |
| | | R<Boolean> diagnosisArea(@RequestBody RouteAndFareDto dto); |
| | | |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.api.feignClient.operation; |
| | | |
| | | import com.xiaopeng.api.factory.operation.XpSystemSettingClientFallbackFactory; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum; |
| | | import org.springframework.cloud.openfeign.FeignClient; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.Mapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | |
| | | @FeignClient(contextId = "XpSystemSettingClient", value = ServiceNameConstantsEnum.XP_OPERATION_SERVICE, fallbackFactory = XpSystemSettingClientFallbackFactory.class) |
| | | public interface XpSystemSettingClient { |
| | | |
| | | |
| | | @GetMapping("/base/v1/mini-program/support/getOrderDistance") |
| | | R getOrderDistance(); |
| | | |
| | | |
| | | |
| | | } |
| | |
| | | |
| | | public TreeSelect(SysMenuBak menu) |
| | | { |
| | | this.id = menu.getId(); |
| | | this.id = menu.getMenuId(); |
| | | this.label = menu.getMenuName(); |
| | | this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); |
| | | } |
| | |
| | | package com.xiaopeng.api.model.operation; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | |
| | | @ApiModelProperty(value = "生效时间段") |
| | | private String effectiveTimePeriod; |
| | | |
| | | @ApiModelProperty(value = "点位地址") |
| | | @TableField("point_address") |
| | | private String pointAddress; |
| | | |
| | | @ApiModelProperty(value = "备注") |
| | | private String remarks; |
| | | |
| | |
| | | @TableField("basic_pricing_json") |
| | | private String basicPricingJson; |
| | | |
| | | @ApiModelProperty(value = "起步价json配置") |
| | | @TableField("starting_fare_json") |
| | | private String startingFareJson; |
| | | |
| | | @ApiModelProperty(value = "动态价格json配置") |
| | | @TableField("dynamic_pricing_json") |
| | | private String dynamicPricingJson; |
| New file |
| | |
| | | package com.xiaopeng.api.vo.map; |
| | | |
| | | import io.swagger.annotations.ApiModel; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | @ApiModel(value = "XpDistanceAndDurationVo对象", description = "距离和时长") |
| | | public class XpDistanceAndDurationVo { |
| | | |
| | | // 距离 米 |
| | | private String distance; |
| | | |
| | | // 时长 秒 |
| | | private String duration; |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.api.vo.operation; |
| | | |
| | | import io.swagger.annotations.ApiModel; |
| | | |
| | | @lombok.Data |
| | | @ApiModel(description = "费用Vo") |
| | | public class FareVo { |
| | | |
| | | private String id; |
| | | |
| | | private Double estimatedDistanceKm; |
| | | |
| | | private Integer estimatedDurationMin; |
| | | |
| | | private Double originalFare; |
| | | |
| | | private Double discountAmount; |
| | | |
| | | private Double finalFare; |
| | | |
| | | private Integer waitingTime; |
| | | |
| | | private Integer maxPeopleNumber=5; |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.api.vo.operation; |
| | | |
| | | import io.swagger.annotations.ApiModel; |
| | | |
| | | @lombok.Data |
| | | @ApiModel(description = "线路和运费") |
| | | public class RouteAndFareVo { |
| | | |
| | | private RouteVo route; |
| | | |
| | | |
| | | private FareVo fareEstimate; |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.api.vo.operation; |
| | | |
| | | import com.xiaopeng.api.dto.operation.LonLatDto; |
| | | import io.swagger.annotations.ApiModel; |
| | | |
| | | import java.util.List; |
| | | |
| | | @lombok.Data |
| | | @ApiModel(description = "线路和运费") |
| | | public class RouteVo { |
| | | private Integer distance; |
| | | |
| | | private Integer duration; |
| | | |
| | | private String routeID; |
| | | |
| | | private List<LonLatDto> polyline; |
| | | } |
| | |
| | | @ApiModel("电子围栏开放区域Vo") |
| | | public class XpElectronicFenceVo { |
| | | @ApiModelProperty(value = "id") |
| | | private Long id; |
| | | private String FenceId; |
| | | |
| | | @ApiModelProperty(value = "围栏名称") |
| | | private String name; |
| | |
| | | if(null == loginUser.getPermissions()){ |
| | | return new HashSet<>(); |
| | | } |
| | | return loginUser.getPermissions(); |
| | | // return loginUser.getPermissions(); |
| | | return null; |
| | | } |
| | | catch (Exception e) |
| | | { |
| New file |
| | |
| | | package com.xiaopeng.map.controller; |
| | | |
| | | |
| | | import com.xiaopeng.api.dto.map.RouteAndFareDto; |
| | | import com.xiaopeng.api.dto.operation.LonLatDto; |
| | | import com.xiaopeng.api.vo.operation.XpElectronicFenceVo; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.common.core.web.controller.BaseController; |
| | | import com.xiaopeng.common.core.web.domain.AjaxResult; |
| | | import com.xiaopeng.map.dto.WalkTimeDto; |
| | | import com.xiaopeng.map.service.XpElectronicFenceAreaService; |
| | | import com.xiaopeng.map.vo.IsInOperationAreaVo; |
| | | import com.xiaopeng.map.vo.WalkTimeVo; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.validation.Valid; |
| | | import java.util.List; |
| | | |
| | | @Slf4j |
| | | @RestController |
| | | @RequestMapping("/base/v1/mini-program") |
| | | @Api(tags = "电子围栏") |
| | | public class XpElectronicFenceController extends BaseController { |
| | | |
| | | private final XpElectronicFenceAreaService xpElectronicFenceAreaService; |
| | | |
| | | public XpElectronicFenceController(XpElectronicFenceAreaService xpElectronicFenceAreaService) { |
| | | this.xpElectronicFenceAreaService = xpElectronicFenceAreaService; |
| | | |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取地图开放区域围栏 |
| | | * @return |
| | | */ |
| | | @ApiOperation(value = "获取地图开放区域围栏",tags = {"小程序-获取地图开放区域围栏"},response = XpElectronicFenceVo.class) |
| | | @GetMapping("/cities/list") |
| | | public AjaxResult<List<XpElectronicFenceVo>> citiesList() { |
| | | return success(xpElectronicFenceAreaService.citiesList()); |
| | | } |
| | | |
| | | |
| | | @ApiOperation(value = "判断当前位置是否在运营区域内",tags = {"小程序-判断当前位置是否在运营区域内"}) |
| | | @PostMapping("/fence/check") |
| | | public AjaxResult<IsInOperationAreaVo> isInOperationArea(@RequestBody @Valid LonLatDto dto) { |
| | | return success(xpElectronicFenceAreaService.isInOperationArea(dto)); |
| | | } |
| | | |
| | | |
| | | @ApiOperation(value = "根据点位获取步行时间和距离",tags = {"小程序-根据点位获取步行时间和距离"}) |
| | | @PostMapping("/fence/walkTime") |
| | | public AjaxResult<WalkTimeVo> getWalkTime(@RequestBody @Valid WalkTimeDto dto) { |
| | | return success(xpElectronicFenceAreaService.getWalkTime(dto)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 判断是否同一经营区域 |
| | | * @param dto |
| | | * @return |
| | | */ |
| | | @PostMapping("/fence/diagnosisArea") |
| | | public R<Boolean> diagnosisArea(@RequestBody RouteAndFareDto dto){ |
| | | return R.ok(xpElectronicFenceAreaService.diagnosisArea(dto)); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.map.controller; |
| | | |
| | | |
| | | import com.xiaopeng.api.dto.map.RouteAndFareDto; |
| | | import com.xiaopeng.api.dto.operation.LonLatDto; |
| | | import com.xiaopeng.api.vo.map.XpDistanceAndDurationVo; |
| | | import com.xiaopeng.api.vo.operation.RouteVo; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.map.utils.GDMapGeocodingUtil; |
| | | import com.xiaopeng.map.utils.mangoUtils.MongoGeoFenceUtil; |
| | | import io.swagger.annotations.Api; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.bson.Document; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Slf4j |
| | | @RestController |
| | | @RequestMapping("/base/v1/mini-program") |
| | | @Api(tags = "地图调用服务") |
| | | public class XpMapController { |
| | | |
| | | |
| | | private final GDMapGeocodingUtil gdMapGeocodingUtil; |
| | | private final MongoGeoFenceUtil mongoGeoFenceUtil; |
| | | |
| | | public XpMapController(GDMapGeocodingUtil gdMapGeocodingUtil, MongoGeoFenceUtil mongoGeoFenceUtil) { |
| | | this.gdMapGeocodingUtil = gdMapGeocodingUtil; |
| | | this.mongoGeoFenceUtil = mongoGeoFenceUtil; |
| | | |
| | | } |
| | | |
| | | |
| | | @PostMapping("/map/getDistanceAndDuration") |
| | | public R<XpDistanceAndDurationVo> getDistanceAndDuration(@RequestBody RouteAndFareDto dto){ |
| | | // 获取电子围栏的禁行区域点位 |
| | | |
| | | MongoGeoFenceUtil.GeoPoint startPoint = new MongoGeoFenceUtil.GeoPoint(dto.getOrigin().getLongitude(),dto.getOrigin().getLatitude()); |
| | | List<String> operatingArea = mongoGeoFenceUtil.findFencesContainingPoint(startPoint, "operating_area", "points"); |
| | | String fenceId = operatingArea.get(0); |
| | | |
| | | |
| | | // List<Document> pointsInFence = mongoGeoFenceUtil.findPointsInFence(fenceId, "parentId", "prohibited_area"); |
| | | List<Document> pointsInFence = mongoGeoFenceUtil.findPointsByParentId(fenceId, "parentId", "prohibited_area"); |
| | | List<LonLatDto> avoidRoads = pointsInFence.stream().map(point -> new LonLatDto(point.getDouble("longitude"), point.getDouble("latitude"))).collect(Collectors.toList()); |
| | | |
| | | |
| | | RouteVo routeVo = gdMapGeocodingUtil.getDistanceAndTime(dto.getOrigin(), dto.getDestination(), avoidRoads, fenceId); |
| | | if(routeVo==null){ |
| | | return R.fail("没有找到路线"); |
| | | } |
| | | |
| | | return R.ok(); |
| | | |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.map.dto; |
| | | |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | |
| | | @Data |
| | | @ApiModel(value = "步行时间DTO") |
| | | public class WalkTimeDto { |
| | | @ApiModelProperty(value = "开始经度") |
| | | @NotNull(message = "开始经度不能为空") |
| | | private Double startLongitude; |
| | | |
| | | @ApiModelProperty(value = "开始纬度") |
| | | @NotNull(message = "开始纬度不能为空") |
| | | private Double startLatitude; |
| | | |
| | | @ApiModelProperty(value = "结束经度") |
| | | @NotNull(message = "结束经度不能为空") |
| | | private Double endLongitude; |
| | | |
| | | @ApiModelProperty(value = "结束纬度") |
| | | @NotNull(message = "结束纬度不能为空") |
| | | private Double endLatitude; |
| | | |
| | | |
| | | |
| | | } |
| | |
| | | |
| | | |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.xiaopeng.api.dto.map.RouteAndFareDto; |
| | | import com.xiaopeng.api.dto.operation.LonLatDto; |
| | | import com.xiaopeng.api.model.operation.XpElectronicFenceArea; |
| | | import com.xiaopeng.api.vo.operation.XpElectronicFenceVo; |
| | | import com.xiaopeng.map.dto.WalkTimeDto; |
| | | import com.xiaopeng.map.vo.IsInOperationAreaVo; |
| | | import com.xiaopeng.map.vo.WalkTimeVo; |
| | | |
| | | import javax.validation.Valid; |
| | | import java.util.List; |
| | | |
| | | /** |
| | |
| | | */ |
| | | public interface XpElectronicFenceAreaService extends IService<XpElectronicFenceArea> { |
| | | |
| | | /** |
| | | * 获取所有的开放区域 |
| | | * @return |
| | | */ |
| | | List<XpElectronicFenceVo> citiesList(); |
| | | |
| | | |
| | | /** |
| | | * 判断当前位置是否在运营区域内 |
| | | * @param dto |
| | | * @return |
| | | */ |
| | | IsInOperationAreaVo isInOperationArea(LonLatDto dto); |
| | | |
| | | |
| | | /** |
| | | * 根据点位获取步行时间 |
| | | * @param dto |
| | | * @return |
| | | */ |
| | | WalkTimeVo getWalkTime(WalkTimeDto dto); |
| | | |
| | | /** |
| | | * 检测区域 |
| | | * @param dto |
| | | * @return |
| | | */ |
| | | Boolean diagnosisArea(RouteAndFareDto dto); |
| | | } |
| | |
| | | */ |
| | | public interface XpElectronicFenceService extends IService<XpElectronicFence> { |
| | | |
| | | |
| | | |
| | | /** |
| | | * 获取电子围栏列表 |
| | | */ |
| | | List<XpElectronicFenceVo> getXpElectronicFenceList(String cityCode); |
| | | |
| | | |
| | | /** |
| | | * 判断当前位置是否在运营区域内 |
| | | */ |
| | | Boolean isInElectronicFence(Double latitude, Double longitude,String cityCode); |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | |
| | |
| | | |
| | | |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.xiaopeng.api.dto.map.RouteAndFareDto; |
| | | import com.xiaopeng.api.dto.operation.LonLatDto; |
| | | import com.xiaopeng.api.feignClient.operation.XpSystemSettingClient; |
| | | import com.xiaopeng.api.model.operation.XpElectronicFenceArea; |
| | | import com.xiaopeng.api.model.operation.XpSystemSetting; |
| | | import com.xiaopeng.api.vo.operation.XpElectronicFenceVo; |
| | | import com.xiaopeng.api.vo.operation.XpPointsVo; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.map.dto.WalkTimeDto; |
| | | import com.xiaopeng.map.mapper.XpElectronicFenceAreaMapper; |
| | | import com.xiaopeng.map.repository.XpGeoFencingPolygonRepository; |
| | | import com.xiaopeng.map.service.XpElectronicFenceAreaService; |
| | | import com.xiaopeng.map.utils.AmapGeoFenceUtil; |
| | | import com.xiaopeng.map.utils.AmapWalkingRouteUtil; |
| | | import com.xiaopeng.map.utils.ElectricFenceUtil; |
| | | import com.xiaopeng.map.utils.mangoUtils.MongoGeoFenceUtil; |
| | | import com.xiaopeng.map.vo.IsInOperationAreaVo; |
| | | import com.xiaopeng.map.vo.WalkTimeVo; |
| | | import com.xiaopeng.map.vo.XpGeoFencingPolygon; |
| | | import io.netty.util.concurrent.DefaultThreadFactory; |
| | | import org.springframework.data.geo.Point; |
| | | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.io.IOException; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.concurrent.ArrayBlockingQueue; |
| | | import java.util.concurrent.ExecutorService; |
| | | import java.util.concurrent.ThreadPoolExecutor; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | | * <p> |
| | |
| | | @Service |
| | | public class XpElectronicFenceAreaServiceImpl extends ServiceImpl<XpElectronicFenceAreaMapper, XpElectronicFenceArea> implements XpElectronicFenceAreaService { |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | @Resource |
| | | private XpSystemSettingClient xpSystemSettingClient; |
| | | |
| | | @Resource |
| | | private XpGeoFencingPolygonRepository xpGeoFencingPolygonRepository; |
| | | |
| | | @Resource |
| | | private AmapGeoFenceUtil amapGeoFenceUtil; |
| | | |
| | | @Override |
| | | public List<XpElectronicFenceVo> citiesList() { |
| | | List<XpGeoFencingPolygon> xpGeoFencingPolygons = xpGeoFencingPolygonRepository.findAll(); |
| | | List<XpElectronicFenceVo> result = new ArrayList<>(); |
| | | |
| | | for (XpGeoFencingPolygon polygon : xpGeoFencingPolygons) { |
| | | XpElectronicFenceVo vo = new XpElectronicFenceVo(); |
| | | // 设置id(使用siteId作为id) |
| | | vo.setFenceId(polygon.getFenceId()); |
| | | // 设置围栏名称 |
| | | vo.setName(polygon.getName()); |
| | | // 转换GeoJsonPolygon为List<XpPointsVo> |
| | | List<XpPointsVo> pointsVoList = new ArrayList<>(); |
| | | if (polygon.getPoints() != null && polygon.getPoints().getCoordinates() != null) { |
| | | // GeoJsonPolygon的coordinates是一个二维数组,第一个元素是外部环 |
| | | List<Point> points = polygon.getPoints().getPoints(); |
| | | for (Point coord : points) { |
| | | XpPointsVo pointsVo = new XpPointsVo(); |
| | | // GeoJson的坐标顺序是[经度, 纬度] |
| | | pointsVo.setLng(coord.getY()); |
| | | pointsVo.setLat(coord.getX()); |
| | | pointsVoList.add(pointsVo); |
| | | } |
| | | } |
| | | vo.setPoints(pointsVoList); |
| | | result.add(vo); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 从MongoDB获取所有地理围栏多边形数据 |
| | | * @return 地理围栏多边形列表 |
| | | */ |
| | | public List<XpGeoFencingPolygon> getAllGeoFencingPolygons() { |
| | | return xpGeoFencingPolygonRepository.findAll(); |
| | | } |
| | | |
| | | /** |
| | | * 根据围栏id从MongoDB获取地理围栏多边形数据 |
| | | * @param siteId 围栏id |
| | | * @return 地理围栏多边形 |
| | | */ |
| | | public XpGeoFencingPolygon getGeoFencingPolygonBySiteId(Integer siteId) { |
| | | return xpGeoFencingPolygonRepository.findBySiteId(siteId); |
| | | } |
| | | |
| | | /** |
| | | * 根据围栏名称从MongoDB获取地理围栏多边形数据 |
| | | * @param name 围栏名称 |
| | | * @return 地理围栏多边形列表 |
| | | */ |
| | | public List<XpGeoFencingPolygon> getGeoFencingPolygonsByName(String name) { |
| | | if (name == null) { |
| | | return xpGeoFencingPolygonRepository.findAll(); |
| | | } |
| | | return xpGeoFencingPolygonRepository.findByName(name); |
| | | } |
| | | |
| | | @Resource |
| | | private MongoGeoFenceUtil mongoGeoFenceUtil; |
| | | |
| | | /** |
| | | * 判断经纬度是否在开放区域中 |
| | | * @param dto |
| | | * @return |
| | | */ |
| | | @Override |
| | | public IsInOperationAreaVo isInOperationArea(LonLatDto dto) { |
| | | |
| | | IsInOperationAreaVo vo = new IsInOperationAreaVo(); |
| | | // 组装数据 |
| | | MongoGeoFenceUtil.GeoPoint geoPoint = new MongoGeoFenceUtil.GeoPoint(dto.getLongitude(),dto.getLatitude()); |
| | | |
| | | List<String> operatingArea = mongoGeoFenceUtil.findFencesContainingPoint(geoPoint, "operating_area", "points"); |
| | | if (!operatingArea.isEmpty()) { |
| | | vo.setStatus("inside"); |
| | | } |
| | | return vo; |
| | | // try { |
| | | // executor.execute(() -> { |
| | | // for (XpElectronicFenceVo xpElectronicFenceVo : xpElectronicFenceVos) { |
| | | // ArrayList<AmapGeoFenceUtil.GeoPoint> geoPoints = new ArrayList<>(); |
| | | // // 获取多边形顶点坐标 |
| | | // List<XpPointsVo> points = xpElectronicFenceVo.getPoints(); |
| | | // for (XpPointsVo point : points) { |
| | | // geoPoints.add(new AmapGeoFenceUtil.GeoPoint(point.getLng(), point.getLat())); |
| | | // } |
| | | // AmapGeoFenceUtil.GeoFence geoFence = new AmapGeoFenceUtil.GeoFence(); |
| | | // geoFence.setPoints(geoPoints); |
| | | // |
| | | // AmapGeoFenceUtil.GeoFenceResult geoFenceResult = null; |
| | | // try { |
| | | // |
| | | // |
| | | // ElectricFenceUtil.areaMonitoring() |
| | | // geoFenceResult = amapGeoFenceUtil.checkInPolygonFence(geoPoint, geoFence); |
| | | // if (geoFenceResult.isInFence()) { |
| | | // vo.setStatus("inside"); |
| | | // } |
| | | // } catch (IOException e) { |
| | | // throw new RuntimeException(e); |
| | | // } |
| | | // } |
| | | // }); |
| | | // return vo; |
| | | // }catch (Exception e){ |
| | | // e.printStackTrace(); |
| | | // vo.setStatus("outside"); |
| | | // }finally { |
| | | // gracefulShutdown(executor); |
| | | // } |
| | | // |
| | | // return vo; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 优雅关闭线程池通用方法 |
| | | * |
| | | * @param pool 需要关闭的线程池 |
| | | */ |
| | | private static void gracefulShutdown(ExecutorService pool) { |
| | | pool.shutdown(); // 拒绝新任务提交 |
| | | try { |
| | | // 等待现有任务完成 |
| | | if (!pool.awaitTermination(60, TimeUnit.SECONDS)) { |
| | | pool.shutdownNow(); // 取消等待中的任务 只等待运行中的任务 |
| | | // 再次等待任务响应中断 |
| | | if (!pool.awaitTermination(60, TimeUnit.SECONDS)) { |
| | | } |
| | | } |
| | | } catch (InterruptedException e) { |
| | | // 重新尝试关闭 |
| | | pool.shutdownNow(); |
| | | Thread.currentThread().interrupt(); |
| | | } finally { |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public WalkTimeVo getWalkTime(WalkTimeDto dto) { |
| | | WalkTimeVo walkTimeVo = new WalkTimeVo(); |
| | | |
| | | // TODO 高德key |
| | | AmapWalkingRouteUtil util = new AmapWalkingRouteUtil(""); |
| | | // 示例1:使用经纬度直接查询 |
| | | System.out.println("=== 示例1:直接使用经纬度查询 ==="); |
| | | AmapWalkingRouteUtil.Location origin = new AmapWalkingRouteUtil.Location(dto.getStartLongitude(), dto.getStartLatitude()); // 天安门 |
| | | AmapWalkingRouteUtil.Location destination = new AmapWalkingRouteUtil.Location(dto.getEndLongitude(), dto.getEndLatitude()); // 王府井 |
| | | AmapWalkingRouteUtil.WalkingRouteResult result = util.getWalkingRoute(origin, destination); |
| | | if (result.isSuccess()) { |
| | | System.out.println("预计时间: " + result.getFormattedDuration()); |
| | | System.out.println("步行距离: " + result.getFormattedDistance()); |
| | | walkTimeVo.setWalkTime(result.getFormattedDuration()); |
| | | walkTimeVo.setWalkDistance(result.getDistance()); |
| | | walkTimeVo.setWalkDistance(result.getDistance()); |
| | | R r = xpSystemSettingClient.getOrderDistance(); |
| | | if (r.getCode()==200) { |
| | | Object orderDistance = r.getData(); |
| | | if (orderDistance != null) { |
| | | Double orderDistanceDouble = Double.parseDouble(orderDistance.toString()); |
| | | if(orderDistanceDouble>result.getDistance()){ |
| | | walkTimeVo.setStatus(1); |
| | | } |
| | | } |
| | | } |
| | | return walkTimeVo; |
| | | }else { |
| | | System.out.println("错误信息: " + result.getMessage()); |
| | | // 抛出异常 |
| | | throw new RuntimeException(result.getMessage()); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public Boolean diagnosisArea(RouteAndFareDto dto) { |
| | | // 起点数据 |
| | | MongoGeoFenceUtil.GeoPoint startPoint = new MongoGeoFenceUtil.GeoPoint(dto.getOrigin().getLongitude(),dto.getOrigin().getLatitude()); |
| | | // 终点数据 |
| | | MongoGeoFenceUtil.GeoPoint endPoint = new MongoGeoFenceUtil.GeoPoint(dto.getDestination().getLongitude(),dto.getDestination().getLatitude()); |
| | | |
| | | List<String> operatingArea = mongoGeoFenceUtil.findFencesContainingPoint(startPoint, "operating_area", "points"); |
| | | if (!operatingArea.isEmpty()) { |
| | | List<String> operatingArea2 = mongoGeoFenceUtil.findFencesContainingPoint(endPoint, "operating_area", "points"); |
| | | if (!operatingArea2.isEmpty()) { |
| | | // "开始判断是否在相同区域"; |
| | | if(operatingArea.get(0).equals(operatingArea2.get(0))){ |
| | | return true; |
| | | } |
| | | |
| | | } |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | } |
| | |
| | | public class XpElectronicFenceServiceImpl extends ServiceImpl<XpElectronicFenceMapper, XpElectronicFence> implements XpElectronicFenceService { |
| | | |
| | | |
| | | @Resource |
| | | private XpOpenCityMapper xpOpenCityMapper; |
| | | |
| | | @Resource |
| | | private XpElectronicFenceAreaMapper xpElectronicFenceAreaMapper; |
| | | |
| | | @Override |
| | | public List<XpElectronicFenceVo> getXpElectronicFenceList(String cityCode) { |
| | | // 通过code找到电子围栏信息 |
| | | // TODO 魔法数字 |
| | | List<XpOpenCity> xpOpenCities = xpOpenCityMapper.selectList(new LambdaQueryWrapper<XpOpenCity>().eq(XpOpenCity::getCityCode, cityCode).eq(XpOpenCity::getStatus, 1)); |
| | | if (!xpOpenCities.isEmpty()) { |
| | | List<Long> openCityIds = xpOpenCities.stream().map(XpOpenCity::getId).collect(Collectors.toList()); |
| | | |
| | | } |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean isInElectronicFence(Double latitude, Double longitude,String cityCode) { |
| | | |
| | | // 获取所有可用的电子围栏信息 |
| | | |
| | | return null; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 通过一个点位 判断是否在某个电子围栏内 |
| | | */ |
| | | private static boolean isInElectronicFence(double lon, double lat,List<String> fencePoints) { |
| | | boolean contains = false; |
| | | |
| | | for (String fencePoint : fencePoints) { |
| | | ArrayList<GeoPolygonUtil.GeoPoint> geoPoints = new ArrayList<>(); |
| | | String[] split = fencePoint.split(","); |
| | | for (String lonLat : split) { |
| | | String[] split1 = lonLat.split("_"); |
| | | GeoPolygonUtil.GeoPoint geoPoint = new GeoPolygonUtil.GeoPoint(Double.parseDouble(split1[0]), Double.parseDouble(split1[1])); |
| | | geoPoints.add(geoPoint); |
| | | } |
| | | contains = GeoPolygonUtil.contains(lon, lat, geoPoints); |
| | | if(contains){ |
| | | // 一旦在范围中直接返回true |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.map.utils; |
| | | |
| | | import com.fasterxml.jackson.databind.JsonNode; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import okhttp3.*; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.io.IOException; |
| | | import java.util.*; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | | * 高德地图地理围栏工具类 |
| | | */ |
| | | @Component |
| | | public class AmapGeoFenceUtil { |
| | | |
| | | private final String apiKey; |
| | | private final OkHttpClient httpClient; |
| | | private final ObjectMapper objectMapper; |
| | | |
| | | // 缓存 |
| | | private final Map<String, GeoFence> geoFenceCache = new HashMap<>(); |
| | | |
| | | public AmapGeoFenceUtil(String apiKey) { |
| | | this.apiKey = apiKey; |
| | | this.httpClient = new OkHttpClient.Builder() |
| | | .connectTimeout(10, TimeUnit.SECONDS) |
| | | .readTimeout(10, TimeUnit.SECONDS) |
| | | .writeTimeout(10, TimeUnit.SECONDS) |
| | | .build(); |
| | | this.objectMapper = new ObjectMapper(); |
| | | } |
| | | |
| | | /** |
| | | * 地理坐标点 |
| | | */ |
| | | public static class GeoPoint { |
| | | private double longitude; |
| | | private double latitude; |
| | | |
| | | public GeoPoint(double longitude, double latitude) { |
| | | this.longitude = longitude; |
| | | this.latitude = latitude; |
| | | } |
| | | |
| | | public double getLongitude() { |
| | | return longitude; |
| | | } |
| | | |
| | | public double getLatitude() { |
| | | return latitude; |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return String.format("%.6f,%.6f", longitude, latitude); |
| | | } |
| | | |
| | | public String toLngLatString() { |
| | | return String.format("%.6f,%.6f", longitude, latitude); |
| | | } |
| | | |
| | | public String toLatLngString() { |
| | | return String.format("%.6f,%.6f", latitude, longitude); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 地理围栏类型 |
| | | */ |
| | | public enum GeoFenceType { |
| | | CIRCLE, // 圆形 |
| | | POLYGON, // 多边形 |
| | | DISTRICT // 行政区 |
| | | } |
| | | |
| | | /** |
| | | * 地理围栏 |
| | | */ |
| | | public static class GeoFence { |
| | | private String id; |
| | | private String name; |
| | | private GeoFenceType type; |
| | | private List<GeoPoint> points; // 多边形顶点 |
| | | private GeoPoint center; // 圆心 |
| | | private double radius; // 半径(米) |
| | | private String adcode; // 行政区划代码 |
| | | private String polygonString; // 多边形字符串 |
| | | |
| | | public GeoFence(String id, String name, GeoFenceType type) { |
| | | this.id = id; |
| | | this.name = name; |
| | | this.type = type; |
| | | this.points = new ArrayList<>(); |
| | | } |
| | | |
| | | public GeoFence() { |
| | | |
| | | } |
| | | |
| | | |
| | | // Getters and Setters |
| | | public String getId() { return id; } |
| | | public void setId(String id) { this.id = id; } |
| | | |
| | | public String getName() { return name; } |
| | | public void setName(String name) { this.name = name; } |
| | | |
| | | public GeoFenceType getType() { return type; } |
| | | public void setType(GeoFenceType type) { this.type = type; } |
| | | |
| | | public List<GeoPoint> getPoints() { return points; } |
| | | public void setPoints(List<GeoPoint> points) { this.points = points; } |
| | | |
| | | public GeoPoint getCenter() { return center; } |
| | | public void setCenter(GeoPoint center) { this.center = center; } |
| | | |
| | | public double getRadius() { return radius; } |
| | | public void setRadius(double radius) { this.radius = radius; } |
| | | |
| | | public String getAdcode() { return adcode; } |
| | | public void setAdcode(String adcode) { this.adcode = adcode; } |
| | | |
| | | public String getPolygonString() { return polygonString; } |
| | | public void setPolygonString(String polygonString) { this.polygonString = polygonString; } |
| | | |
| | | public void addPoint(GeoPoint point) { |
| | | this.points.add(point); |
| | | } |
| | | |
| | | public void addPoint(double lng, double lat) { |
| | | this.points.add(new GeoPoint(lng, lat)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 围栏检查结果 |
| | | */ |
| | | public static class GeoFenceResult { |
| | | private boolean isInFence; |
| | | private String fenceId; |
| | | private String fenceName; |
| | | private double distance; // 距离围栏边界的距离(米) |
| | | private String message; |
| | | private GeoPoint location; |
| | | private GeoFenceType fenceType; |
| | | |
| | | // Getters and Setters |
| | | public boolean isInFence() { return isInFence; } |
| | | public void setInFence(boolean inFence) { isInFence = inFence; } |
| | | |
| | | public String getFenceId() { return fenceId; } |
| | | public void setFenceId(String fenceId) { this.fenceId = fenceId; } |
| | | |
| | | public String getFenceName() { return fenceName; } |
| | | public void setFenceName(String fenceName) { this.fenceName = fenceName; } |
| | | |
| | | public double getDistance() { return distance; } |
| | | public void setDistance(double distance) { this.distance = distance; } |
| | | |
| | | public String getMessage() { return message; } |
| | | public void setMessage(String message) { this.message = message; } |
| | | |
| | | public GeoPoint getLocation() { return location; } |
| | | public void setLocation(GeoPoint location) { this.location = location; } |
| | | |
| | | public GeoFenceType getFenceType() { return fenceType; } |
| | | public void setFenceType(GeoFenceType fenceType) { this.fenceType = fenceType; } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return String.format("GeoFenceResult{inFence=%s, fence='%s', distance=%.2fm, message='%s'}", |
| | | isInFence, fenceName, distance, message); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 检查点是否在圆形围栏内(调用高德API) |
| | | */ |
| | | public GeoFenceResult checkInCircleFence(GeoPoint point, GeoFence fence) throws IOException { |
| | | GeoFenceResult result = new GeoFenceResult(); |
| | | result.setLocation(point); |
| | | result.setFenceId(fence.getId()); |
| | | result.setFenceName(fence.getName()); |
| | | result.setFenceType(GeoFenceType.CIRCLE); |
| | | |
| | | // 构建API请求参数 |
| | | Map<String, String> params = new HashMap<>(); |
| | | params.put("key", apiKey); |
| | | params.put("location", point.toLngLatString()); |
| | | params.put("radius", String.valueOf(fence.getRadius())); |
| | | params.put("center", fence.getCenter().toLngLatString()); |
| | | |
| | | String url = buildUrl("https://restapi.amap.com/v3/geofence/status", params); |
| | | |
| | | Request request = new Request.Builder() |
| | | .url(url) |
| | | .get() |
| | | .build(); |
| | | |
| | | try (Response response = httpClient.newCall(request).execute()) { |
| | | if (!response.isSuccessful()) { |
| | | result.setInFence(false); |
| | | result.setMessage("API请求失败: " + response.code()); |
| | | return result; |
| | | } |
| | | |
| | | String responseBody = response.body().string(); |
| | | JsonNode rootNode = objectMapper.readTree(responseBody); |
| | | |
| | | String status = rootNode.get("status").asText(); |
| | | String info = rootNode.get("info").asText(); |
| | | |
| | | if ("1".equals(status)) { |
| | | JsonNode fencesNode = rootNode.get("fences"); |
| | | if (fencesNode != null && fencesNode.size() > 0) { |
| | | JsonNode fenceNode = fencesNode.get(0); |
| | | String fenceStatus = fenceNode.get("status").asText(); |
| | | |
| | | // status: in-在围栏内, out-在围栏外 |
| | | result.setInFence("in".equals(fenceStatus)); |
| | | result.setMessage("围栏状态: " + fenceStatus); |
| | | |
| | | // 获取距离 |
| | | if (fenceNode.has("distance")) { |
| | | result.setDistance(fenceNode.get("distance").asDouble()); |
| | | } |
| | | } |
| | | } else { |
| | | result.setInFence(false); |
| | | result.setMessage("API返回错误: " + info); |
| | | } |
| | | } catch (Exception e) { |
| | | result.setInFence(false); |
| | | result.setMessage("检查围栏状态时发生错误: " + e.getMessage()); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 检查点是否在多边形围栏内(调用高德API) |
| | | */ |
| | | public GeoFenceResult checkInPolygonFence(GeoPoint point, GeoFence fence) throws IOException { |
| | | GeoFenceResult result = new GeoFenceResult(); |
| | | result.setLocation(point); |
| | | result.setFenceId(fence.getId()); |
| | | result.setFenceName(fence.getName()); |
| | | result.setFenceType(GeoFenceType.POLYGON); |
| | | |
| | | // 构建多边形字符串 |
| | | String polygon = buildPolygonString(fence.getPoints()); |
| | | |
| | | // 构建API请求参数 |
| | | Map<String, String> params = new HashMap<>(); |
| | | params.put("key", apiKey); |
| | | params.put("location", point.toLngLatString()); |
| | | params.put("polygon", polygon); |
| | | |
| | | String url = buildUrl("https://restapi.amap.com/v3/geofence/status", params); |
| | | |
| | | Request request = new Request.Builder() |
| | | .url(url) |
| | | .get() |
| | | .build(); |
| | | |
| | | try (Response response = httpClient.newCall(request).execute()) { |
| | | if (!response.isSuccessful()) { |
| | | result.setInFence(false); |
| | | result.setMessage("API请求失败: " + response.code()); |
| | | return result; |
| | | } |
| | | |
| | | String responseBody = response.body().string(); |
| | | JsonNode rootNode = objectMapper.readTree(responseBody); |
| | | |
| | | String status = rootNode.get("status").asText(); |
| | | String info = rootNode.get("info").asText(); |
| | | |
| | | if ("1".equals(status)) { |
| | | JsonNode fencesNode = rootNode.get("fences"); |
| | | if (fencesNode != null && fencesNode.size() > 0) { |
| | | JsonNode fenceNode = fencesNode.get(0); |
| | | String fenceStatus = fenceNode.get("status").asText(); |
| | | |
| | | result.setInFence("in".equals(fenceStatus)); |
| | | result.setMessage("围栏状态: " + fenceStatus); |
| | | |
| | | if (fenceNode.has("distance")) { |
| | | result.setDistance(fenceNode.get("distance").asDouble()); |
| | | } |
| | | } |
| | | } else { |
| | | result.setInFence(false); |
| | | result.setMessage("API返回错误: " + info); |
| | | } |
| | | } catch (Exception e) { |
| | | result.setInFence(false); |
| | | result.setMessage("检查围栏状态时发生错误: " + e.getMessage()); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 检查点是否在行政区划内 |
| | | */ |
| | | public GeoFenceResult checkInDistrict(GeoPoint point, String adcode) throws IOException { |
| | | GeoFenceResult result = new GeoFenceResult(); |
| | | result.setLocation(point); |
| | | result.setFenceType(GeoFenceType.DISTRICT); |
| | | |
| | | // 调用逆地理编码API获取行政区划信息 |
| | | Map<String, String> params = new HashMap<>(); |
| | | params.put("key", apiKey); |
| | | params.put("location", point.toLngLatString()); |
| | | params.put("extensions", "base"); |
| | | params.put("poitype", ""); |
| | | |
| | | String url = buildUrl("https://restapi.amap.com/v3/geocode/regeo", params); |
| | | |
| | | Request request = new Request.Builder() |
| | | .url(url) |
| | | .get() |
| | | .build(); |
| | | |
| | | try (Response response = httpClient.newCall(request).execute()) { |
| | | if (!response.isSuccessful()) { |
| | | result.setInFence(false); |
| | | result.setMessage("API请求失败: " + response.code()); |
| | | return result; |
| | | } |
| | | |
| | | String responseBody = response.body().string(); |
| | | JsonNode rootNode = objectMapper.readTree(responseBody); |
| | | |
| | | String status = rootNode.get("status").asText(); |
| | | String info = rootNode.get("info").asText(); |
| | | |
| | | if ("1".equals(status)) { |
| | | JsonNode regeocode = rootNode.get("regeocode"); |
| | | if (regeocode != null) { |
| | | JsonNode addressComponent = regeocode.get("addressComponent"); |
| | | if (addressComponent != null) { |
| | | String districtAdcode = addressComponent.get("adcode").asText(); |
| | | |
| | | result.setInFence(adcode.equals(districtAdcode)); |
| | | result.setMessage("行政区划代码: " + districtAdcode); |
| | | result.setFenceId(adcode); |
| | | result.setFenceName(addressComponent.get("district").asText()); |
| | | } |
| | | } |
| | | } else { |
| | | result.setInFence(false); |
| | | result.setMessage("API返回错误: " + info); |
| | | } |
| | | } catch (Exception e) { |
| | | result.setInFence(false); |
| | | result.setMessage("检查行政区划时发生错误: " + e.getMessage()); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 批量检查多个围栏 |
| | | */ |
| | | public List<GeoFenceResult> checkMultipleFences(GeoPoint point, List<GeoFence> fences) { |
| | | List<GeoFenceResult> results = new ArrayList<>(); |
| | | |
| | | for (GeoFence fence : fences) { |
| | | try { |
| | | GeoFenceResult result; |
| | | if (fence.getType() == GeoFenceType.CIRCLE) { |
| | | result = checkInCircleFence(point, fence); |
| | | } else if (fence.getType() == GeoFenceType.POLYGON) { |
| | | result = checkInPolygonFence(point, fence); |
| | | } else { |
| | | result = new GeoFenceResult(); |
| | | result.setInFence(false); |
| | | result.setMessage("不支持的围栏类型"); |
| | | } |
| | | results.add(result); |
| | | } catch (IOException e) { |
| | | GeoFenceResult errorResult = new GeoFenceResult(); |
| | | errorResult.setInFence(false); |
| | | errorResult.setFenceId(fence.getId()); |
| | | errorResult.setFenceName(fence.getName()); |
| | | errorResult.setMessage("检查失败: " + e.getMessage()); |
| | | results.add(errorResult); |
| | | } |
| | | } |
| | | |
| | | return results; |
| | | } |
| | | |
| | | /** |
| | | * 创建并缓存地理围栏 |
| | | */ |
| | | public void createGeoFence(GeoFence fence) { |
| | | geoFenceCache.put(fence.getId(), fence); |
| | | } |
| | | |
| | | /** |
| | | * 从缓存获取地理围栏 |
| | | */ |
| | | public GeoFence getGeoFence(String fenceId) { |
| | | return geoFenceCache.get(fenceId); |
| | | } |
| | | |
| | | /** |
| | | * 通过高德地图行政区域查询API获取边界坐标 |
| | | */ |
| | | public GeoFence getDistrictPolygon(String adcode) throws IOException { |
| | | Map<String, String> params = new HashMap<>(); |
| | | params.put("key", apiKey); |
| | | params.put("keywords", adcode); |
| | | params.put("subdistrict", "0"); // 不获取下级行政区 |
| | | params.put("extensions", "all"); // 获取边界坐标 |
| | | |
| | | String url = buildUrl("https://restapi.amap.com/v3/config/district", params); |
| | | |
| | | Request request = new Request.Builder() |
| | | .url(url) |
| | | .get() |
| | | .build(); |
| | | |
| | | try (Response response = httpClient.newCall(request).execute()) { |
| | | if (!response.isSuccessful()) { |
| | | throw new IOException("请求失败: " + response.code()); |
| | | } |
| | | |
| | | String responseBody = response.body().string(); |
| | | JsonNode rootNode = objectMapper.readTree(responseBody); |
| | | |
| | | String status = rootNode.get("status").asText(); |
| | | String info = rootNode.get("info").asText(); |
| | | |
| | | if ("1".equals(status)) { |
| | | JsonNode districts = rootNode.get("districts"); |
| | | if (districts != null && districts.size() > 0) { |
| | | JsonNode district = districts.get(0); |
| | | |
| | | GeoFence fence = new GeoFence( |
| | | adcode, |
| | | district.get("name").asText(), |
| | | GeoFenceType.POLYGON |
| | | ); |
| | | |
| | | // 解析多边形坐标 |
| | | String polyline = district.get("polyline").asText(); |
| | | parsePolylineString(polyline, fence); |
| | | |
| | | return fence; |
| | | } |
| | | } |
| | | |
| | | throw new IOException("获取行政区划失败: " + info); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 解析多边形坐标字符串 |
| | | */ |
| | | private void parsePolylineString(String polyline, GeoFence fence) { |
| | | // 格式示例: "116.32,39.91|116.33,39.92|116.34,39.93" |
| | | String[] points = polyline.split("\\|"); |
| | | for (String point : points) { |
| | | String[] coords = point.split(","); |
| | | if (coords.length >= 2) { |
| | | double lng = Double.parseDouble(coords[0].trim()); |
| | | double lat = Double.parseDouble(coords[1].trim()); |
| | | fence.addPoint(lng, lat); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 计算两点间距离(Haversine公式) |
| | | */ |
| | | public double calculateDistance(GeoPoint p1, GeoPoint p2) { |
| | | final double EARTH_RADIUS = 6371000.0; // 地球平均半径(米) |
| | | |
| | | double lat1 = Math.toRadians(p1.getLatitude()); |
| | | double lon1 = Math.toRadians(p1.getLongitude()); |
| | | double lat2 = Math.toRadians(p2.getLatitude()); |
| | | double lon2 = Math.toRadians(p2.getLongitude()); |
| | | |
| | | double dlat = lat2 - lat1; |
| | | double dlon = lon2 - lon1; |
| | | |
| | | double a = Math.sin(dlat / 2) * Math.sin(dlat / 2) + |
| | | Math.cos(lat1) * Math.cos(lat2) * |
| | | Math.sin(dlon / 2) * Math.sin(dlon / 2); |
| | | |
| | | double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); |
| | | |
| | | return EARTH_RADIUS * c; |
| | | } |
| | | |
| | | /** |
| | | * 构建多边形字符串 |
| | | */ |
| | | private String buildPolygonString(List<GeoPoint> points) { |
| | | StringBuilder sb = new StringBuilder(); |
| | | for (GeoPoint point : points) { |
| | | sb.append(point.getLongitude()).append(",").append(point.getLatitude()).append("|"); |
| | | } |
| | | // 移除最后一个竖线 |
| | | if (sb.length() > 0) { |
| | | sb.deleteCharAt(sb.length() - 1); |
| | | } |
| | | return sb.toString(); |
| | | } |
| | | |
| | | /** |
| | | * 构建URL |
| | | */ |
| | | private String buildUrl(String baseUrl, Map<String, String> params) { |
| | | StringBuilder urlBuilder = new StringBuilder(baseUrl).append("?"); |
| | | params.forEach((key, value) -> { |
| | | urlBuilder.append(key).append("=").append(value).append("&"); |
| | | }); |
| | | urlBuilder.deleteCharAt(urlBuilder.length() - 1); |
| | | return urlBuilder.toString(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.map.utils; |
| | | |
| | | import com.fasterxml.jackson.databind.JsonNode; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import okhttp3.OkHttpClient; |
| | | import okhttp3.Request; |
| | | import okhttp3.Response; |
| | | |
| | | import java.io.IOException; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | | * 高德地图步行导航工具类 |
| | | */ |
| | | public class AmapWalkingRouteUtil { |
| | | |
| | | // 高德地图Web服务API配置 |
| | | private static final String AMAP_API_BASE_URL = "https://restapi.amap.com/v3/direction/walking"; |
| | | private static final String AMAP_GEOCODING_URL = "https://restapi.amap.com/v3/geocode/geo"; |
| | | |
| | | // 你的高德地图API Key(请替换成你自己的) |
| | | private final String apiKey; |
| | | |
| | | // HTTP客户端 |
| | | private final OkHttpClient httpClient; |
| | | private final ObjectMapper objectMapper; |
| | | |
| | | /** |
| | | * 构造函数 |
| | | * @param apiKey 高德地图API Key |
| | | */ |
| | | public AmapWalkingRouteUtil(String apiKey) { |
| | | this.apiKey = apiKey; |
| | | this.httpClient = new OkHttpClient.Builder() |
| | | .connectTimeout(10, TimeUnit.SECONDS) |
| | | .readTimeout(10, TimeUnit.SECONDS) |
| | | .writeTimeout(10, TimeUnit.SECONDS) |
| | | .build(); |
| | | this.objectMapper = new ObjectMapper(); |
| | | } |
| | | |
| | | /** |
| | | * 位置坐标类 |
| | | */ |
| | | public static class Location { |
| | | private final double longitude; // 经度 |
| | | private final double latitude; // 纬度 |
| | | |
| | | public Location(double longitude, double latitude) { |
| | | this.longitude = longitude; |
| | | this.latitude = latitude; |
| | | } |
| | | |
| | | public double getLongitude() { |
| | | return longitude; |
| | | } |
| | | |
| | | public double getLatitude() { |
| | | return latitude; |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return String.format("%.6f,%.6f", longitude, latitude); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 步行路线结果类 |
| | | */ |
| | | public static class WalkingRouteResult { |
| | | private int duration; // 步行时间(秒) |
| | | private int distance; // 步行距离(米) |
| | | private String origin; // 起点地址 |
| | | private String destination; // 终点地址 |
| | | private boolean success; // 是否成功 |
| | | private String message; // 返回信息 |
| | | |
| | | // getters and setters |
| | | public int getDuration() { |
| | | return duration; |
| | | } |
| | | |
| | | public void setDuration(int duration) { |
| | | this.duration = duration; |
| | | } |
| | | |
| | | public int getDistance() { |
| | | return distance; |
| | | } |
| | | |
| | | public void setDistance(int distance) { |
| | | this.distance = distance; |
| | | } |
| | | |
| | | public String getOrigin() { |
| | | return origin; |
| | | } |
| | | |
| | | public void setOrigin(String origin) { |
| | | this.origin = origin; |
| | | } |
| | | |
| | | public String getDestination() { |
| | | return destination; |
| | | } |
| | | |
| | | public void setDestination(String destination) { |
| | | this.destination = destination; |
| | | } |
| | | |
| | | public boolean isSuccess() { |
| | | return success; |
| | | } |
| | | |
| | | public void setSuccess(boolean success) { |
| | | this.success = success; |
| | | } |
| | | |
| | | public String getMessage() { |
| | | return message; |
| | | } |
| | | |
| | | public void setMessage(String message) { |
| | | this.message = message; |
| | | } |
| | | |
| | | /** |
| | | * 获取格式化的步行时间 |
| | | * @return 格式化的时间字符串(如:15分钟30秒) |
| | | */ |
| | | public String getFormattedDuration() { |
| | | int hours = duration / 3600; |
| | | int minutes = (duration % 3600) / 60; |
| | | int seconds = duration % 60; |
| | | |
| | | if (hours > 0) { |
| | | return String.format("%d小时%d分钟", hours, minutes); |
| | | } else { |
| | | return String.format("%d秒", seconds); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取格式化的步行距离 |
| | | * @return 格式化的距离字符串(如:1.2公里) |
| | | */ |
| | | public String getFormattedDistance() { |
| | | if (distance >= 1000) { |
| | | double km = distance / 1000.0; |
| | | return String.format("%.1f公里", km); |
| | | } else { |
| | | return String.format("%d米", distance); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 根据地址获取经纬度坐标 |
| | | * @param address 地址信息 |
| | | * @return 经纬度坐标 |
| | | */ |
| | | public Location getLocationByAddress(String address) throws IOException { |
| | | Map<String, String> params = new HashMap<>(); |
| | | params.put("key", apiKey); |
| | | params.put("address", address); |
| | | |
| | | String url = buildUrl(AMAP_GEOCODING_URL, params); |
| | | Request request = new Request.Builder().url(url).build(); |
| | | |
| | | try (Response response = httpClient.newCall(request).execute()) { |
| | | if (!response.isSuccessful()) { |
| | | throw new IOException("请求失败: " + response); |
| | | } |
| | | |
| | | String responseBody = response.body().string(); |
| | | JsonNode rootNode = objectMapper.readTree(responseBody); |
| | | |
| | | if ("1".equals(rootNode.get("status").asText())) { |
| | | JsonNode geocodes = rootNode.get("geocodes"); |
| | | if (geocodes != null && geocodes.size() > 0) { |
| | | String locationStr = geocodes.get(0).get("location").asText(); |
| | | String[] locationArray = locationStr.split(","); |
| | | double longitude = Double.parseDouble(locationArray[0]); |
| | | double latitude = Double.parseDouble(locationArray[1]); |
| | | return new Location(longitude, latitude); |
| | | } |
| | | } |
| | | |
| | | throw new IOException("地址解析失败: " + rootNode.get("info").asText()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取步行路线和时间 |
| | | * @param origin 起点坐标 |
| | | * @param destination 终点坐标 |
| | | * @return 步行路线结果 |
| | | */ |
| | | public WalkingRouteResult getWalkingRoute(Location origin, Location destination) { |
| | | WalkingRouteResult result = new WalkingRouteResult(); |
| | | |
| | | try { |
| | | Map<String, String> params = new HashMap<>(); |
| | | params.put("key", apiKey); |
| | | params.put("origin", origin.toString()); |
| | | params.put("destination", destination.toString()); |
| | | |
| | | String url = buildUrl(AMAP_API_BASE_URL, params); |
| | | Request request = new Request.Builder().url(url).build(); |
| | | |
| | | try (Response response = httpClient.newCall(request).execute()) { |
| | | if (!response.isSuccessful()) { |
| | | result.setSuccess(false); |
| | | result.setMessage("HTTP请求失败: " + response.code()); |
| | | return result; |
| | | } |
| | | |
| | | String responseBody = response.body().string(); |
| | | JsonNode rootNode = objectMapper.readTree(responseBody); |
| | | |
| | | String status = rootNode.get("status").asText(); |
| | | String info = rootNode.get("info").asText(); |
| | | |
| | | if ("1".equals(status)) { |
| | | JsonNode route = rootNode.get("route"); |
| | | JsonNode paths = route.get("paths"); |
| | | |
| | | if (paths != null && paths.size() > 0) { |
| | | JsonNode firstPath = paths.get(0); |
| | | int duration = firstPath.get("duration").asInt(); |
| | | int distance = firstPath.get("distance").asInt(); |
| | | |
| | | result.setDuration(duration); |
| | | result.setDistance(distance); |
| | | result.setSuccess(true); |
| | | result.setMessage("成功获取步行路线"); |
| | | |
| | | // 尝试获取地址信息 |
| | | result.setOrigin(getAddressByLocation(origin)); |
| | | result.setDestination(getAddressByLocation(destination)); |
| | | |
| | | } else { |
| | | result.setSuccess(false); |
| | | result.setMessage("未找到步行路线"); |
| | | } |
| | | } else { |
| | | result.setSuccess(false); |
| | | result.setMessage("API返回错误: " + info); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | result.setSuccess(false); |
| | | result.setMessage("获取步行路线失败: " + e.getMessage()); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 根据经纬度获取地址(逆地理编码) |
| | | * @param location 经纬度坐标 |
| | | * @return 地址信息 |
| | | */ |
| | | private String getAddressByLocation(Location location) { |
| | | try { |
| | | Map<String, String> params = new HashMap<>(); |
| | | params.put("key", apiKey); |
| | | params.put("location", location.toString()); |
| | | |
| | | String url = buildUrl("https://restapi.amap.com/v3/geocode/regeo", params); |
| | | Request request = new Request.Builder().url(url).build(); |
| | | |
| | | try (Response response = httpClient.newCall(request).execute()) { |
| | | if (response.isSuccessful()) { |
| | | String responseBody = response.body().string(); |
| | | JsonNode rootNode = objectMapper.readTree(responseBody); |
| | | |
| | | if ("1".equals(rootNode.get("status").asText())) { |
| | | JsonNode regeocode = rootNode.get("regeocode"); |
| | | if (regeocode != null) { |
| | | return regeocode.get("formatted_address").asText(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | // 地址解析失败时返回坐标 |
| | | } |
| | | return location.toString(); |
| | | } |
| | | |
| | | /** |
| | | * 构建带参数的URL |
| | | * @param baseUrl 基础URL |
| | | * @param params 参数Map |
| | | * @return 完整的URL |
| | | */ |
| | | private String buildUrl(String baseUrl, Map<String, String> params) { |
| | | StringBuilder urlBuilder = new StringBuilder(baseUrl).append("?"); |
| | | params.forEach((key, value) -> { |
| | | urlBuilder.append(key).append("=").append(value).append("&"); |
| | | }); |
| | | // 删除最后一个& |
| | | urlBuilder.deleteCharAt(urlBuilder.length() - 1); |
| | | return urlBuilder.toString(); |
| | | } |
| | | |
| | | /** |
| | | * 计算两点间的直线距离(仅供参考,不是实际步行距离) |
| | | * @param loc1 坐标1 |
| | | * @param loc2 坐标2 |
| | | * @return 直线距离(米) |
| | | */ |
| | | public static double calculateStraightDistance(Location loc1, Location loc2) { |
| | | double earthRadius = 6371000; // 地球半径(米) |
| | | double lat1 = Math.toRadians(loc1.getLatitude()); |
| | | double lat2 = Math.toRadians(loc2.getLatitude()); |
| | | double deltaLat = Math.toRadians(loc2.getLatitude() - loc1.getLatitude()); |
| | | double deltaLon = Math.toRadians(loc2.getLongitude() - loc1.getLongitude()); |
| | | |
| | | double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) + |
| | | Math.cos(lat1) * Math.cos(lat2) * |
| | | Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2); |
| | | double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); |
| | | |
| | | return earthRadius * c; |
| | | } |
| | | |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.map.utils; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 电子围栏工具类 |
| | | */ |
| | | public class ElectricFenceUtil { |
| | | |
| | | |
| | | |
| | | /** |
| | | * |
| | | * @param coordinate 电子围栏 20,20:30,20 |
| | | * @param locations 当前点位 |
| | | * @return |
| | | */ |
| | | public static boolean monitorElectricFenc(String coordinate, String locations){ |
| | | return areaMonitoring(coordinate, locations); |
| | | } |
| | | public static boolean areaMonitoring(String coordinate, String lonLat){ |
| | | boolean flag = false; |
| | | String[] split = coordinate.split(";"); |
| | | List<Double> pointsX = new ArrayList<>(); |
| | | List<Double> pointsY = new ArrayList<>(); |
| | | for (String s : split) { |
| | | pointsX.add(Double.valueOf(s.split(",")[0])); |
| | | pointsY.add(Double.valueOf(s.split(",")[1])); |
| | | } |
| | | String lon1 = lonLat.split(",")[0]; |
| | | String lat1 = lonLat.split(",")[1]; |
| | | boolean inPolygon = isInPolygon(Double.valueOf(lon1), Double.valueOf(lat1), pointsX, pointsY); |
| | | if (inPolygon){ |
| | | flag = true; |
| | | } |
| | | return flag; |
| | | } |
| | | |
| | | |
| | | /** |
| | | // * 判断点是否在多边形内 |
| | | // * @param px 测试点 |
| | | // * @param polygonXA 多边形的点 |
| | | // * @return boolean true:在多边形内, false:在多边形外 |
| | | // * @throws |
| | | // * @Title: IsPointInPoly |
| | | // */ |
| | | public static boolean isInPolygon(double px, double py,List<Double> polygonXA, List<Double> polygonYA) { |
| | | boolean isInside = false; |
| | | double ESP = 1e-9; |
| | | int count = 0; |
| | | double linePoint1x; |
| | | double linePoint1y; |
| | | double linePoint2x = 180; |
| | | double linePoint2y; |
| | | |
| | | linePoint1x = px; |
| | | linePoint1y = py; |
| | | linePoint2y = py; |
| | | |
| | | for (int i = 0; i < polygonXA.size() - 1; i++) { |
| | | double cx1 = polygonXA.get(i); |
| | | double cy1 = polygonYA.get(i); |
| | | double cx2 = polygonXA.get(i + 1); |
| | | double cy2 = polygonYA.get(i + 1); |
| | | if (isPointOnLine(px, py, cx1, cy1, cx2, cy2)) { |
| | | return true; |
| | | } |
| | | if (Math.abs(cy2 - cy1) < ESP) { |
| | | continue; |
| | | } |
| | | |
| | | if (isPointOnLine(cx1, cy1, linePoint1x, linePoint1y, linePoint2x, |
| | | linePoint2y)) { |
| | | if (cy1 > cy2) |
| | | count++; |
| | | } else if (isPointOnLine(cx2, cy2, linePoint1x, linePoint1y, |
| | | linePoint2x, linePoint2y)) { |
| | | if (cy2 > cy1) |
| | | count++; |
| | | } else if (isIntersect(cx1, cy1, cx2, cy2, linePoint1x, |
| | | linePoint1y, linePoint2x, linePoint2y)) { |
| | | count++; |
| | | } |
| | | } |
| | | if (count % 2 == 1) { |
| | | isInside = true; |
| | | } |
| | | |
| | | return isInside; |
| | | } |
| | | public static double Multiply(double px0, double py0, double px1, double py1, |
| | | double px2, double py2) { |
| | | return ((px1 - px0) * (py2 - py0) - (px2 - px0) * (py1 - py0)); |
| | | } |
| | | |
| | | public static boolean isPointOnLine(double px0, double py0, double px1, |
| | | double py1, double px2, double py2) { |
| | | boolean flag = false; |
| | | double ESP = 1e-9; |
| | | if ((Math.abs(Multiply(px0, py0, px1, py1, px2, py2)) < ESP) |
| | | && ((px0 - px1) * (px0 - px2) <= 0) |
| | | && ((py0 - py1) * (py0 - py2) <= 0)) { |
| | | flag = true; |
| | | } |
| | | return flag; |
| | | } |
| | | |
| | | public static boolean isIntersect(double px1, double py1, double px2, double py2, |
| | | double px3, double py3, double px4, double py4) { |
| | | boolean flag = false; |
| | | double d = (px2 - px1) * (py4 - py3) - (py2 - py1) * (px4 - px3); |
| | | if (d != 0) { |
| | | double r = ((py1 - py3) * (px4 - px3) - (px1 - px3) * (py4 - py3)) |
| | | / d; |
| | | double s = ((py1 - py3) * (px2 - px1) - (px1 - px3) * (py2 - py1)) |
| | | / d; |
| | | if ((r >= 0) && (r <= 1) && (s >= 0) && (s <= 1)) { |
| | | flag = true; |
| | | } |
| | | } |
| | | return flag; |
| | | } |
| | | |
| | | } |
| File was renamed from xp-service/xp-operation/src/main/java/com/xiaopeng/operation/utils/map/GDMapGeocodingUtil.java |
| | |
| | | package com.xiaopeng.operation.utils.map; |
| | | package com.xiaopeng.map.utils; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.xiaopeng.api.dto.operation.LonLatDto; |
| | | import com.xiaopeng.api.vo.operation.RouteVo; |
| | | import com.xiaopeng.map.utils.mangoUtils.MongoGeoFenceUtil; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.web.client.RestTemplate; |
| | |
| | | |
| | | @Autowired |
| | | private RestTemplate restTemplate; |
| | | |
| | | @Autowired |
| | | private MongoGeoFenceUtil mongoGeoFenceUtil; |
| | | |
| | | |
| | | /** |
| | |
| | | return map; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 根据起始点获取驾车距离 和 时间 以及避让区域 |
| | | * |
| | | * @return |
| | | * @throws Exception |
| | | */ |
| | | public RouteVo getDistanceAndTime(LonLatDto start, LonLatDto end,List<LonLatDto> avoidRoads,String fenceId) { |
| | | RouteVo routeVo = new RouteVo(); |
| | | |
| | | String url = "https://tsapi.amap.com/v1/route/price?key=" + key + "&extensions=all&start=" + start.getLongitude() + "," + start.getLatitude(); |
| | | HashMap<String, Object> params = new HashMap<>(); |
| | | params.put("extensions", "all"); |
| | | params.put("start", start.getLongitude() + "," + start.getLatitude()); |
| | | params.put("end", end.getLongitude() + "," + end.getLatitude()); |
| | | params.put("routeUserType",2); |
| | | params.put("voidPolygons", JSON.toJSONString(avoidRoads)); |
| | | |
| | | String forObject = restTemplate.getForObject(url, String.class, params); |
| | | JSONObject jsonObject = JSON.parseObject(forObject); |
| | | String data = jsonObject.getString("data"); |
| | | |
| | | JSONObject jsonObject1 = JSON.parseObject(data); |
| | | String routeID = jsonObject1.getString("routeID"); |
| | | JSONArray path = jsonObject1.getJSONArray("path"); |
| | | for (Object o : path) { |
| | | JSONObject jsonObject2 = (JSONObject) o; |
| | | |
| | | String points = jsonObject2.getString("points"); |
| | | String[] split = points.split(";"); |
| | | int temp=0; |
| | | // 取5分之一的点位 |
| | | for (int i = 0; i < split.length; i++) { |
| | | if (i % 5 == 0) { |
| | | String[] split1 = split[i].split(","); |
| | | LonLatDto lonLatDto = new LonLatDto(Double.parseDouble(split1[0]), Double.parseDouble(split1[1])); |
| | | if (!mongoGeoFenceUtil.isInFence(lonLatDto.getLongitude(), lonLatDto.getLatitude(), fenceId, "operating_area")) { |
| | | temp++; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | if(temp==0){ |
| | | Integer costTime = jsonObject2.getInteger("costTime"); |
| | | Integer distance = jsonObject2.getInteger("distance"); |
| | | routeVo.setDistance(distance); |
| | | routeVo.setDuration(costTime); |
| | | ArrayList<LonLatDto> lonLatDtos = new ArrayList<>(split.length); |
| | | for (String s : split) { |
| | | String[] split1 = s.split(","); |
| | | LonLatDto lonLatDto = new LonLatDto(Double.parseDouble(split1[0]), Double.parseDouble(split1[1])); |
| | | lonLatDtos.add(lonLatDto); |
| | | } |
| | | routeVo.setPolyline(lonLatDtos); |
| | | routeVo.setRouteID(routeID); |
| | | return routeVo; |
| | | } |
| | | } |
| | | |
| | | return null; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.map.utils.mangoUtils; |
| | | |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.mongodb.client.*; |
| | | import com.mongodb.client.model.Filters; |
| | | import com.mongodb.client.model.geojson.Point; |
| | | import com.mongodb.client.model.geojson.Position; |
| | | import org.bson.Document; |
| | | import org.bson.conversions.Bson; |
| | | |
| | | import java.util.*; |
| | | |
| | | /** |
| | | * MongoDB地理空间查询工具类 |
| | | */ |
| | | public class MongoGeoFenceUtil { |
| | | |
| | | private final MongoClient mongoClient; |
| | | private final MongoDatabase database; |
| | | private final ObjectMapper objectMapper; |
| | | |
| | | /** |
| | | * 地理坐标点 |
| | | */ |
| | | public static class GeoPoint { |
| | | private final double longitude; |
| | | private final double latitude; |
| | | |
| | | public GeoPoint(double longitude, double latitude) { |
| | | this.longitude = longitude; |
| | | this.latitude = latitude; |
| | | } |
| | | |
| | | public double getLongitude() { |
| | | return longitude; |
| | | } |
| | | |
| | | public double getLatitude() { |
| | | return latitude; |
| | | } |
| | | |
| | | public Position toPosition() { |
| | | return new Position(longitude, latitude); |
| | | } |
| | | |
| | | public Point toGeoJsonPoint() { |
| | | return new Point(new Position(longitude, latitude)); |
| | | } |
| | | |
| | | public Document toDocument() { |
| | | return new Document() |
| | | .append("type", "Point") |
| | | .append("coordinates", Arrays.asList(longitude, latitude)); |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return String.format("[%.6f, %.6f]", longitude, latitude); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 电子围栏类型 |
| | | */ |
| | | public enum FenceType { |
| | | CIRCLE, // 圆形 |
| | | POLYGON, // 多边形 |
| | | LINESTRING, // 线形 |
| | | MULTI_POLYGON // 多个多边形 |
| | | } |
| | | |
| | | /** |
| | | * 电子围栏 |
| | | */ |
| | | public static class GeoFence { |
| | | private String id; |
| | | private String name; |
| | | private FenceType type; |
| | | private List<GeoPoint> coordinates; |
| | | private GeoPoint center; |
| | | private double radius; // 半径(米) |
| | | private Map<String, Object> properties; |
| | | |
| | | public GeoFence(String id, String name, FenceType type) { |
| | | this.id = id; |
| | | this.name = name; |
| | | this.type = type; |
| | | this.coordinates = new ArrayList<>(); |
| | | this.properties = new HashMap<>(); |
| | | } |
| | | |
| | | // Getters and Setters |
| | | public String getId() { |
| | | return id; |
| | | } |
| | | |
| | | public void setId(String id) { |
| | | this.id = id; |
| | | } |
| | | |
| | | public String getName() { |
| | | return name; |
| | | } |
| | | |
| | | public void setName(String name) { |
| | | this.name = name; |
| | | } |
| | | |
| | | public FenceType getType() { |
| | | return type; |
| | | } |
| | | |
| | | public void setType(FenceType type) { |
| | | this.type = type; |
| | | } |
| | | |
| | | public List<GeoPoint> getCoordinates() { |
| | | return coordinates; |
| | | } |
| | | |
| | | public void setCoordinates(List<GeoPoint> coordinates) { |
| | | this.coordinates = coordinates; |
| | | } |
| | | |
| | | public GeoPoint getCenter() { |
| | | return center; |
| | | } |
| | | |
| | | public void setCenter(GeoPoint center) { |
| | | this.center = center; |
| | | } |
| | | |
| | | public double getRadius() { |
| | | return radius; |
| | | } |
| | | |
| | | public void setRadius(double radius) { |
| | | this.radius = radius; |
| | | } |
| | | |
| | | public Map<String, Object> getProperties() { |
| | | return properties; |
| | | } |
| | | |
| | | public void setProperties(Map<String, Object> properties) { |
| | | this.properties = properties; |
| | | } |
| | | |
| | | public void addCoordinate(GeoPoint point) { |
| | | this.coordinates.add(point); |
| | | } |
| | | |
| | | public void addCoordinate(double lng, double lat) { |
| | | this.coordinates.add(new GeoPoint(lng, lat)); |
| | | } |
| | | |
| | | public void addProperty(String key, Object value) { |
| | | this.properties.put(key, value); |
| | | } |
| | | } |
| | | |
| | | public MongoGeoFenceUtil(String connectionString, String databaseName) { |
| | | this.mongoClient = MongoClients.create(connectionString); |
| | | this.database = mongoClient.getDatabase(databaseName); |
| | | this.objectMapper = new ObjectMapper(); |
| | | } |
| | | |
| | | /** |
| | | * 创建地理空间索引 |
| | | */ |
| | | public void create2dSphereIndex(String collectionName, String fieldName) { |
| | | MongoCollection<Document> collection = database.getCollection(collectionName); |
| | | collection.createIndex(new Document(fieldName, "2dsphere")); |
| | | System.out.println("创建2dsphere索引成功: " + collectionName + "." + fieldName); |
| | | } |
| | | |
| | | /** |
| | | * 创建2d索引(平面坐标) |
| | | */ |
| | | public void create2dIndex(String collectionName, String fieldName) { |
| | | MongoCollection<Document> collection = database.getCollection(collectionName); |
| | | collection.createIndex(new Document(fieldName, "2d")); |
| | | System.out.println("创建2d索引成功: " + collectionName + "." + fieldName); |
| | | } |
| | | |
| | | /** |
| | | * 保存电子围栏到数据库 |
| | | */ |
| | | public void saveGeoFence(String collectionName, GeoFence fence) { |
| | | MongoCollection<Document> collection = database.getCollection(collectionName); |
| | | |
| | | Document geometry = createGeometryDocument(fence); |
| | | |
| | | Document fenceDoc = new Document() |
| | | .append("_id", fence.getId()) |
| | | .append("name", fence.getName()) |
| | | .append("type", fence.getType().name()) |
| | | .append("geometry", geometry) |
| | | .append("properties", fence.getProperties()) |
| | | .append("createdAt", new Date()) |
| | | .append("updatedAt", new Date()); |
| | | |
| | | // 添加中心点用于快速查询 |
| | | if (fence.getCenter() != null) { |
| | | fenceDoc.append("center", fence.getCenter().toDocument()); |
| | | } |
| | | |
| | | if (fence.getRadius() > 0) { |
| | | fenceDoc.append("radius", fence.getRadius()); |
| | | } |
| | | |
| | | collection.insertOne(fenceDoc); |
| | | System.out.println("保存围栏成功: " + fence.getName()); |
| | | } |
| | | |
| | | /** |
| | | * 创建几何图形文档 |
| | | */ |
| | | private Document createGeometryDocument(GeoFence fence) { |
| | | Document geometry = new Document(); |
| | | |
| | | switch (fence.getType()) { |
| | | case POLYGON: |
| | | geometry.append("type", "Polygon"); |
| | | List<List<Position>> polygonPositions = new ArrayList<>(); |
| | | List<Position> positions = new ArrayList<>(); |
| | | |
| | | for (GeoPoint point : fence.getCoordinates()) { |
| | | positions.add(point.toPosition()); |
| | | } |
| | | // 闭合多边形:最后一个点等于第一个点 |
| | | if (!positions.isEmpty() && !positions.get(0).equals(positions.get(positions.size() - 1))) { |
| | | positions.add(positions.get(0)); |
| | | } |
| | | |
| | | polygonPositions.add(positions); |
| | | geometry.append("coordinates", polygonPositions); |
| | | break; |
| | | |
| | | case CIRCLE: |
| | | // MongoDB原生不支持圆形,使用多边形近似 |
| | | geometry.append("type", "Polygon"); |
| | | List<List<Position>> circlePositions = new ArrayList<>(); |
| | | List<Position> circlePoints = new ArrayList<>(); |
| | | |
| | | int segments = 36; // 36边形近似圆形 |
| | | for (int i = 0; i < segments; i++) { |
| | | double angle = 2 * Math.PI * i / segments; |
| | | double lat = fence.getCenter().getLatitude() + |
| | | (fence.getRadius() / 111320.0) * Math.cos(angle); // 1度≈111320米 |
| | | double lng = fence.getCenter().getLongitude() + |
| | | (fence.getRadius() / (111320.0 * Math.cos(Math.toRadians(fence.getCenter().getLatitude())))) * Math.sin(angle); |
| | | |
| | | circlePoints.add(new Position(lng, lat)); |
| | | } |
| | | // 闭合 |
| | | if (!circlePoints.isEmpty()) { |
| | | circlePoints.add(circlePoints.get(0)); |
| | | } |
| | | |
| | | circlePositions.add(circlePoints); |
| | | geometry.append("coordinates", circlePositions); |
| | | break; |
| | | |
| | | case LINESTRING: |
| | | geometry.append("type", "LineString"); |
| | | List<Position> linePositions = new ArrayList<>(); |
| | | for (GeoPoint point : fence.getCoordinates()) { |
| | | linePositions.add(point.toPosition()); |
| | | } |
| | | geometry.append("coordinates", linePositions); |
| | | break; |
| | | |
| | | default: |
| | | throw new IllegalArgumentException("不支持的围栏类型: " + fence.getType()); |
| | | } |
| | | |
| | | return geometry; |
| | | } |
| | | |
| | | /** |
| | | * 方法1:使用$geoWithin判断点是否在多边形内 |
| | | */ |
| | | public boolean isPointInPolygonGeoWithin(GeoPoint point, String fenceId, String collectionName) { |
| | | MongoCollection<Document> collection = database.getCollection(collectionName); |
| | | |
| | | // 获取围栏的多边形数据 |
| | | Document fence = collection.find(Filters.eq("_id", fenceId)).first(); |
| | | if (fence == null) { |
| | | throw new IllegalArgumentException("围栏不存在: " + fenceId); |
| | | } |
| | | |
| | | // 创建查询:点是否在多边形内 |
| | | Bson filter = Filters.and( |
| | | Filters.eq("_id", fenceId), |
| | | Filters.geoWithin( |
| | | "geometry", |
| | | point.toGeoJsonPoint() |
| | | ) |
| | | ); |
| | | |
| | | long count = collection.countDocuments(filter); |
| | | return count > 0; |
| | | } |
| | | |
| | | /** |
| | | * 方法2:使用$geoIntersects判断点是否与几何图形相交 |
| | | */ |
| | | public boolean isPointInPolygonGeoIntersects(GeoPoint point, String fenceId, String collectionName) { |
| | | MongoCollection<Document> collection = database.getCollection(collectionName); |
| | | |
| | | Bson filter = Filters.and( |
| | | Filters.eq("_id", fenceId), |
| | | Filters.geoIntersects("geometry", point.toGeoJsonPoint()) |
| | | ); |
| | | |
| | | long count = collection.countDocuments(filter); |
| | | return count > 0; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 方法3:查找包含指定点的所有围栏(支持自定义地理字段名称) |
| | | */ |
| | | public List<String> findFencesContainingPoint(GeoPoint point, String collectionName, String geoFieldName) { |
| | | MongoCollection<Document> collection = database.getCollection(collectionName); |
| | | List<String> fenceIds = new ArrayList<>(); |
| | | |
| | | // 查询所有包含该点的围栏 |
| | | Bson filter = Filters.geoIntersects(geoFieldName, point.toGeoJsonPoint()); |
| | | |
| | | collection.find(filter).forEach(doc -> { |
| | | fenceIds.add( doc.getString("_id")); |
| | | }); |
| | | |
| | | return fenceIds; |
| | | } |
| | | |
| | | |
| | | public List<Document> findPointsInFence(String fenceId, |
| | | String pointsCollectionName, |
| | | String fencesCollectionName) { |
| | | // 1. 从围栏集合获取围栏的几何数据 |
| | | Document fence = getFenceGeometry(fenceId, fencesCollectionName); |
| | | if (fence == null) { |
| | | throw new IllegalArgumentException("围栏不存在: " + fenceId); |
| | | } |
| | | |
| | | // 2. 构建地理空间查询 |
| | | Bson filter = Filters.geoWithin("location", fence.get("geometry", Document.class)); |
| | | |
| | | // 3. 执行查询 |
| | | MongoCollection<Document> pointsCollection = database.getCollection(pointsCollectionName); |
| | | List<Document> results = new ArrayList<>(); |
| | | |
| | | try (MongoCursor<Document> cursor = pointsCollection.find(filter).iterator()) { |
| | | while (cursor.hasNext()) { |
| | | results.add(cursor.next()); |
| | | } |
| | | } |
| | | |
| | | return results; |
| | | } |
| | | /** |
| | | * 获取围栏的几何数据 |
| | | */ |
| | | private Document getFenceGeometry(String fenceId, String fencesCollectionName) { |
| | | MongoCollection<Document> fencesCollection = database.getCollection(fencesCollectionName); |
| | | return fencesCollection.find(Filters.eq("_id", fenceId)).first(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 方法4:使用聚合管道进行复杂的地理查询 |
| | | */ |
| | | public Map<String, Object> analyzePointInFences(GeoPoint point, String collectionName) { |
| | | MongoCollection<Document> collection = database.getCollection(collectionName); |
| | | |
| | | List<Document> pipeline = Arrays.asList( |
| | | // 阶段1:筛选包含该点的围栏 |
| | | new Document("$match", |
| | | new Document("geometry", |
| | | new Document("$geoIntersects", |
| | | new Document("$geometry", point.toDocument()) |
| | | ) |
| | | ) |
| | | ), |
| | | // 阶段2:投影所需字段 |
| | | new Document("$project", |
| | | new Document("_id", 1) |
| | | .append("name", 1) |
| | | .append("type", 1) |
| | | .append("properties", 1) |
| | | .append("area", 1) |
| | | ), |
| | | // 阶段3:统计 |
| | | new Document("$group", |
| | | new Document("_id", null) |
| | | .append("totalFences", new Document("$sum", 1)) |
| | | .append("fences", new Document("$push", |
| | | new Document("id", "$_id") |
| | | .append("name", "$name") |
| | | .append("type", "$type") |
| | | )) |
| | | ) |
| | | ); |
| | | |
| | | Document result = collection.aggregate(pipeline).first(); |
| | | if (result != null) { |
| | | return result; |
| | | } |
| | | |
| | | return new Document("totalFences", 0) |
| | | .append("fences", new ArrayList<>()); |
| | | } |
| | | |
| | | /** |
| | | * 方法5:查找距离点最近的围栏 |
| | | */ |
| | | public Document findNearestFence(GeoPoint point, String collectionName, int maxDistance) { |
| | | MongoCollection<Document> collection = database.getCollection(collectionName); |
| | | |
| | | List<Document> pipeline = Arrays.asList( |
| | | // 阶段1:添加距离字段 |
| | | new Document("$geoNear", |
| | | new Document("near", point.toDocument()) |
| | | .append("distanceField", "distance") |
| | | .append("spherical", true) |
| | | .append("maxDistance", maxDistance) |
| | | .append("query", new Document("type", "Polygon")) // 只查询多边形 |
| | | .append("key", "geometry") |
| | | ), |
| | | // 阶段2:按距离排序 |
| | | new Document("$sort", new Document("distance", 1)), |
| | | // 阶段3:限制返回数量 |
| | | new Document("$limit", 1) |
| | | ); |
| | | |
| | | return collection.aggregate(pipeline).first(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 方法7:批量判断多个点是否在围栏内 |
| | | */ |
| | | public Map<GeoPoint, List<String>> batchCheckPointsInFences(List<GeoPoint> points, String collectionName) { |
| | | MongoCollection<Document> collection = database.getCollection(collectionName); |
| | | Map<GeoPoint, List<String>> result = new HashMap<>(); |
| | | |
| | | for (GeoPoint point : points) { |
| | | Bson filter = Filters.geoIntersects("geometry", point.toGeoJsonPoint()); |
| | | List<String> fenceNames = new ArrayList<>(); |
| | | |
| | | collection.find(filter).forEach(doc -> { |
| | | fenceNames.add(doc.getString("name")); |
| | | }); |
| | | |
| | | result.put(point, fenceNames); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 查找与指定点距离在一定范围内的围栏 |
| | | */ |
| | | public List<Document> findFencesNearPoint(GeoPoint point, double maxDistanceMeters, String collectionName) { |
| | | MongoCollection<Document> collection = database.getCollection(collectionName); |
| | | |
| | | // 将米转换为弧度(适用于球面几何) |
| | | double maxDistanceRadians = maxDistanceMeters / 6378100; // 地球半径 |
| | | |
| | | Bson filter = Filters.nearSphere("geometry", point.toGeoJsonPoint(), maxDistanceRadians, 0.0); |
| | | |
| | | List<Document> results = new ArrayList<>(); |
| | | collection.find(filter).into(results); |
| | | |
| | | return results; |
| | | } |
| | | |
| | | /** |
| | | * 创建圆形围栏(使用$centerSphere) |
| | | */ |
| | | public void createCircleFenceUsingCenterSphere(String fenceId, String name, GeoPoint center, |
| | | double radiusMeters, String collectionName) { |
| | | MongoCollection<Document> collection = database.getCollection(collectionName); |
| | | |
| | | // 将米转换为弧度 |
| | | double radiusRadians = radiusMeters / 6378100; |
| | | |
| | | Document fenceDoc = new Document() |
| | | .append("_id", fenceId) |
| | | .append("name", name) |
| | | .append("type", "Circle") |
| | | .append("center", Arrays.asList(center.getLongitude(), center.getLatitude())) |
| | | .append("radius", radiusRadians) // 以弧度存储 |
| | | .append("radiusMeters", radiusMeters) |
| | | .append("geometry", new Document() |
| | | .append("type", "Point") |
| | | .append("coordinates", Arrays.asList(center.getLongitude(), center.getLatitude()))) |
| | | .append("createdAt", new Date()); |
| | | |
| | | collection.insertOne(fenceDoc); |
| | | } |
| | | |
| | | /** |
| | | * 使用$centerSphere查询圆形区域内的点 |
| | | */ |
| | | public boolean isPointInCircleUsingCenterSphere(GeoPoint point, String fenceId, String collectionName) { |
| | | MongoCollection<Document> collection = database.getCollection(collectionName); |
| | | |
| | | Document fence = collection.find(Filters.eq("_id", fenceId)).first(); |
| | | if (fence == null) return false; |
| | | |
| | | List<Double> center = (List<Double>) fence.get("center"); |
| | | double radius = fence.getDouble("radius"); |
| | | |
| | | Bson filter = Filters.geoWithinCenterSphere( |
| | | "location", // 假设点存储在location字段 |
| | | center.get(0), // 经度 |
| | | center.get(1), // 纬度 |
| | | radius |
| | | ); |
| | | |
| | | // 查找该点是否在圆形内 |
| | | MongoCollection<Document> pointsCollection = database.getCollection("tracking_points"); |
| | | Document pointDoc = pointsCollection.find( |
| | | Filters.and( |
| | | Filters.eq("location.coordinates", |
| | | Arrays.asList(point.getLongitude(), point.getLatitude())), |
| | | filter |
| | | ) |
| | | ).first(); |
| | | |
| | | return pointDoc != null; |
| | | } |
| | | |
| | | /** |
| | | * 关闭连接 |
| | | */ |
| | | public void close() { |
| | | if (mongoClient != null) { |
| | | mongoClient.close(); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 查找指定父围栏ID的禁行区域点位 |
| | | * @param parentId 父围栏ID |
| | | * @param parentIdField 父围栏ID字段名 |
| | | * @param collectionName 集合名称 |
| | | * @return 禁行区域点位列表 |
| | | */ |
| | | public List<Document> findPointsByParentId(String parentId, String parentIdField, String collectionName) { |
| | | MongoCollection<Document> collection = database.getCollection(collectionName); |
| | | List<Document> points = new ArrayList<>(); |
| | | |
| | | // 查询父围栏ID等于指定值的所有文档 |
| | | Bson filter = Filters.eq(parentIdField, parentId); |
| | | |
| | | collection.find(filter).forEach(doc -> { |
| | | points.add(doc); |
| | | }); |
| | | |
| | | return points; |
| | | } |
| | | |
| | | /** |
| | | * 根据围栏ID获取电子围栏点位 |
| | | * @param fenceId 围栏ID |
| | | * @param collectionName 集合名称 |
| | | * @return 电子围栏点位 |
| | | */ |
| | | public Document getFenceById(String fenceId, String collectionName) { |
| | | MongoCollection<Document> collection = database.getCollection(collectionName); |
| | | return collection.find(Filters.eq("_id", fenceId)).first(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 根据经纬度点位判断是否在指定的电子围栏中 |
| | | * @param longitude 经度 |
| | | * @param latitude 纬度 |
| | | * @param fenceId 围栏ID |
| | | * @param collectionName 集合名称 |
| | | * @return 是否在围栏中 |
| | | */ |
| | | public boolean isInFence(double longitude, double latitude, String fenceId, String collectionName) { |
| | | // 创建GeoPoint对象 |
| | | MongoGeoFenceUtil.GeoPoint geoPoint = new MongoGeoFenceUtil.GeoPoint(longitude, latitude); |
| | | |
| | | // 调用MongoGeoFenceUtil的方法查询包含该点的围栏 |
| | | List<String> fences = findFencesContainingPoint(geoPoint, collectionName, "points"); |
| | | |
| | | // 检查查询结果中是否包含指定的fenceId |
| | | return fences.contains(fenceId); |
| | | } |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.map.vo; |
| | | |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | @ApiModel(value = "IsInOperationAreaVo对象", description = "判断是否在运营区域") |
| | | public class IsInOperationAreaVo { |
| | | |
| | | @ApiModelProperty(value = "是否在运营区域 \"inside\" — 在围栏内(允许服务) \"outside\" — 在围栏外(禁止服务)") |
| | | private String status="outsize"; |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.map.vo; |
| | | |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | @ApiModel("步行时间和距离Vo") |
| | | public class WalkTimeVo { |
| | | @ApiModelProperty("步行时间") |
| | | private String walkTime; |
| | | |
| | | @ApiModelProperty("步行距离") |
| | | private Integer walkDistance; |
| | | |
| | | @ApiModelProperty("状态 0不可下单 1可下单") |
| | | private Integer status=0; |
| | | |
| | | } |
| File was renamed from xp-service/xp-map/src/main/java/com/xiaopeng/map/vo/GeoFencingPolygon.java |
| | |
| | | * @date 2023/4/29 23:53 |
| | | */ |
| | | @Data |
| | | @Document(collection = "geo_fencing") |
| | | public class GeoFencingPolygon implements Serializable { |
| | | @Document(collection = "operating_area") |
| | | public class XpGeoFencingPolygon implements Serializable { |
| | | private static final long serialVersionUID = 4508868382007529971L; |
| | | |
| | | private String id; |
| | | /** |
| | | * 站点id |
| | | * 围栏id |
| | | */ |
| | | private Integer siteId; |
| | | private String fenceId; |
| | | /** |
| | | * 站点名称 |
| | | * 围栏名称 |
| | | */ |
| | | private String name; |
| | | |
| | | |
| | | |
| | | /** |
| | | * 多边形经纬度 |
| | | */ |
| | | @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) |
| | | private GeoJsonPolygon geoJsonPolygon; |
| | | private GeoJsonPolygon points; |
| | | |
| | | } |
| | |
| | | </description> |
| | | |
| | | <dependencies> |
| | | |
| | | |
| | | <dependency> |
| | | <groupId>com.github.stuxuhai</groupId> |
| | | <artifactId>jpinyin</artifactId> |
| | | <version>1.1.8</version> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>com.alibaba.cloud</groupId> |
| | | <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> |
| New file |
| | |
| | | package com.xiaopeng.operation.controller; |
| | | |
| | | |
| | | import com.xiaopeng.common.core.web.controller.BaseController; |
| | | import com.xiaopeng.common.core.web.domain.AjaxResult; |
| | | import com.xiaopeng.api.dto.map.RouteAndFareDto; |
| | | import com.xiaopeng.operation.service.XpPricingService; |
| | | import com.xiaopeng.api.vo.operation.RouteAndFareVo; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.validation.Valid; |
| | | |
| | | /** |
| | | * <p> |
| | | * 费项管理 前端控制器 |
| | | * </p> |
| | | * |
| | | * @author mitao |
| | | * @since 2026-02-03 |
| | | */ |
| | | |
| | | @Slf4j |
| | | @RestController |
| | | @RequestMapping("/base/v1/mini-program") |
| | | @Api(tags = "费用管理") |
| | | public class XpPricingController extends BaseController { |
| | | |
| | | private final XpPricingService xpPricingService; |
| | | |
| | | public XpPricingController(XpPricingService xpPricingService) { |
| | | this.xpPricingService = xpPricingService; |
| | | } |
| | | |
| | | |
| | | |
| | | @PostMapping("/navigation/route-and-fare") |
| | | @ApiOperation(value = "导航-获取路线和费用",tags = {"小程序-获取路线和费用"},response = RouteAndFareVo.class) |
| | | public AjaxResult<RouteAndFareVo> getRouteAndFare(@RequestBody @Valid RouteAndFareDto routeAndFareDto) { |
| | | return success(xpPricingService.getRouteAndFare(routeAndFareDto)); |
| | | } |
| | | |
| | | |
| | | |
| | | } |
| | |
| | | package com.xiaopeng.operation.controller; |
| | | |
| | | import com.xiaopeng.api.model.operation.SysOperLog; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.common.core.web.controller.BaseController; |
| | | import com.xiaopeng.common.core.web.domain.AjaxResult; |
| | | import com.xiaopeng.operation.service.XpSystemSettingService; |
| | |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.List; |
| | | |
| | |
| | | } |
| | | |
| | | |
| | | // @GetMapping("/support/getOrderDistance") |
| | | // @ApiOperation(value = "获取下单距离",tags = {"小程序首页-服务调用获取下单距离"},response = Integer.class) |
| | | // public AjaxResult<Integer> getOrderDistance() { |
| | | // return success(xpSystemSettingService.getOrderDistance()); |
| | | // } |
| | | |
| | | |
| | | @GetMapping("/support/getOrderDistance") |
| | | @ApiOperation(value = "获取下单距离",tags = {"小程序首页-服务调用获取下单距离"},response = Integer.class) |
| | | public R getOrderDistance() { |
| | | return R.ok(xpSystemSettingService.getOrderDistance()); |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.xiaopeng.api.model.operation.XpAgreementAppUser; |
| | | import com.xiaopeng.api.model.maintenance.XpAgreementAppUser; |
| | | |
| | | /** |
| | | * <p> |
| | |
| | | |
| | | |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.xiaopeng.api.model.maintenance.XpAgreementAppUser; |
| | | import com.xiaopeng.operation.dto.XpAgreementAppUserDto; |
| | | import com.xiaopeng.api.model.operation.XpAgreementAppUser; |
| | | |
| | | /** |
| | | * <p> |
| | |
| | | |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.xiaopeng.api.model.operation.XpPricing; |
| | | import com.xiaopeng.api.dto.map.RouteAndFareDto; |
| | | import com.xiaopeng.api.vo.operation.RouteAndFareVo; |
| | | |
| | | /** |
| | | * <p> |
| | |
| | | */ |
| | | public interface XpPricingService extends IService<XpPricing> { |
| | | |
| | | RouteAndFareVo getRouteAndFare(RouteAndFareDto routeAndFareDto); |
| | | |
| | | |
| | | |
| | | } |
| | |
| | | */ |
| | | String getCustomerServicePhone(); |
| | | |
| | | /** |
| | | * 获取距离点X米内可下单 |
| | | * @return |
| | | */ |
| | | Integer getOrderDistance(); |
| | | |
| | | |
| | | |
| | | } |
| | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.xiaopeng.api.model.operation.XpPoint; |
| | | import com.xiaopeng.api.model.operation.XpSystemSetting; |
| | | import com.xiaopeng.operation.dto.NearestDto; |
| | | import com.xiaopeng.common.core.web.page.PageInfo; |
| | | import com.xiaopeng.operation.mapper.XpPointMapper; |
| | | import com.xiaopeng.operation.mapper.XpSystemSettingMapper; |
| | | import com.xiaopeng.operation.query.PointMapQuery; |
| | | import com.xiaopeng.operation.service.XpPointService; |
| | | import com.xiaopeng.operation.utils.map.FastDistanceCalculator; |
| | |
| | | import com.xiaopeng.operation.vo.NearestVo; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.util.ArrayList; |
| | | import java.util.Comparator; |
| | | import java.util.List; |
| | |
| | | @Service |
| | | public class XpPointServiceImpl extends ServiceImpl<XpPointMapper, XpPoint> implements XpPointService { |
| | | |
| | | @Resource |
| | | private XpSystemSettingMapper xpSystemSettingMapper; |
| | | |
| | | |
| | | |
| | | @Override |
| | |
| | | List<XpPoint> list = list(new LambdaQueryWrapper<XpPoint>().eq(XpPoint::getOperationStatus, 1)); |
| | | NearestVo nearestVo = new NearestVo(); |
| | | List<NearestNextVo> nearestNextVos = new ArrayList<>(); |
| | | |
| | | XpSystemSetting xpSystemSetting = xpSystemSettingMapper.selectOne(null); |
| | | Integer orderDistance = xpSystemSetting.getOrderDistance(); |
| | | for (XpPoint xpPoint : list) { |
| | | // 计算距离 小于1000m的站点 |
| | | // 计算距离 小于的站点 |
| | | double v = FastDistanceCalculator.distanceFast(dto.getLatitude(), dto.getLongitude(), xpPoint.getPointLat(), xpPoint.getPointLon()); |
| | | if (v < 1000) { |
| | | if (v < orderDistance) { |
| | | NearestNextVo nearestNextVo = new NearestNextVo(); |
| | | nearestNextVo.setId(xpPoint.getId()); |
| | | nearestNextVo.setName(xpPoint.getPointName()); |
| | |
| | | package com.xiaopeng.operation.service.impl; |
| | | |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.xiaopeng.api.dto.map.RouteAndFareDto; |
| | | import com.xiaopeng.api.feignClient.map.XpMapClient; |
| | | import com.xiaopeng.api.vo.map.XpDistanceAndDurationVo; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.operation.mapper.XpPricingMapper; |
| | | import com.xiaopeng.api.model.operation.XpPricing; |
| | | import com.xiaopeng.operation.service.XpPricingService; |
| | | import com.xiaopeng.operation.utils.XpPricingUtil; |
| | | import com.xiaopeng.api.vo.operation.RouteAndFareVo; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.annotation.Resource; |
| | | |
| | | /** |
| | | * <p> |
| | |
| | | @Service |
| | | public class XpPricingServiceImpl extends ServiceImpl<XpPricingMapper, XpPricing> implements XpPricingService { |
| | | |
| | | |
| | | @Resource |
| | | private XpPricingUtil xpPricingUtil; |
| | | |
| | | @Resource |
| | | private XpMapClient xpMapClient; |
| | | |
| | | |
| | | @Override |
| | | public RouteAndFareVo getRouteAndFare(RouteAndFareDto routeAndFareDto) { |
| | | XpPricing xpPricing = this.baseMapper.selectOne(new LambdaQueryWrapper<XpPricing>().eq(XpPricing::getCityId, routeAndFareDto.getOpenCityId())); |
| | | if(xpPricing==null){ |
| | | // 抛出异常 |
| | | throw new RuntimeException("当前区域未开通服务"); |
| | | } |
| | | |
| | | // 判断2个点是否在同一个经营区域 |
| | | R<Boolean> r = xpMapClient.diagnosisArea(routeAndFareDto); |
| | | if(r.getCode()!=R.SUCCESS){ |
| | | // 抛出异常 |
| | | throw new RuntimeException(r.getMsg()); |
| | | } |
| | | |
| | | if(!r.getData()){ |
| | | // 抛出异常 |
| | | throw new RuntimeException("当前区域未开通服务"); |
| | | } |
| | | |
| | | |
| | | // 调用地图 |
| | | R<XpDistanceAndDurationVo> distanceAndDuration = xpMapClient.getDistanceAndDuration(routeAndFareDto); |
| | | |
| | | |
| | | xpPricingUtil.calculationPrice(xpPricing.getBasicPricingJson(), xpPricing.getStartingFareJson(), routeAndFareDto.getDistance(), routeAndFareDto.getDuration(), routeAndFareDto.getWait(), routeAndFareDto.getWaitMoney()) |
| | | |
| | | |
| | | return null; |
| | | } |
| | | } |
| | |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | @Override |
| | | public Integer getOrderDistance() { |
| | | XpSystemSetting xpSystemSetting = this.getOne(null); |
| | | if (xpSystemSetting != null) { |
| | | return xpSystemSetting.getOrderDistance(); |
| | | } |
| | | return 0; |
| | | } |
| | | } |
| | |
| | | package com.xiaopeng.operation.utils; |
| | | |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.xiaopeng.operation.service.XpPricingService; |
| | | import com.xiaopeng.operation.utils.map.GDMapElectricFenceUtil; |
| | | import com.xiaopeng.operation.utils.holiday.HolidayUtil; |
| | | import com.xiaopeng.operation.utils.map.GDMapGeocodingUtil; |
| | | import com.xiaopeng.operation.vo.XpPricingCardVo; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | |
| | | public class XpPricingUtil { |
| | | |
| | | @Autowired |
| | | private GDMapGeocodingUtil gdMapGeocodingUtil; |
| | | |
| | | @Autowired |
| | | private GDMapElectricFenceUtil gdMapElectricFenceUtil; |
| | | |
| | | @Autowired |
| | | private XpPricingService xpPricingService; |
| | | |
| | | // public void queryServerCarModel(String startLonLat, String endLonLat, Integer type) throws Exception { |
| | | // //查找与起点匹配的企业(经营范围) |
| | | //// Map<String, String> geocode = gdMapGeocodingUtil.geocode(startLonLat.split(",")[0], startLonLat.split(",")[1]); |
| | | // |
| | | // |
| | | // // TODO: 2023/11/4 无法修改 |
| | | // Map<String, String> distance = gdMapElectricFenceUtil.getDistance(startLonLat, endLonLat, 1); |
| | | // if (null == distance) { |
| | | // return; |
| | | // } |
| | | // String distance1 = distance.get("distance");//距离(米) |
| | | // double distance1_ = Double.valueOf(distance1).doubleValue(); |
| | | // String duration = distance.get("duration");//时间(秒) |
| | | // long duration_ = Long.valueOf(duration).longValue(); |
| | | // |
| | | // |
| | | // List<ServerCarModelWarpper> price = this.getPrice(query.getId(), distance1_, duration_, 0, 1); |
| | | // //添加出租车车型和价格 |
| | | // ServerCarModelWarpper serverCarModelWarpper = this.getTextPrice(query.getId(), distance1_, duration_, 0, 2); |
| | | // price.add(serverCarModelWarpper); |
| | | // return ResultUtil.success(price); |
| | | // } |
| | | // |
| | | // |
| | | // /** |
| | | // * |
| | | // * @param distance 米 |
| | | // * @param duration 秒 |
| | | // * @param cityId |
| | | // * @return |
| | | // * @throws Exception |
| | | // */ |
| | | // public ServerCarModelWarpper getPrice(double distance, long duration, Long cityId) throws Exception { |
| | | // XpPricing xpPricing = xpPricingService.getOne(new LambdaQueryWrapper<XpPricing>().eq(XpPricing::getCityId, cityId)); |
| | | // if (null == xpPricing) { |
| | | // return null; |
| | | // } |
| | | // BigDecimal money =BigDecimal.ZERO; |
| | | // String basicPricingJson = xpPricing.getBasicPricingJson(); |
| | | // JSONObject jsonObject = JSON.parseObject(basicPricingJson); |
| | | // Double v2 = calculationPrice(jsonObject, distance, duration, 0, 0.0); |
| | | // return v2; |
| | | // } |
| | | private HolidayUtil holidayUtil; |
| | | |
| | | |
| | | public Double calculationPrice(JSONObject rule, double distance, long duration, long wait,Double waitMoney) throws Exception { |
| | | |
| | | /** |
| | | * 计算基础价格 |
| | | * @param rule |
| | | * @param distance |
| | | * @param duration |
| | | * @param wait |
| | | * @param waitMoney |
| | | * @return |
| | | * @throws Exception |
| | | */ |
| | | public XpPricingCardVo calculationPrice(JSONObject rule, JSONArray startingFareJson, double distance, long duration, long wait, Double waitMoney) throws Exception { |
| | | XpPricingCardVo xpPricingCardVo = new XpPricingCardVo(); |
| | | double amount = 0; |
| | | //等待费 |
| | | Date date = new Date(); |
| | | double d = distance / 1000;//实际公里 |
| | | double t = duration / 60;//实际时间 |
| | | double d1 = (d - rule.getDouble("num2")) < 0 ? 0 : d - rule.getDouble("num2");//超出起步里程的公里 |
| | | double t1 = (t - rule.getDouble("num3")) < 0 ? 0 : t - rule.getDouble("num3");//超过起步分钟数的时间 |
| | | double yt1 = 0;//远途1段 |
| | | double yt2 = 0;//远途2段 |
| | | double yt3 = 0;//远途3段 |
| | | // 计算出是在那个时间段内 |
| | | for (Object o : startingFareJson) { |
| | | JSONObject startingFare = (JSONObject) o; |
| | | Calendar calendarStart = Calendar.getInstance(); |
| | | calendarStart.setTime(date); |
| | | calendarStart.set(Calendar.HOUR_OF_DAY, Integer.valueOf(startingFare.getString("num1").split(":")[0])); |
| | | calendarStart.set(Calendar.MINUTE, Integer.valueOf(startingFare.getString("num1").split(":")[1])); |
| | | |
| | | Calendar calendarEnd = Calendar.getInstance(); |
| | | calendarEnd.setTime(date); |
| | | calendarEnd.set(Calendar.HOUR_OF_DAY, Integer.valueOf(startingFare.getString("num2").split(":")[0])); |
| | | calendarEnd.set(Calendar.MINUTE, Integer.valueOf(startingFare.getString("num2").split(":")[1])); |
| | | // 找到符合条件的直接跳出循环 |
| | | if (date.getTime() > calendarStart.getTimeInMillis() && date.getTime() < calendarEnd.getTimeInMillis()) { |
| | | rule.put("num1", startingFare.get("num3")); |
| | | rule.put("num2", startingFare.get("num4")); |
| | | rule.put("num3", startingFare.get("num5")); |
| | | rule.put("num4", startingFare.get("num6")); |
| | | rule.put("num5", startingFare.get("num7")); |
| | | |
| | | rule.put("startTime", startingFare.getString("num1")); |
| | | rule.put("endTime", startingFare.getString("num2")); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | |
| | | double realDistance = distance / 1000;//实际公里 |
| | | double realTime = duration / 60;//实际时间 |
| | | double exceedDistance = (realDistance - rule.getDouble("num2")) < 0 ? 0 : realDistance - rule.getDouble("num2");//超出起步里程的公里 |
| | | double exceedTime = (realTime - rule.getDouble("num3")) < 0 ? 0 : realTime - rule.getDouble("num3");//超过起步分钟数的时间 |
| | | double longDistanceOne = 0;//远途1段 |
| | | double longDistanceTwo = 0;//远途2段 |
| | | double longDistanceThree = 0;//远途3段 |
| | | |
| | | |
| | | //夜间服务处理逻辑 |
| | | Calendar s = Calendar.getInstance(); |
| | | s.setTime(date); |
| | | s.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num14").split(" - ")[0].split(":")[0])); |
| | | s.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num14").split(" - ")[0].split(":")[1])); |
| | | Calendar calendarNight = Calendar.getInstance(); |
| | | calendarNight.setTime(date); |
| | | calendarNight.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num14").split(" - ")[0].split(":")[0])); |
| | | calendarNight.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num14").split(" - ")[0].split(":")[1])); |
| | | |
| | | Calendar e = Calendar.getInstance(); |
| | | e.setTime(date); |
| | | e.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num14").split(" - ")[1].split(":")[0])); |
| | | e.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num14").split(" - ")[1].split(":")[1])); |
| | | Calendar calendar = Calendar.getInstance(); |
| | | calendar.setTime(date); |
| | | calendar.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num14").split(" - ")[1].split(":")[0])); |
| | | calendar.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num14").split(" - ")[1].split(":")[1])); |
| | | |
| | | if(date.getTime() > s.getTimeInMillis() && date.getTime() < e.getTimeInMillis()){ |
| | | if(d > rule.getDouble("num6")){ |
| | | yt1 = rule.getDouble("num18") * (d - rule.getDouble("num6")); |
| | | // 先判断今日是否节假日 |
| | | boolean holiday = holidayUtil.isHoliday(); |
| | | if (holiday) { |
| | | if (realDistance > rule.getDouble("num6")) { |
| | | longDistanceOne = rule.getDouble("num32") * (realDistance - rule.getDouble("num6")); |
| | | } |
| | | if(d > rule.getDouble("num9")){ |
| | | yt2 = rule.getDouble("num19") * (d - rule.getDouble("num9")); |
| | | if (realDistance > rule.getDouble("num9")) { |
| | | longDistanceTwo = rule.getDouble("num33") * (realDistance - rule.getDouble("num9")); |
| | | } |
| | | if(d > rule.getDouble("num12")){ |
| | | yt3 = rule.getDouble("num20") * (d - rule.getDouble("num12")); |
| | | if (realDistance > rule.getDouble("num12")) { |
| | | longDistanceThree = rule.getDouble("num34") * (realDistance - rule.getDouble("num12")); |
| | | } |
| | | amount = rule.getDouble("num15") + (d1 * rule.getDouble("num16")) + (t1 * rule.getDouble("num17")) + (wait * waitMoney) + yt1 + yt2 + yt3; |
| | | }else{ |
| | | Calendar s1 = Calendar.getInstance(); |
| | | s1.setTime(date); |
| | | s1.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num21").split(" - ")[0].split(":")[0])); |
| | | s1.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num21").split(" - ")[0].split(":")[1])); |
| | | amount = rule.getDouble("num29") + (exceedDistance * rule.getDouble("num30")) + (exceedTime * rule.getDouble("num31")) + (wait * waitMoney) + longDistanceOne + longDistanceTwo + longDistanceThree; |
| | | |
| | | Calendar e1 = Calendar.getInstance(); |
| | | e1.setTime(date); |
| | | e1.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num21").split(" - ")[1].split(":")[0])); |
| | | e1.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num21").split(" - ")[1].split(":")[1])); |
| | | |
| | | Calendar s2 = Calendar.getInstance(); |
| | | s2.setTime(date); |
| | | s2.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num22").split(" - ")[0].split(":")[0])); |
| | | s2.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num22").split(" - ")[0].split(":")[1])); |
| | | // 起步价 |
| | | xpPricingCardVo.setStartPrice(rule.getDouble("num29")); |
| | | // 时间段 |
| | | xpPricingCardVo.setTime("节假日"); |
| | | |
| | | Calendar e2 = Calendar.getInstance(); |
| | | e2.setTime(date); |
| | | e2.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num22").split(" - ")[1].split(":")[0])); |
| | | e2.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num22").split(" - ")[1].split(":")[1])); |
| | | //高峰时段处理逻辑 |
| | | if((date.getTime() > s1.getTimeInMillis() && date.getTime() < e1.getTimeInMillis()) || (date.getTime() > s2.getTimeInMillis() && date.getTime() < e2.getTimeInMillis())){ |
| | | if(d > rule.getDouble("num6")){ |
| | | yt1 = rule.getDouble("num26") * (d - rule.getDouble("num6")); |
| | | |
| | | // 里程 |
| | | xpPricingCardVo.setDistance(realDistance); |
| | | |
| | | // 里程费 |
| | | double distancePrice = (exceedDistance * rule.getDouble("num30")) + longDistanceOne + longDistanceTwo + longDistanceThree; |
| | | xpPricingCardVo.setDistancePrice(distancePrice); |
| | | |
| | | // 时长 |
| | | xpPricingCardVo.setDuration(realTime); |
| | | |
| | | // 时长费 |
| | | xpPricingCardVo.setDurationPrice((exceedTime * rule.getDouble("num31"))); |
| | | |
| | | |
| | | } else { |
| | | if (date.getTime() > calendarNight.getTimeInMillis() && date.getTime() < calendar.getTimeInMillis()) { |
| | | if (realDistance > rule.getDouble("num6")) { |
| | | longDistanceOne = rule.getDouble("num18") * (realDistance - rule.getDouble("num6")); |
| | | } |
| | | if(d > rule.getDouble("num9")){ |
| | | yt2 = rule.getDouble("num27") * (d - rule.getDouble("num9")); |
| | | if (realDistance > rule.getDouble("num9")) { |
| | | longDistanceTwo = rule.getDouble("num19") * (realDistance - rule.getDouble("num9")); |
| | | } |
| | | if(d > rule.getDouble("num12")){ |
| | | yt3 = rule.getDouble("num28") * (d - rule.getDouble("num12")); |
| | | if (realDistance > rule.getDouble("num12")) { |
| | | longDistanceThree = rule.getDouble("num20") * (realDistance - rule.getDouble("num12")); |
| | | } |
| | | amount = rule.getDouble("num23") + (d1 * rule.getDouble("num24")) + (t1 * rule.getDouble("num25")) + (wait * waitMoney) + yt1 + yt2 + yt3; |
| | | }else{ |
| | | //其他时间段的计算 |
| | | if(d > rule.getDouble("num6")){ |
| | | yt1 = rule.getDouble("num8") * (d - rule.getDouble("num6")); |
| | | amount = rule.getDouble("num15") + (exceedDistance * rule.getDouble("num16")) + (exceedTime * rule.getDouble("num17")) + (wait * waitMoney) + longDistanceOne + longDistanceTwo + longDistanceThree; |
| | | |
| | | |
| | | // 起步价 |
| | | xpPricingCardVo.setStartPrice(rule.getDouble("num15")); |
| | | // 时间段 |
| | | xpPricingCardVo.setTime(rule.getString("num14")); |
| | | |
| | | // 里程 |
| | | xpPricingCardVo.setDistance(realDistance); |
| | | |
| | | // 里程费 |
| | | double distancePrice = (exceedDistance * rule.getDouble("num16")) + longDistanceOne + longDistanceTwo + longDistanceThree; |
| | | xpPricingCardVo.setDistancePrice(distancePrice); |
| | | |
| | | // 时长 |
| | | xpPricingCardVo.setDuration(realTime); |
| | | |
| | | // 时长费 |
| | | xpPricingCardVo.setDurationPrice((exceedTime * rule.getDouble("num17"))); |
| | | |
| | | |
| | | } else { |
| | | |
| | | // 高峰处理 |
| | | Calendar calendarStart = Calendar.getInstance(); |
| | | calendarStart.setTime(date); |
| | | calendarStart.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num21").split(" - ")[0].split(":")[0])); |
| | | calendarStart.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num21").split(" - ")[0].split(":")[1])); |
| | | |
| | | Calendar calendarEnd = Calendar.getInstance(); |
| | | calendarEnd.setTime(date); |
| | | calendarEnd.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num21").split(" - ")[1].split(":")[0])); |
| | | calendarEnd.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num21").split(" - ")[1].split(":")[1])); |
| | | |
| | | Calendar s2 = Calendar.getInstance(); |
| | | s2.setTime(date); |
| | | s2.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num22").split(" - ")[0].split(":")[0])); |
| | | s2.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num22").split(" - ")[0].split(":")[1])); |
| | | |
| | | Calendar e2 = Calendar.getInstance(); |
| | | e2.setTime(date); |
| | | e2.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num22").split(" - ")[1].split(":")[0])); |
| | | e2.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num22").split(" - ")[1].split(":")[1])); |
| | | //高峰时段处理逻辑 |
| | | if ((date.getTime() > calendarStart.getTimeInMillis() && date.getTime() < calendarEnd.getTimeInMillis()) || (date.getTime() > s2.getTimeInMillis() && date.getTime() < e2.getTimeInMillis())) { |
| | | if (realDistance > rule.getDouble("num6")) { |
| | | longDistanceOne = rule.getDouble("num26") * (realDistance - rule.getDouble("num6")); |
| | | } |
| | | if (realDistance > rule.getDouble("num9")) { |
| | | longDistanceTwo = rule.getDouble("num27") * (realDistance - rule.getDouble("num9")); |
| | | } |
| | | if (realDistance > rule.getDouble("num12")) { |
| | | longDistanceThree = rule.getDouble("num28") * (realDistance - rule.getDouble("num12")); |
| | | } |
| | | amount = rule.getDouble("num23") + (exceedDistance * rule.getDouble("num24")) + (exceedTime * rule.getDouble("num25")) + (wait * waitMoney) + longDistanceOne + longDistanceTwo + longDistanceThree; |
| | | |
| | | |
| | | // 起步价 |
| | | xpPricingCardVo.setStartPrice(rule.getDouble("num23")); |
| | | // 时间段 |
| | | if (date.getTime() > calendarStart.getTimeInMillis() && date.getTime() < calendarEnd.getTimeInMillis()) { |
| | | xpPricingCardVo.setTime(rule.getString("num21")); |
| | | } else { |
| | | xpPricingCardVo.setTime(rule.getString("num22")); |
| | | } |
| | | |
| | | |
| | | // 里程 |
| | | xpPricingCardVo.setDistance(realDistance); |
| | | |
| | | // 里程费 |
| | | double distancePrice = (exceedDistance * rule.getDouble("num24")) + longDistanceOne + longDistanceTwo + longDistanceThree; |
| | | xpPricingCardVo.setDistancePrice(distancePrice); |
| | | |
| | | // 时长 |
| | | xpPricingCardVo.setDuration(realTime); |
| | | |
| | | // 时长费 |
| | | xpPricingCardVo.setDurationPrice((exceedTime * rule.getDouble("num25"))); |
| | | |
| | | } else { |
| | | //其他时间段的计算 |
| | | if (realDistance > rule.getDouble("num6")) { |
| | | longDistanceOne = rule.getDouble("num8") * (realDistance - rule.getDouble("num6")); |
| | | } |
| | | if (realDistance > rule.getDouble("num9")) { |
| | | longDistanceTwo = rule.getDouble("num11") * (realDistance - rule.getDouble("num9")); |
| | | } |
| | | if (realDistance > rule.getDouble("num12")) { |
| | | longDistanceThree = rule.getDouble("num13") * (realDistance - rule.getDouble("num12")); |
| | | } |
| | | amount = rule.getDouble("num1") + (exceedDistance * rule.getDouble("num4")) + (exceedTime * rule.getDouble("num5")) + (wait * waitMoney) + longDistanceOne + longDistanceTwo + longDistanceThree; |
| | | |
| | | |
| | | // 起步价 |
| | | xpPricingCardVo.setStartPrice(rule.getDouble("num1")); |
| | | // 时间段 |
| | | xpPricingCardVo.setTime(rule.getString("startTime") + " - " + rule.getString("endTime")); |
| | | |
| | | xpPricingCardVo.setDistancePriceNormal(rule.getDouble("num2")); |
| | | |
| | | xpPricingCardVo.setDurationPriceNormal(rule.getDouble("num3")); |
| | | |
| | | |
| | | // 里程 |
| | | xpPricingCardVo.setDistance(realDistance); |
| | | |
| | | // 里程费 |
| | | double distancePrice = (exceedDistance * rule.getDouble("num4")) + longDistanceOne + longDistanceTwo + longDistanceThree; |
| | | xpPricingCardVo.setDistancePrice(distancePrice); |
| | | |
| | | // 时长 |
| | | xpPricingCardVo.setDuration(realTime); |
| | | |
| | | // 时长费 |
| | | xpPricingCardVo.setDurationPrice((exceedTime * rule.getDouble("num5"))); |
| | | |
| | | } |
| | | if(d > rule.getDouble("num9")){ |
| | | yt2 = rule.getDouble("num11") * (d - rule.getDouble("num9")); |
| | | } |
| | | if(d > rule.getDouble("num12")){ |
| | | yt3 = rule.getDouble("num13") * (d - rule.getDouble("num12")); |
| | | } |
| | | amount = rule.getDouble("num1") + (d1 * rule.getDouble("num4")) + (t1 * rule.getDouble("num5")) + (wait * waitMoney) + yt1 + yt2 + yt3; |
| | | } |
| | | } |
| | | return amount; |
| | | |
| | | // 订单总额 |
| | | xpPricingCardVo.setTotalPrice(amount); |
| | | |
| | | |
| | | return xpPricingCardVo; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.operation.utils.holiday; |
| | | |
| | | import cn.hutool.http.HttpUtil; |
| | | import com.alibaba.fastjson2.JSONArray; |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.data.redis.core.RedisTemplate; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.time.LocalDate; |
| | | |
| | | |
| | | |
| | | @Component |
| | | public class HolidayUtil { |
| | | |
| | | |
| | | @Autowired |
| | | private RedisTemplate redisTemplate; |
| | | |
| | | |
| | | public boolean isHoliday() { |
| | | LocalDate now = LocalDate.now(); |
| | | |
| | | // 判断缓存里面是否有节假日算法 |
| | | if (redisTemplate.opsForValue().get("isHoliday_"+now) != null && ("1").equals(redisTemplate.opsForValue().get("isHoliday_"+now).toString())) { |
| | | return true; |
| | | } |
| | | String result = HttpUtil.get("https://publicapi.xiaoai.me/holiday/day?date=" +now); |
| | | JSONObject jsonObject = JSONObject.parseObject(result); |
| | | Object data = jsonObject.get("data"); |
| | | JSONArray parse = JSONArray.parse(data.toString()); |
| | | for (int i = 0; i < parse.size(); i++) { |
| | | JSONObject jsonObject1 = parse.getJSONObject(i); |
| | | if (jsonObject1.get("daytype").toString().equals("1")) { |
| | | redisTemplate.opsForValue().set("isHoliday_"+now, "1", 24*60*60); |
| | | return true; |
| | | } |
| | | } |
| | | redisTemplate.opsForValue().set("isHoliday_"+now, "0", 24*60*60); |
| | | return false; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.operation.vo; |
| | | |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | |
| | | |
| | | @Data |
| | | @ApiModel(value = "XpPricingCardVo", description = "冒泡页价格卡片") |
| | | public class XpPricingCardVo { |
| | | |
| | | @ApiModelProperty(value = "价格总额") |
| | | private Double totalPrice; |
| | | |
| | | |
| | | @ApiModelProperty(value = "起步价格") |
| | | private Double startPrice; |
| | | |
| | | |
| | | @ApiModelProperty(value = "时间段") |
| | | private String time; |
| | | |
| | | |
| | | @ApiModelProperty(value = "距离") |
| | | private Double distance; |
| | | |
| | | |
| | | @ApiModelProperty(value = "时长") |
| | | private Double duration; |
| | | |
| | | |
| | | @ApiModelProperty(value = "里程费") |
| | | private Double distancePrice; |
| | | |
| | | @ApiModelProperty(value = "普通时段公里数") |
| | | private Double distancePriceNormal; |
| | | |
| | | |
| | | @ApiModelProperty(value = "时长费") |
| | | private Double durationPrice; |
| | | |
| | | @ApiModelProperty(value = "普通时段时长数") |
| | | private Double durationPriceNormal; |
| | | |
| | | @ApiModelProperty(value = "优惠券") |
| | | private String coupon; |
| | | } |