diff --git a/nxhs-service/src/main/java/cc/yunxi/controller/RecycleStationController.java b/nxhs-service/src/main/java/cc/yunxi/controller/RecycleStationController.java index 5a39964..7b19e71 100644 --- a/nxhs-service/src/main/java/cc/yunxi/controller/RecycleStationController.java +++ b/nxhs-service/src/main/java/cc/yunxi/controller/RecycleStationController.java @@ -12,6 +12,7 @@ import cc.yunxi.domain.query.RecycleStationQuery; import cc.yunxi.domain.vo.priceproduct.ProductRespVO; import cc.yunxi.domain.vo.recyclestation.RecycleStationRespVO; import cc.yunxi.service.IRecycleStationService; +import cc.yunxi.service.IStationAreaService; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import io.swagger.annotations.Api; @@ -43,6 +44,7 @@ import java.util.List; public class RecycleStationController { private final IRecycleStationService recycleStationService; + private final IStationAreaService stationAreaService; @ApiOperation("分页查询回收站") @PostMapping("/page") @@ -112,7 +114,9 @@ public class RecycleStationController { @ApiOperation("附近的回收站") @GetMapping("/nearby") public CommonResult stationNearby(@Valid LocationDTO location) { - RecycleStation nearbyStation = recycleStationService.getNearbyStation(location); +// RecycleStation nearbyStation = recycleStationService.getNearbyStation(location); + // 增加区域筛选条件 + RecycleStation nearbyStation = recycleStationService.getNearbyInArea(location); RecycleStationRespVO stationRespVO = null; if (ObjectUtil.isNotEmpty(nearbyStation)) { stationRespVO = BeanUtils.copyBean(nearbyStation, RecycleStationRespVO.class); diff --git a/nxhs-service/src/main/java/cc/yunxi/controller/StationAreaController.java b/nxhs-service/src/main/java/cc/yunxi/controller/StationAreaController.java new file mode 100644 index 0000000..36fee46 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/controller/StationAreaController.java @@ -0,0 +1,37 @@ +package cc.yunxi.controller; + +import cc.yunxi.common.domain.CommonResult; +import cc.yunxi.domain.dto.LocationDTO; +import cc.yunxi.domain.po.StationArea; +import cc.yunxi.service.IStationAreaService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; + +@Api(tags = "商户经营区域") +@RequiredArgsConstructor +@RestController +@RequestMapping("/station-area") +@Slf4j +@Validated +public class StationAreaController { + + + private final IStationAreaService stationAreaService; + + @GetMapping("/inArea") + @ApiOperation("获取当前位置所属的经营区域") + public CommonResult getInArea(@Valid LocationDTO location) { + StationArea area = stationAreaService.getBelongArea(location); + if (null != area) return CommonResult.success(area); + return CommonResult.error(400, "当前位置不在经营区域内"); + } + +} diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/dto/PositionDTO.java b/nxhs-service/src/main/java/cc/yunxi/domain/dto/PositionDTO.java new file mode 100644 index 0000000..a529c4c --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/domain/dto/PositionDTO.java @@ -0,0 +1,36 @@ +package cc.yunxi.domain.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; +import java.math.BigDecimal; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PositionDTO { + @ApiModelProperty(value = "当前经度", required = true, example = "121.404032") + @NotNull(message = "未知定位") + private BigDecimal longitude; + + @ApiModelProperty(value = "当前纬度", required = true, example = "31.163973") + @NotNull(message = "未知定位") + private BigDecimal latitude; + + @ApiModelProperty(value = "所属商户", required = true, example = "543049974737277765") + @NotNull(message = "未知商户") + private String companyId; + + @ApiModelProperty(value = "转换为pos") + public static PositionDTO transformFromLoc(LocationDTO locationDTO, @NotNull String companyId) { + PositionDTO dto = new PositionDTO(); + dto.setLongitude(new BigDecimal(locationDTO.getLongitude())); + dto.setLatitude(new BigDecimal(locationDTO.getLatitude())); + dto.setCompanyId(companyId); + return dto; + } + +} diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/po/StationArea.java b/nxhs-service/src/main/java/cc/yunxi/domain/po/StationArea.java new file mode 100644 index 0000000..b14353f --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/domain/po/StationArea.java @@ -0,0 +1,60 @@ +package cc.yunxi.domain.po; + +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.util.Date; + +/** + * 回收站接单地图围栏 + * + * @版本: V3.5 + * @版权: 引迈信息技术有限公司(https://www.jnpfsoft.com) + * @作者: JNPF开发平台组 + * @日期: 2024-05-22 + */ +@Data +@TableName("nx_station_area") +public class StationArea { + @TableId(value ="ID" ) + private String id; + @TableField("STATION_CODE") + private String stationCode; + @TableField(value = "AREA_TYPE" , updateStrategy = FieldStrategy.IGNORED) + private Integer areaType; + @TableField(value = "AREA_NAME" , updateStrategy = FieldStrategy.IGNORED) + private String areaName; + @TableField(value = "AREA_DESC" , updateStrategy = FieldStrategy.IGNORED) + private String areaDesc; + @TableField(value = "AREA_MAP_INFO" , updateStrategy = FieldStrategy.IGNORED) + private String areaMapInfo; + @TableField(value = "STATUS" , updateStrategy = FieldStrategy.IGNORED) + private Integer status; + @TableField("F_CREATOR_TIME") + private Date creatorTime; + @TableField("F_CREATOR_USER_ID") + private String creatorUserId; + @TableField("F_LAST_MODIFY_TIME") + private Date lastModifyTime; + @TableField("F_LAST_MODIFY_USER_ID") + private String lastModifyUserId; + @TableField("F_DELETE_TIME") + private Date deleteTime; + @TableField("F_DELETE_USER_ID") + private String deleteUserId; + @TableField("F_DELETE_MARK") + private String deleteMark; + @TableField("F_TENANT_ID") + private String tenantId; + @TableField(value = "COMPANY_ID" , updateStrategy = FieldStrategy.IGNORED) + private String companyId; + @TableField("DEPARTMENT_ID") + private String departmentId; + @TableField("ORGANIZE_JSON_ID") + private String organizeJsonId; + @TableField("F_FLOW_ID") + private String flowId; +} diff --git a/nxhs-service/src/main/java/cc/yunxi/mapper/RecycleStationMapper.java b/nxhs-service/src/main/java/cc/yunxi/mapper/RecycleStationMapper.java index a18ceac..89b197f 100644 --- a/nxhs-service/src/main/java/cc/yunxi/mapper/RecycleStationMapper.java +++ b/nxhs-service/src/main/java/cc/yunxi/mapper/RecycleStationMapper.java @@ -1,6 +1,7 @@ package cc.yunxi.mapper; import cc.yunxi.domain.dto.LocationDTO; +import cc.yunxi.domain.dto.PositionDTO; import cc.yunxi.domain.po.RecycleStation; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @@ -27,6 +28,7 @@ public interface RecycleStationMapper extends BaseMapper { /** * 复杂sql xml分页查询回收站 + * * @param page * @param queryWrapper * @return Page @@ -38,9 +40,18 @@ public interface RecycleStationMapper extends BaseMapper { /** * 获取最近的站点 + * * @param locationDTO * @return RecycleStation */ RecycleStation getNearbyStation(@Param("location") LocationDTO locationDTO); + /** + * 获取当区域所属商户下最近的站点 + * + * @param positionDTO + * @return RecycleStation + */ + RecycleStation getNearbyInArea(@Param("position") PositionDTO positionDTO); + } diff --git a/nxhs-service/src/main/java/cc/yunxi/mapper/StationAreaMapper.java b/nxhs-service/src/main/java/cc/yunxi/mapper/StationAreaMapper.java new file mode 100644 index 0000000..9e4b81c --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/mapper/StationAreaMapper.java @@ -0,0 +1,16 @@ +package cc.yunxi.mapper; + + +import cc.yunxi.domain.po.StationArea; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 商户经营区域 + * 版本: V3.5 + * 版权: 引迈信息技术有限公司(https://www.jnpfsoft.com) + * 作者: JNPF开发平台组 + * 日期: 2024-05-23 + */ +public interface StationAreaMapper extends BaseMapper { + +} diff --git a/nxhs-service/src/main/java/cc/yunxi/service/IRecycleStationService.java b/nxhs-service/src/main/java/cc/yunxi/service/IRecycleStationService.java index f7bf2c1..01062ab 100644 --- a/nxhs-service/src/main/java/cc/yunxi/service/IRecycleStationService.java +++ b/nxhs-service/src/main/java/cc/yunxi/service/IRecycleStationService.java @@ -24,6 +24,7 @@ public interface IRecycleStationService extends IService { /** * 分页查询 + * * @param recycleStationQuery * @return Page */ @@ -37,8 +38,17 @@ public interface IRecycleStationService extends IService { */ RecycleStation getNearbyStation(LocationDTO locationDTO); + /** + * 获取区域内最近的站点 + * + * @param locationDTO + * @return RecycleStation + */ + RecycleStation getNearbyInArea(LocationDTO locationDTO); + /** * 站点详情 + * * @param id * @return RecycleOrder */ @@ -46,6 +56,7 @@ public interface IRecycleStationService extends IService { /** * 通过id集获取站点列表 + * * @param ids * @return RecycleOrder */ @@ -54,6 +65,7 @@ public interface IRecycleStationService extends IService { /** * 站点废品价目信息 + * * @param stationId * @return RecycleOrder */ @@ -62,6 +74,7 @@ public interface IRecycleStationService extends IService { /** * 站点废品价目信息, 一二级全部信息 + * * @param stationId * @return RecycleOrder */ @@ -70,11 +83,11 @@ public interface IRecycleStationService extends IService { /** * 站点废品价目信息, 一级全部信息 + * * @param stationId * @return RecycleOrder */ List getStationProductFirstByStationId(String stationId); - } diff --git a/nxhs-service/src/main/java/cc/yunxi/service/IStationAreaService.java b/nxhs-service/src/main/java/cc/yunxi/service/IStationAreaService.java new file mode 100644 index 0000000..5730bd5 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/service/IStationAreaService.java @@ -0,0 +1,21 @@ +package cc.yunxi.service; + +import cc.yunxi.domain.dto.LocationDTO; +import cc.yunxi.domain.po.StationArea; +import com.baomidou.mybatisplus.extension.service.IService; + +import javax.validation.Valid; + +/** + * 商户经营区域 + * 版本: V3.5 + * 版权: 引迈信息技术有限公司(https://www.jnpfsoft.com) + * 作者: JNPF开发平台组 + * 日期: 2024-05-23 + */ +public interface IStationAreaService extends IService { + + //获取位置所属区域 + StationArea getBelongArea(@Valid LocationDTO locationDTO); + +} diff --git a/nxhs-service/src/main/java/cc/yunxi/service/impl/IStationAreaServiceImpl.java b/nxhs-service/src/main/java/cc/yunxi/service/impl/IStationAreaServiceImpl.java new file mode 100644 index 0000000..afa0597 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/service/impl/IStationAreaServiceImpl.java @@ -0,0 +1,49 @@ +package cc.yunxi.service.impl; + +import cc.yunxi.domain.dto.LocationDTO; +import cc.yunxi.domain.po.StationArea; +import cc.yunxi.mapper.StationAreaMapper; +import cc.yunxi.service.IStationAreaService; +import cc.yunxi.utils.AreaUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.util.List; + +/** + * + * 商户经营区域 + * 版本: V3.5 + * 版权: 引迈信息技术有限公司(https://www.jnpfsoft.com) + * 作者: JNPF开发平台组 + * 日期: 2024-05-23 + */ +@Service +public class IStationAreaServiceImpl extends ServiceImpl implements IStationAreaService { + + @Resource + private StationAreaMapper stationAreaMapper; + + @Override + public StationArea getBelongArea(LocationDTO location) { + //获取所有的围栏信息 + QueryWrapper wrapper = new QueryWrapper<>(); +// wrapper.eq("area_type","1");//1接单 2不接单 + wrapper.eq("status","1");//0禁用 1启用 + List areas = stationAreaMapper.selectList(wrapper); + BigDecimal lat = new BigDecimal(location.getLatitude());//纬度 + BigDecimal lng = new BigDecimal(location.getLongitude());//经度 + + for (StationArea area : areas) { + String mapInfo = area.getAreaMapInfo(); + if(StringUtils.hasText(mapInfo)&& AreaUtil.isInsidePolygon(mapInfo, lng, lat)){ + return area; + } + } + return null; + } +} diff --git a/nxhs-service/src/main/java/cc/yunxi/service/impl/RecycleStationServiceImpl.java b/nxhs-service/src/main/java/cc/yunxi/service/impl/RecycleStationServiceImpl.java index cc301eb..79a070d 100644 --- a/nxhs-service/src/main/java/cc/yunxi/service/impl/RecycleStationServiceImpl.java +++ b/nxhs-service/src/main/java/cc/yunxi/service/impl/RecycleStationServiceImpl.java @@ -5,6 +5,7 @@ import cc.yunxi.common.exception.BizIllegalException; import cc.yunxi.common.utils.BeanUtils; import cc.yunxi.common.utils.CollUtils; import cc.yunxi.domain.dto.LocationDTO; +import cc.yunxi.domain.dto.PositionDTO; import cc.yunxi.domain.po.*; import cc.yunxi.domain.query.RecycleStationQuery; import cc.yunxi.domain.vo.priceproduct.ProductRespVO; @@ -12,6 +13,7 @@ import cc.yunxi.domain.vo.recyclestation.RecycleStationRespVO; import cc.yunxi.enums.GlobalStatusEnum; import cc.yunxi.mapper.*; import cc.yunxi.service.IRecycleStationService; +import cc.yunxi.service.IStationAreaService; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; @@ -49,6 +51,8 @@ public class RecycleStationServiceImpl extends ServiceImpl + * 坐标的排列顺序对于多边形区域判断算法(如射线法或奇偶规则)来说非常重要。在判断一个点是否位于多边形内部时,坐标的排列顺序决定了射线或扫描线与多边形边相交的顺序。 + *

+ * 对于多边形,坐标的排列顺序通常遵循以下规则: + *

+ * 1.顺时针或逆时针方向:坐标点必须按照顺时针或逆时针的顺序排列,以确保在判断点与多边形关系时的一致性。一旦选择了方向(顺时针或逆时针),就必须保持整个多边形使用相同的方向。 + * 2.闭合多边形:对于多边形,最后一个坐标点应该与第一个坐标点相同,以确保多边形是闭合的。这样,射线或扫描线在离开多边形时最终会回到起始点,从而确保算法的正确性。 + * 3.不交叉:多边形的边不应相交(除了在多边形的顶点处)。如果边相交,算法可能会产生错误的结果。 + * 4.连续性:相邻的坐标点之间应该是连续的,即从一个顶点到下一个顶点应该是直接相连的线段 + *

+ * + * @author guochaojie + * @version V1.0.0 + * @date 2024年5月21日09:49:29 + * @description 电子围栏相关类 + */ +public class AreaUtil { + + /** + * 判断一个点是否在多边形内部 + * + * @param polygonStr 多边形的坐标点数组,格式为 [x1,y1,x2,y2,...] + * 其中,x1,y1表示第一个坐标点 经度,纬度,依此类推 + * @param lat 点的纬度 + * @param lon 点的经度 + * @return + */ + public static boolean isInsidePolygonStr(String polygonStr, double lat, double lon) { + double[][] polygon = parsePolygonFromArray(polygonStr); + + int j = polygon.length - 1; // 多边形最后一个点的索引 + boolean oddNodes = false; + + for (int i = 0; i < polygon.length; i++) { + if ((polygon[i][1] < lon && polygon[j][1] >= lon) // 检查点是否在多边形边的右侧 + || (polygon[j][1] < lon && polygon[i][1] >= lon)) { // 或在多边形边的左侧 + if (polygon[i][0] + (lon - polygon[i][1]) / (polygon[j][1] - polygon[i][1]) + * (polygon[j][0] - polygon[i][0]) < lat) { // 检查交点是否在线段上 + oddNodes = !oddNodes; // 如果是,则翻转状态 + } + } + j = i; // 移动到下一个边 + } + + return oddNodes; // 如果状态为true,则点在多边形内 + } + + /** + * 解析多边形的坐标点数组 + * + * @param polygonStr + * @return + */ + public static double[][] parsePolygonFromArray(String polygonStr) { + // 移除可能的空格,并用逗号分割字符串` + String[] coordinatePairs = polygonStr.replaceAll(" ", "") + .replaceAll("\\[", "") + .replaceAll("\\]", "") + .split(","); + + // 计算有多少个坐标对 + int pairCount = coordinatePairs.length / 2; + double[][] polygon = new double[pairCount][2]; + + // 遍历坐标对数组,并填充二维数组 + for (int i = 0, j = 0; i < coordinatePairs.length; i += 2, j++) { + // 将字符串转换为double + polygon[j][0] = Double.parseDouble(coordinatePairs[i]); // 纬度 + polygon[j][1] = Double.parseDouble(coordinatePairs[i + 1]); // 经度 + } + + // 由于多边形应该是闭合的,通常我们需要复制第一个点作为最后一个点 + // 判读最后一个点是否与第一个点相同 + if (polygon[0][0] != polygon[pairCount - 1][0] && polygon[0][1] != polygon[pairCount - 1][1]) { + //追加一个点 + polygon = Arrays.copyOf(polygon, polygon.length + 1); + polygon[pairCount] = new double[]{polygon[0][0], polygon[0][1]}; + } + + return polygon; + } + + /** + * @param json 转换 + * @return + */ + public static List> parsePolygonFromJson(String json) { + List list = JSONUtil.toList(json, HashMap.class); + HashMap start = list.get(0); + HashMap end = list.get(list.size() - 1); + if (!start.equals(end)) { + list.add(start); + } + List> maps = new ArrayList<>(); + for (HashMap map : list) { + maps.add(map); + } + return maps; + } + + /** + * @param polygonJson 多边形 + * @param lat 纬度 + * @param lng 经度 + * @return + */ + public static boolean isInsidePolygon(String polygonJson, BigDecimal lat, BigDecimal lng) { + List> polygon = parsePolygonFromJson(polygonJson); + boolean isInside = false; + + for (int i = 0, j = polygon.size() - 1; i < polygon.size(); j = i++) { + Map vertex1 = polygon.get(i); + Map vertex2 = polygon.get(j); + + BigDecimal lat1 = new BigDecimal(vertex1.get("lat").toString()); + BigDecimal lng1 = new BigDecimal(vertex1.get("lng").toString()); + BigDecimal lat2 = new BigDecimal(vertex2.get("lat").toString()); + BigDecimal lng2 = new BigDecimal(vertex2.get("lng").toString()); + + boolean intersect = (lng1.compareTo(lng) > 0) != (lng2.compareTo(lng) > 0) && + lat.compareTo(lat1.add(lat2.subtract(lat1) + .multiply(lng.subtract(lng1)) + .divide(lng2.subtract(lng1), 20, BigDecimal.ROUND_HALF_UP))) < 0; + if (intersect) { + isInside = !isInside; + } + } + return isInside; + } + + public static void main(String[] args) { + String json = "[{\"lat\":39.98423552382614,\"lng\":116.3059581286434},{\"lat\":39.986865947052266,\"lng\":116.3783954344508},{\"lat\":39.94897808062252,\"lng\":116.37942533460955},{\"lat\":39.94818852953558,\"lng\":116.3093911759388},{\"lat\":39.960820297511674,\"lng\":116.30801795711886}]"; + BigDecimal testLat = new BigDecimal(39.94397741373967); + BigDecimal testLng = new BigDecimal(116.21807210599582); + BigDecimal testLat2 = new BigDecimal(39.97292354297105); + BigDecimal testLng2 = new BigDecimal(116.34818459585586); + boolean isInside = isInsidePolygon(json, testLat, testLng); + boolean isInside2 = isInsidePolygon(json, testLat2, testLng2); + System.out.println(isInside); + System.out.println(isInside2); + } +} + diff --git a/nxhs-service/src/main/resources/mapper/RecycleStationMapper.xml b/nxhs-service/src/main/resources/mapper/RecycleStationMapper.xml index 73c5b54..865fd52 100644 --- a/nxhs-service/src/main/resources/mapper/RecycleStationMapper.xml +++ b/nxhs-service/src/main/resources/mapper/RecycleStationMapper.xml @@ -50,4 +50,26 @@ LIMIT 1; + + //区域所属商户名下距离最近的回收站 + + + diff --git a/nxhs-service/src/main/resources/mapper/StationAreaMapper.xml b/nxhs-service/src/main/resources/mapper/StationAreaMapper.xml new file mode 100644 index 0000000..292f2e7 --- /dev/null +++ b/nxhs-service/src/main/resources/mapper/StationAreaMapper.xml @@ -0,0 +1,7 @@ + + + + + + +