当前位置: 首页 > news >正文

产品外观设计网站智能建站制作

产品外观设计网站,智能建站制作,有本地服务器怎么做网站,wordpress封面外链🎯 本文档详细介绍了如何使用Redis GEO模块实现场馆位置的存储与查询,以支持“附近场馆”搜索功能。首先,通过微信小程序获取用户当前位置,并将该位置信息与场馆的经纬度数据一同存储至Redis中。利用Redis GEO高效的地理空间索引能…

🎯 本文档详细介绍了如何使用Redis GEO模块实现场馆位置的存储与查询,以支持“附近场馆”搜索功能。首先,通过微信小程序获取用户当前位置,并将该位置信息与场馆的经纬度数据一同存储至Redis中。利用Redis GEO高效的地理空间索引能力,文档展示了如何初始化缓存、批量处理和存储场馆位置信息,以及执行基于距离和多种条件的分页查询。此外,还提供了计算两个地理位置间距离的工具类。此方案适用于开发具备地理定位功能的应用程序,如体育场馆预订系统。
🏠️ HelloDam/场快订(场馆预定 SaaS 平台)

文章目录

  • Redis GEO 介绍
  • 流程
  • 数据库设计
  • 实体类
  • 位置缓存初始化
  • 附近场馆条件查询

Redis GEO 介绍

Redis GEO模块为Redis数据库引入了地理位置处理的功能,使得开发者能够基于地理坐标(经纬度)进行数据的操作和查询。通过使用GEO功能,可以方便地存储带有地理位置信息的数据,并执行如添加地理位置、计算两个位置之间的距离、查找指定半径内所有位置等操作。这些特性非常适合于构建需要处理地理位置的应用程序,比如附近的人或地点搜索功能。Redis GEO背后的技术基于高效的GeoHash算法,将地理位置映射到一个字符串上,从而允许对地理位置进行快速检索。这一功能极大地扩展了Redis在地理空间数据处理方面的能力,使其成为开发具有地理定位功能应用的强大工具。

流程

1、小程序前端获取位置(在小程序中获取当前位置的功能通常是通过调用微信小程序提供的API来实现的)

2、后端将位置存储到数据库

3、附近场馆查询

  • 将位置信息存储到Redis GEO中
  • 查询的时候,先根据缓存查询附近的场馆,再带着附近场馆ID和查询条件去数据库中查询

数据库设计

为了实现附近场馆功能,需要存储场馆的经纬度信息

DROP TABLE IF EXISTS `venue`;
CREATE TABLE `venue`(`id` bigint NOT NULL COMMENT 'ID',`create_time` datetime,`update_time` datetime,`is_deleted` tinyint default 0 COMMENT '逻辑删除 0:没删除 1:已删除',`organization_id` bigint NOT NULL COMMENT '所属机构ID',`name` varchar(30) NOT NULL COMMENT '场馆名称',`type` int NOT NULL COMMENT '场馆类型 1:篮球馆(场) 2:足球场 3:羽毛球馆(场) 4:排球馆(场)100:体育馆 1000:其他',`address` varchar(255) NOT NULL COMMENT '场馆地址名称',`latitude` DECIMAL(9, 6) NOT NULL COMMENT '纬度',`longitude` DECIMAL(9, 6) NOT NULL COMMENT '经度',`description` varchar(255) DEFAULT '' COMMENT '场馆描述,也可以说是否提供器材等等',`open_time` varchar(2000) NOT NULL COMMENT '场馆营业时间',`phone_number` varchar(11) NULL DEFAULT '' COMMENT '联系电话',`status` tinyint NOT NULL COMMENT '场馆状态 0:关闭 1:开放 2:维护中',`is_open` tinyint NOT NULL COMMENT '是否对外开放 0:否 1:是 如果不对外开放,需要相同机构的用户才可以预定',`advance_booking_day` int NOT NULL COMMENT '提前可预定天数,例如设置为1,即今天可预订明天的场',`start_booking_time` time NOT NULL COMMENT '开放预订时间',PRIMARY KEY (`id`) USING BTREE
)

实体类

【查询请求类】

import com.vrs.convention.page.PageRequest;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.math.BigDecimal;/*** @Author dam* @create 2024/12/7 10:51*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class VenueListReqDTO extends PageRequest {/*** 场馆名称*/private String name;/*** 场馆类型 1:篮球馆(场) 2:足球场 3:羽毛球馆(场) 4:排球馆(场)100:体育馆 1000:其他*/private Integer type;/*** 维度*/private BigDecimal latitude;/*** 经度*/private BigDecimal longitude;/*** 多少千米*/private double km;/*** 场馆状态 0:关闭 1:开放 2:维护中*/private Integer status;
}

【返回实体类】

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.vrs.domain.base.BaseEntity;
import lombok.Data;import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalTime;/*** * @TableName venue*/
@TableName(value ="venue")
@Data
public class VenueRespDTO extends BaseEntity implements Serializable {/*** 所属机构ID*/private Long organizationId;/*** 所属机构名称*/private String organizationName;/*** 场馆名称*/private String name;/*** 场馆类型 1:篮球馆(场) 2:足球场 3:羽毛球馆(场) 4:排球馆(场)100:体育馆 1000:其他*/private Integer type;private String typeName;/*** 场馆地址*/private String address;/*** 场馆描述,也可以说是否提供器材等等*/private String description;/*** 场馆营业时间*/private String openTime;/*** 联系电话*/private String phoneNumber;/*** 场馆状态 0:关闭 1:开放 2:维护中*/private Integer status;private String statusName;/*** 是否对外开放 0:否 1:是 如果不对外开放,需要相同机构的用户才可以预定*/private Integer isOpen;/*** 提前可预定天数,例如设置为1,即今天可预订明天的场*/private Integer advanceBookingDay;/*** 开放预订时间*/private LocalTime startBookingTime;/*** 维度*/private BigDecimal latitude;/*** 经度*/private BigDecimal longitude;/*** 距离多少公里*/private Double distance;@TableField(exist = false)private static final long serialVersionUID = 1L;
}

位置缓存初始化

【初始化类】

当场馆服务启动起来的时候,调用cacheVenueLocations方法

import com.vrs.service.VenueService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;/*** @Author dam* @create 2025/1/28 9:59*/
@Component
@Slf4j
@RequiredArgsConstructor
public class VenueLocationCacheInit implements CommandLineRunner {private final VenueService venueService;@Overridepublic void run(String... args) throws Exception {log.info("读取数据库中的场馆信息,将其位置存储缓存到Redis");venueService.cacheVenueLocations();log.info("场馆位置缓存成功");}
}

【位置缓存加载】

这段代码通过流式处理从数据库中查询场馆的位置信息(经度和纬度),等到缓冲区数据到达容量之后,使用 Redis 的管道技术将这些信息批量存储到 Redis 的地理空间索引中。

通过分批处理(每次处理 1000 条数据)和管道技术,代码优化了数据存储的效率,减少了与 Redis 的交互次数,从而提升了性能。优点是降低了数据库和 Redis 的负载,提高了数据写入的速度,同时避免了内存溢出风险。

/*** 将场馆的位置信息存储到 Redis 中*/
@Override
@SneakyThrows
public void cacheVenueLocations() {// 获取 dataSource Bean 的连接@Cleanup Connection conn = dataSource.getConnection();@Cleanup Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);stmt.setFetchSize(Integer.MIN_VALUE);// 查询sql,只查询关键的字段String sql = "SELECT id,latitude,longitude FROM venue where is_deleted = 0";@Cleanup ResultSet rs = stmt.executeQuery(sql);// 每次获取一行数据进行处理,rs.next()如果有数据返回true,否则返回falseList<VenueDO> buffer = new ArrayList<>();int bufferSize = 1000;while (rs.next()) {// 获取数据中的属性VenueDO venueDO = new VenueDO();venueDO.setId(rs.getLong("id"));venueDO.setLongitude(rs.getBigDecimal("longitude"));venueDO.setLatitude(rs.getBigDecimal("latitude"));buffer.add(venueDO);if (buffer.size() >= bufferSize) {cacheLocations(buffer);buffer.clear();}}if (buffer.size() >= 0) {cacheLocations(buffer);buffer.clear();}
}/*** 使用 Redis 管道将场馆位置添加到 Redis 缓存中** @param buffer*/
private void cacheLocations(List<VenueDO> buffer) {// 使用 Redis 管道批量操作redisTemplate.executePipelined((RedisCallback<?>) (connection) -> {for (VenueDO venue : buffer) {// 确保经纬度信息不为空if (venue.getLongitude() != null && venue.getLatitude() != null) {// 将场馆的经纬度信息存储到 Redis 中Point point = new Point(venue.getLongitude().doubleValue(), venue.getLatitude().doubleValue());connection.geoAdd(RedisCacheConstant.VENUE_LOCATION_KEY.getBytes(), point, venue.getId().toString().getBytes());}}// 管道操作不需要返回值return null;});
}

附近场馆条件查询

这段代码实现了一个基于地理位置和多种条件的分页查询场馆信息的功能。

  • 首先根据用户提供的经纬度和半径范围,从缓存中查询出附近的场馆ID列表。
  • 然后,结合用户输入的其他条件(如场馆名称、类型、状态等),构建查询条件,从数据库中筛选出符合条件的场馆信息。注意构建查询条件的时候,需要使用 in 语句 传入 附近场馆ID列表。
  • 接着将查询结果转换为响应对象(DTO),并计算每个场馆与用户当前位置的距离,最后返回分页后的场馆信息列表。
@Override
public PageResponse<VenueRespDTO> pageVenueDO(VenueListReqDTO request) {List<Long> venueIdList = null;if (request.getLatitude() != null && request.getLongitude() != null) {// 先去缓存中,把位置靠近的场馆ID查询出来venueIdList = this.findVenuesWithinRadius(request.getLongitude(), request.getLatitude(), request.getKm());}LambdaQueryWrapper<VenueDO> queryWrapper = Wrappers.lambdaQuery(VenueDO.class);// 只查询附近的场馆if (venueIdList != null) {if (venueIdList.size() > 0) {queryWrapper.in(VenueDO::getId, venueIdList);} else {return new PageResponse(request.getCurrent(), request.getSize(), 0L, null);}}// 根据名字模糊查询if (!StringUtils.isBlank(request.getName())) {queryWrapper.like(VenueDO::getName, "%" + request.getName() + "%");}// 根据类型查询if (request.getType() != null) {queryWrapper.eq(VenueDO::getType, request.getType());}// 根据状态查询if (request.getStatus() != null) {queryWrapper.eq(VenueDO::getStatus, request.getStatus());}// 查询对方开放场馆,或者相同机构的场馆queryWrapper.eq(VenueDO::getIsOpen, 1).or().eq(VenueDO::getOrganizationId, UserContext.getOrganizationId());IPage<VenueDO> page = baseMapper.selectPage(new Page(request.getCurrent(), request.getSize()), queryWrapper);List<VenueRespDTO> venueRespDTOList = new ArrayList<>();for (VenueDO record : page.getRecords()) {VenueRespDTO venueRespDTO = new VenueRespDTO();BeanUtils.copyProperties(record, venueRespDTO);venueRespDTO.setTypeName(VenueTypeEnum.findValueByType(record.getType()));venueRespDTO.setStatusName(VenueStatusEnum.findValueByType(record.getStatus()));// 计算距离并设置到 DTO 中if (request.getLatitude() != null && request.getLongitude() != null) {double distance = DistanceUtil.calculateDistance(request.getLatitude().doubleValue(),request.getLongitude().doubleValue(),record.getLatitude().doubleValue(),record.getLongitude().doubleValue());venueRespDTO.setDistance(distance);}venueRespDTOList.add(venueRespDTO);}return new PageResponse(request.getCurrent(), request.getSize(), page.getTotal(), venueRespDTOList);
}/*** 根据经纬度和半径(公里)查询附近的场馆 ID** @param longitude 经度* @param latitude  纬度* @param radiusKm  半径(公里)* @return 附近的场馆 ID 列表*/
public List<Long> findVenuesWithinRadius(BigDecimal longitude, BigDecimal latitude, double radiusKm) {// 获取 GeoOperationsGeoOperations<String, String> geoOps = redisTemplate.opsForGeo();// 定义查询的中心点和半径Point center = new Point(longitude.doubleValue(), latitude.doubleValue());Distance distance = new Distance(radiusKm, Metrics.KILOMETERS);Circle circle = new Circle(center, distance);// 执行地理空间查询GeoResults<RedisGeoCommands.GeoLocation<String>> results = geoOps.radius(// Redis 中的 keyRedisCacheConstant.VENUE_LOCATION_KEY,circle);// 提取场馆 ID 并返回return results.getContent().stream().map(result ->{Long venueId = Long.parseLong(result.getContent().getName());
//                            double venueDistance = result.getDistance().getValue();
//                            System.out.println("场馆 ID: " + venueId + ", 距离: " + venueDistance + " 公里");return venueId;}).collect(Collectors.toList());
}

【工具类】

该工具列的作用是:给定两个经纬度,求它们之间的距离(单位:千米)

/*** 根据经纬度结算公里* @Author dam* @create 2025/1/28 19:39*/
public class DistanceUtil {/*** 地球半径,单位:公里*/private static final double EARTH_RADIUS = 6371;/*** 计算两个经纬度点之间的距离(公里)** @param lat1 纬度 1* @param lon1 经度 1* @param lat2 纬度 2* @param lon2 经度 2* @return 距离(公里)*/public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {double dLat = Math.toRadians(lat2 - lat1);double dLon = Math.toRadians(lon2 - lon1);double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)+ Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(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;}
}
http://www.yayakq.cn/news/58477/

相关文章:

  • 能上国外网站的dns信息科技公司网站怎么做
  • 广州住房保障城市建设局网站网站开发是
  • 音乐网站开发分享易语言如何做浏网站
  • 优秀的电商设计网站有哪些内容织梦网站系统删除不了
  • 域名到网站上线小程序平台开发多少钱
  • 南昌做网站哪个公司好大气红色礼品公司网站源码
  • 网页制作专业个人职业生涯规划书邢台谷歌seo
  • 网站建设硬件设计方案青白江网站建设
  • 做个网站出来要多少钱给人做网站赚钱
  • 自适应式网站模板品牌策划公司取名
  • 鄂州网站建设网络公司开元酒店集团品牌建设
  • 发行商城小程序沈阳seo排名优化软件
  • 菜单网站图片素材python做直播网站
  • 手机网站免费建设中国营销咨询公司排名
  • 产品网站用什么软件做软件开发平台设计
  • 马云是做网站的百度联盟怎么加入赚钱
  • 如何做网站的的关键词仿站网站源码
  • 电子商务网站建设详细策划书十堰今天刚刚发生新闻
  • 网站建设未验收会计账务处理微问数据平台入口
  • 网站备案登记表品牌全网推广
  • 改版一个网站多少钱wordpress安装插件无法创建目录
  • dw做的网站链接花生壳如何做网站
  • 学做网站要学什么新科网站建设
  • 网站的首页需要什么内容网站后台栏目管理
  • 封面型网页网站有哪些如何通过做网站赚钱
  • 广西网站建设设计广州网站设计营销公司
  • 网站建设维护员网站建设运营合作合同
  • 学做网站怎么样如何在网络上做广告
  • 免费开源的个人网站系统店铺推广引流
  • 网站建设找邓金平做三方网站多少钱