物流项目第九期(MongoDB的应用之作业范围)
本项目专栏:
物流项目_Auc23的博客-CSDN博客
建议先看这期:
MongoDB入门之Java的使用-CSDN博客
需求分析
在项目中,会有两个作业范围,分别是机构作业范围和快递员作业范围,这两个作业范围的逻辑是一致的,就是在地图中进行画出范围,就是其作业范围。
实现分析
对于作业范围是一个由多个坐标点组成的多边形,并且必须是闭合的多边形,这个就比较适合用MongoDB来存储。
现在想一个实际需求,用户小王下了订单,如何找到属于该服务范围内的快递员呢?这个就需要使用MongoDB的$geoIntersects
查询操作,其原理就是查找小王的位置坐标点与哪个多边形有交叉,这个就是为其服务的快递员。
ServiceScopeEntity
/*** 服务范围实体*/
@Data
@Document("sl_service_scope")
public class ServiceScopeEntity {@Id@JsonIgnoreprivate ObjectId id;/*** 业务id,可以是机构或快递员*/@Indexedprivate Long bid;/*** 类型 {@link com.sl.ms.scope.enums.ServiceTypeEnum}*/@Indexedprivate Integer type;/*** 多边形范围,是闭合的范围,开始经纬度与结束经纬度必须一样* x: 经度,y:纬度*/@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)private GeoJsonPolygon polygon;private Long created; //创建时间private Long updated; //更新时间
}
/*** 服务类型枚举*/
public enum ServiceTypeEnum {ORGAN(1, "机构"),COURIER(2, "快递员");/*** 类型编码*/private final Integer code;/*** 类型值*/private final String value;ServiceTypeEnum(Integer code, String value) {this.code = code;this.value = value;}public Integer getCode() {return code;}public String getValue() {return value;}public static ServiceTypeEnum codeOf(Integer code) {return EnumUtil.getBy(ServiceTypeEnum::getCode, code);}
}
ScopeService
在ScopeService中主要定义了如下方法:
- 新增或更新服务范围
- 根据主键id删除数据
- 根据业务id和类型删除数据
- 根据主键查询数据
- 根据业务id和类型查询数据
- 根据坐标点查询所属的服务对象
- 根据详细地址查询所属的服务对象
/*** 服务范围Service*/
public interface ScopeService {/*** 新增或更新服务范围** @param bid 业务id* @param type 类型* @param polygon 多边形坐标点* @return 是否成功*/Boolean saveOrUpdate(Long bid, ServiceTypeEnum type, GeoJsonPolygon polygon);/*** 根据主键id删除数据** @param id 主键* @return 是否成功*/Boolean delete(String id);/*** 根据业务id和类型删除数据** @param bid 业务id* @param type 类型* @return 是否成功*/Boolean delete(Long bid, ServiceTypeEnum type);/*** 根据主键查询数据** @param id 主键* @return 服务范围数据*/ServiceScopeEntity queryById(String id);/*** 根据业务id和类型查询数据** @param bid 业务id* @param type 类型* @return 服务范围数据*/ServiceScopeEntity queryByBidAndType(Long bid, ServiceTypeEnum type);/*** 根据坐标点查询所属的服务对象** @param type 类型* @param point 坐标点* @return 服务范围数据*/List<ServiceScopeEntity> queryListByPoint(ServiceTypeEnum type, GeoJsonPoint point);/*** 根据详细地址查询所属的服务对象** @param type 类型* @param address 详细地址,如:北京市昌平区金燕龙办公楼传智教育总部* @return 服务范围数据*/List<ServiceScopeEntity> queryListByPoint(ServiceTypeEnum type, String address);
}
ScopeController
/*** 服务范围*/
@Api(tags = "服务范围")
@RestController
@RequestMapping("scopes")
@Validated
public class ScopeController {@Resourceprivate ScopeService scopeService;/*** 新增或更新服务服务范围** @return REST标准响应*/@ApiOperation(value = "新增/更新", notes = "新增或更新服务服务范围")@PostMappingpublic ResponseEntity<Void> saveScope(@RequestBody ServiceScopeDTO serviceScopeDTO) {ServiceScopeEntity serviceScopeEntity = EntityUtils.toEntity(serviceScopeDTO);Long bid = serviceScopeEntity.getBid();ServiceTypeEnum type = ServiceTypeEnum.codeOf(serviceScopeEntity.getType());Boolean result = this.scopeService.saveOrUpdate(bid, type, serviceScopeEntity.getPolygon());if (result) {return ResponseEntityUtils.ok();}return ResponseEntityUtils.error();}/*** 删除服务范围** @param bid 业务id* @param type 类型* @return REST标准响应*/@ApiImplicitParams({@ApiImplicitParam(name = "bid", value = "业务id,可以是机构或快递员", dataTypeClass = Long.class),@ApiImplicitParam(name = "type", value = "类型,1-机构,2-快递员", dataTypeClass = Integer.class)})@ApiOperation(value = "删除", notes = "删除服务范围")@DeleteMapping("{bid}/{type}")public ResponseEntity<Void> delete(@NotNull(message = "bid不能为空") @PathVariable("bid") Long bid,@NotNull(message = "type不能为空") @PathVariable("type") Integer type) {Boolean result = this.scopeService.delete(bid, ServiceTypeEnum.codeOf(type));if (result) {return ResponseEntityUtils.ok();}return ResponseEntityUtils.error();}/*** 查询服务范围** @param bid 业务id* @param type 类型* @return 服务范围数据*/@ApiImplicitParams({@ApiImplicitParam(name = "bid", value = "业务id,可以是机构或快递员", dataTypeClass = Long.class),@ApiImplicitParam(name = "type", value = "类型,1-机构,2-快递员", dataTypeClass = Integer.class)})@ApiOperation(value = "查询", notes = "查询服务范围")@GetMapping("{bid}/{type}")public ResponseEntity<ServiceScopeDTO> queryServiceScope(@NotNull(message = "bid不能为空") @PathVariable("bid") Long bid,@NotNull(message = "type不能为空") @PathVariable("type") Integer type) {ServiceScopeEntity serviceScopeEntity = this.scopeService.queryByBidAndType(bid, ServiceTypeEnum.codeOf(type));return ResponseEntityUtils.ok(EntityUtils.toDTO(serviceScopeEntity));}/*** 地址查询服务范围** @param type 类型,1-机构,2-快递员* @param address 详细地址,如:北京市昌平区金燕龙办公楼传智教育总部* @return 服务范围数据列表*/@ApiImplicitParams({@ApiImplicitParam(name = "type", value = "类型,1-机构,2-快递员", dataTypeClass = Integer.class),@ApiImplicitParam(name = "address", value = "详细地址,如:北京市昌平区金燕龙办公楼传智教育总部", dataTypeClass = String.class)})@ApiOperation(value = "地址查询服务范围", notes = "地址查询服务范围")@GetMapping("address")public ResponseEntity<List<ServiceScopeDTO>> queryListByAddress(@NotNull(message = "type不能为空") @RequestParam("type") Integer type,@NotNull(message = "address不能为空") @RequestParam("address") String address) {List<ServiceScopeEntity> serviceScopeEntityList = this.scopeService.queryListByPoint(ServiceTypeEnum.codeOf(type), address);return ResponseEntityUtils.ok(EntityUtils.toDTOList(serviceScopeEntityList));}/*** 位置查询服务范围** @param type 类型,1-机构,2-快递员* @param longitude 经度* @param latitude 纬度* @return 服务范围数据列表*/@ApiImplicitParams({@ApiImplicitParam(name = "type", value = "类型,1-机构,2-快递员", dataTypeClass = Integer.class),@ApiImplicitParam(name = "longitude", value = "经度", dataTypeClass = Double.class),@ApiImplicitParam(name = "latitude", value = "纬度", dataTypeClass = Double.class)})@ApiOperation(value = "位置查询服务范围", notes = "位置查询服务范围")@GetMapping("location")public ResponseEntity<List<ServiceScopeDTO>> queryListByAddress(@NotNull(message = "type不能为空") @RequestParam("type") Integer type,@NotNull(message = "longitude不能为空") @RequestParam("longitude") Double longitude,@NotNull(message = "latitude不能为空") @RequestParam("latitude") Double latitude) {List<ServiceScopeEntity> serviceScopeEntityList = this.scopeService.queryListByPoint(ServiceTypeEnum.codeOf(type), new GeoJsonPoint(longitude, latitude));return ResponseEntityUtils.ok(EntityUtils.toDTOList(serviceScopeEntityList));}
}
实现接口
@Slf4j
@Service
public class ScopeServiceImpl implements ScopeService {@Resourceprivate MongoTemplate mongoTemplate;@Resourceprivate EagleMapTemplate eagleMapTemplate;@Overridepublic Boolean saveOrUpdate(Long bid, ServiceTypeEnum type, GeoJsonPolygon polygon) {Query query = Query.query(Criteria.where("bid").is(bid).and("type").is(type.getCode())); //构造查询条件ServiceScopeEntity serviceScopeEntity = this.mongoTemplate.findOne(query, ServiceScopeEntity.class);if (ObjectUtil.isEmpty(serviceScopeEntity)) {//新增serviceScopeEntity = new ServiceScopeEntity();serviceScopeEntity.setBid(bid);serviceScopeEntity.setType(type.getCode());serviceScopeEntity.setPolygon(polygon);serviceScopeEntity.setCreated(System.currentTimeMillis());serviceScopeEntity.setUpdated(serviceScopeEntity.getCreated());} else {//更新serviceScopeEntity.setPolygon(polygon);serviceScopeEntity.setUpdated(System.currentTimeMillis());}try {this.mongoTemplate.save(serviceScopeEntity);return true;} catch (Exception e) {log.error("新增/更新服务范围数据失败! bid = {}, type = {}, points = {}", bid, type, polygon.getPoints(), e);}return false;}@Overridepublic Boolean delete(String id) {Query query = Query.query(Criteria.where("id").is(new ObjectId(id))); //构造查询条件return this.mongoTemplate.remove(query, ServiceScopeEntity.class).getDeletedCount() > 0;}@Overridepublic Boolean delete(Long bid, ServiceTypeEnum type) {Query query = Query.query(Criteria.where("bid").is(bid).and("type").is(type.getCode())); //构造查询条件return this.mongoTemplate.remove(query, ServiceScopeEntity.class).getDeletedCount() > 0;}@Overridepublic ServiceScopeEntity queryById(String id) {return this.mongoTemplate.findById(new ObjectId(id), ServiceScopeEntity.class);}@Overridepublic ServiceScopeEntity queryByBidAndType(Long bid, ServiceTypeEnum type) {Query query = Query.query(Criteria.where("bid").is(bid).and("type").is(type.getCode())); //构造查询条件return this.mongoTemplate.findOne(query, ServiceScopeEntity.class);}@Overridepublic List<ServiceScopeEntity> queryListByPoint(ServiceTypeEnum type, GeoJsonPoint point) {Query query = Query.query(Criteria.where("polygon").intersects(point).and("type").is(type.getCode()));return this.mongoTemplate.find(query, ServiceScopeEntity.class);}@Overridepublic List<ServiceScopeEntity> queryListByPoint(ServiceTypeEnum type, String address) {//根据详细地址查询坐标GeoResult geoResult = this.eagleMapTemplate.opsForBase().geoCode(ProviderEnum.AMAP, address, null);Coordinate coordinate = geoResult.getLocation();return this.queryListByPoint(type, new GeoJsonPoint(coordinate.getLongitude(), coordinate.getLatitude()));}
}
测试
package com.sl.ms.scope.service;import com.sl.ms.scope.entity.ServiceScopeEntity;
import com.sl.ms.scope.enums.ServiceTypeEnum;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;@SpringBootTest
public class ScopeServiceTest {@Resourceprivate ScopeService scopeService;@Testvoid saveOrUpdate() {List<Point> pointList = Arrays.asList(new Point(116.340064,40.061245),new Point(116.347081,40.061836),new Point(116.34751,40.05842),new Point(116.342446,40.058092),new Point(116.340064,40.061245));Boolean result = this.scopeService.saveOrUpdate(2L, ServiceTypeEnum.ORGAN, new GeoJsonPolygon(pointList));System.out.println(result);}@Testvoid testQueryListByPoint() {GeoJsonPoint point = new GeoJsonPoint(116.344828,40.05911);List<ServiceScopeEntity> serviceScopeEntities = this.scopeService.queryListByPoint(ServiceTypeEnum.ORGAN, point);serviceScopeEntities.forEach(serviceScopeEntity -> System.out.println(serviceScopeEntity));}@Testvoid testQueryListByPoint2() {String address = "北京市昌平区金燕龙办公楼";List<ServiceScopeEntity> serviceScopeEntities = this.scopeService.queryListByPoint(ServiceTypeEnum.ORGAN, address);serviceScopeEntities.forEach(serviceScopeEntity -> System.out.println(serviceScopeEntity));}
}
相关文章:

物流项目第九期(MongoDB的应用之作业范围)
本项目专栏: 物流项目_Auc23的博客-CSDN博客 建议先看这期: MongoDB入门之Java的使用-CSDN博客 需求分析 在项目中,会有两个作业范围,分别是机构作业范围和快递员作业范围,这两个作业范围的逻辑是一致的…...

系统思考:经营决策沙盘
今年是我为黄浦区某国有油漆涂料企业提供经营决策沙盘培训的第二年。在这段时间里,我越来越感受到,企业的最大成本往往不在生产环节,而是在决策错误上所带来的长远影响。尤其是在如今这个复杂多变的环境下,企业面临的挑战愈发严峻…...

[网页五子棋][对战模块]实现游戏房间页面,服务器开发(创建落子请求/响应对象)
实现游戏房间页面 创建 css/game_room.css #screen 用于显示当前的状态,例如“等待玩家连接中…”,“轮到你落子”,“轮到对方落子”等 #screen { width: 450px; height: 50px; margin-top: 10px; color: #8f4e19; font-size: 28px; …...
数据结构-代码总结
下面代码自己上完课写着玩的,除了克鲁斯卡尔那里完全ai,其他基本上都是自己写的,具体请参考书本,同时也欢迎各位大佬来纠错 线性表 //线性表--顺序存储结构 #include<iostream> using namespace std; template<typename T> …...
快速掌握 GO 之 RabbitMQ
更多个人笔记见: github个人笔记仓库 gitee 个人笔记仓库 个人学习,学习过程中还会不断补充~ (后续会更新在github和 gitee上) 文章目录 作用经典例子生产者(发送端)消费者(接收端&a…...
SQL Server 事务详解:概念、特性、隔离级别与实践
一、事务的基本概念 事务(Transaction)是数据库操作的基本单位,它是由一组SQL语句组成的逻辑工作单元。事务具有以下关键特性,通常被称为ACID特性: 原子性(Atomicity):事务…...
MAC软件游戏打开提示已损坏
打开「终端.app」,输入以下命令并回车,输入开机密码回车 sudo spctl --master-disable 按照上述步骤操作完成后,打开「系统偏好设置」-「安全与隐私」-「通用」,确保已经修改为「任何来源」。 打开「终端.app」,输入…...
React基础教程(13):路由的使用
文章目录 1、什么是路由?2、路由安装3、路由使用(1)路由方法导入和使用(2)定义路由以及重定向(3)嵌套路由(4)路由跳转方式(5)动态路由动态路由写法一动态路由写法二4、实现效果5、完整代码下载1、什么是路由? 路由是根据不同的url地址展示不同的内容或页面。 一个…...
力扣刷题(第四十三天)
灵感来源 - 保持更新,努力学习 - python脚本学习 解题思路 1. 逐位检查法:通过右移操作逐位检查每一位是否为1,统计计数 2. 位运算优化法:利用 n & (n-1) 操作消除最低位的1,减少循环次数 3. 内置函数法&…...

Centos环境下安装/重装MySQL完整教程
目录 一、卸载残留的MySQL环境: 二、安装MySQL: 1、下载MySQL官方的yum源: 2、更新系统yum源: 3、确保系统中有了对应的MySQL安装包: 4、安装MySQL服务: 5、密钥问题安装失败解决方法: …...

【Linux】环境变量完全解析
9.环境变量 文章目录 9.环境变量一、命令行参数二、获取环境变量程序中获取环境变量1. 使用命令行参数2. 使用系统调用函数getenv("字符串");3. 使用系统提供的全局变量environ 命令行中查询环境变量 三、常见环境变量1. HOME2. OLDPWD3. PATH4. SHELL 四、环境变量与…...
【Java】mybatis-plus乐观锁-基本使用
乐观锁(Optimistic Locking)是解决并发问题的重要机制。它通过在数据更新时验证数据版本来确保数据的一致性,从而避免并发冲突。与悲观锁不同,乐观锁并不依赖数据库的锁机制,而是通过检查数据的版本或标志字段来判断数…...

力扣每日一题——找到离给定两个节点最近的节点
目录 题目链接:2359. 找到离给定两个节点最近的节点 - 力扣(LeetCode) 题目描述 解法一:双指针路径交汇法 基本思路 关键步骤 为什么这样可行呢我请问了? 举个例子 特殊情况 Java写法: C写法&a…...
机器学习与深度学习03-逻辑回归01
目录 上集回顾1. 逻辑回归与线性回归的区别2.逻辑回归的常见目标函数3.逻辑回归如何分类4.Sigmoid函数详解5.逻辑回归模型的参数 上集回顾 上一节文章地址:链接 1. 逻辑回归与线性回归的区别 应用领域 线性回归通常⽤于解决回归问题,其中⽬标是预测⼀…...

卷积神经网络(CNN)入门学习笔记
什么是 CNN? CNN,全称 卷积神经网络(Convolutional Neural Network),是一种专门用来处理图片、语音、文本等结构化数据的神经网络。 它模仿人眼识别图像的方式: 从局部到整体,一步步提取特征&a…...
【优笔】基于STM32的多模态智能门禁系统
代码功能详细描述 该代码实现了一个基于STM32的多模态智能门禁系统,整合密码、指纹、人脸识别(预留)三种验证方式,并提供完善的管理功能。系统架构如下图所示: #mermaid-svg-Uufpcoeo5Lega096 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size…...
Metasploit工具使用详解(上)丨小白WEB安全入门笔记
Metasploit工具使用详解(上)丨小白WEB安全入门笔记 一、课程定位与工具概述 课程性质: 小白WEB安全入门课程聚焦基础操作,非深度专题(Metasploit专题可讲数十节课)目标:掌握基本概念和简单漏洞利用 Metasploit核心定…...
Femap许可证与网络安全策略
随着科技的快速发展,网络安全问题已成为各行各业关注的焦点。在电磁仿真领域,Femap作为一款领先的软件,其许可证的安全性和网络策略的重要性不言而喻。本文将探讨Femap许可证与网络安全策略的关系,确保您的电磁仿真工作能够在一个…...

VLAN的作用和原理
1. 为什么要有vlan? 分割广播域,避免广播风暴,造成网络资源的浪费 可以灵活的组网,便于管理,同时还有安全加固的功能 2. vlan是怎么实现的?端口的原理? 设置VLAN后,流量之间的转…...

深入探讨集合与数组转换方法
目录 1、Arrays.asList() 1.1、方法作用 1.2、内部实现 1.3、修改元素的影响 1.4、注意事项 2、list.toArray() 2.1、方法作用 2.2、内部实现 2.3、修改元素的影响 2.4、特殊情况 1、对象引用 2、数组copy 3、对比总结 4、常见误区与解决方案 5、实际应用建议…...
让大模型看得见自己的推理 — KnowTrace结构化知识追踪
让大模型“看得见”自己的推理 —— KnowTrace 结构化知识追踪式 RAG 全解析 一句话概括:把检索-推理“改造”成 动态知识图构建任务,再让 LLM 只关注这张不断精炼的小图 —— 这就是显式知识追踪的核心价值。 1. 背景:为什么 RAG 仍难以搞定多跳推理? 长上下文负担 传统 I…...

【HarmonyOS 5应用架构详解】深入理解应用程序包与多Module设计机制
⭐本期内容:【HarmonyOS 5应用架构详解】深入理解应用程序包与多Module设计机制 🏆系列专栏:鸿蒙HarmonyOS:探索未来智能生态新纪元 文章目录 前言应用与应用程序包应用程序的基本概念应用程序包的类型标识机制应用安装流程 应用的…...

【Oracle】DCL语言
个人主页:Guiat 归属专栏:Oracle 文章目录 1. DCL概述1.1 什么是DCL?1.2 DCL的核心功能 2. 用户管理2.1 创建用户2.2 修改用户2.3 删除用户2.4 用户信息查询 3. 权限管理3.1 系统权限3.1.1 授予系统权限3.1.2 撤销系统权限 3.2 对象权限3.2.1…...

MySQL强化关键_017_索引
目 录 一、概述 二、索引 1.主键索引 2.唯一索引 3.查看索引 4.添加索引 (1)建表时添加 (2)建表后添加 5.删除索引 三、树 1.二叉树 2.红黑树 3.B树 4.B树 (1)为什么 MySQL 选择B树作为索引…...
stm32——SPI协议
stm32——SPI协议 STM32的SPI(Serial Peripheral Interface,串行外设接口)协议是一种高速、全双工、同步的串行通信协议,广泛评估微控制器与各种外设(如传感器、器件、显示器、模块等)之间的数据传输。STM3…...
Linux 下如何查看进程的资源限制信息?
简介 Linux 上的 cat /proc/$pid/limits 命令提供有关特定进程的资源限制的信息,其中 $pid 是相关进程的进程 ID (pid)。该文件是 /proc 文件系统的一部分,该文件系统是一个虚拟文件系统,提供有关进程和系统资源的信息…...

【备忘】php命令行异步执行超长时间任务
环境说明: 操作系统:windows10 IDE:phpstorm 开发语言:php7.4 框架:thinkphp5.1 测试环境:linuxwindows均测试通过。 初级方法: function longRunningTask() {$root_path Tools::get_ro…...
对于ARM开发各种手册的分类
手册名称全称主要内容适用范围是不是讲SysTick?Cortex-M3 Technical Reference Manual (TRM)Cortex-M3 Technical Reference Manual描述 Cortex-M3内核架构,如寄存器模型、总线接口、指令集、异常模型只适合 Cortex-M3 内核,不含外设❌ 没有C…...
java开发中#和$的区别
在Spring框架中,$ 和 # 是两种不同的表达式前缀,用于从不同的来源获取值或执行计算。下面详细解释它们的区别和用法: 一、$ 占位符(Property Placeholder) 1. 作用 从配置文件(如 application.propertie…...

在 RK3588 上通过 VSCode 远程开发配置指南
在 RK3588 上通过 VSCode 远程开发配置指南 RK3588 设备本身不具备可视化编程环境,但可以通过 VSCode 的 Remote - SSH 插件 实现远程代码编写与调试。以下是完整的配置流程。 一、连接 RK3588 1. 安装 Debian 系统 先在 RK3588 上安装 Debian 操作系统。 2. 安…...