项目实战—-河豚快充宝

  • Post author:
  • Post category:其他


一. 项目介绍

本项目是采用SpringBoot、MongoDB、Redis等核心组件开发的共享充电宝微信小程序。

实现了模拟手机绑定、支付押金、身份验证、搜索附近充电宝等功能

二.用到的关键技术


数据库采用:MongoDB(可对地理位置进行索引

2Dsphere索引,用于存储和查找球面上的点


MongoDB是一个基于分布式文件存储的数据库。具有高性能、易部署、易使用,存储数据非常方便等特点。它支持的数据结构非常松散,是类似

json



bson

格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立

索引



SpringBoot整合时,通过已经封装好的dao

MongoTemplate 来Query 实现增删改查等功能,不需要在写dao层和sql语句

删除:Query query = new Query(Criteria.where(“id”).is(id)); mongoTemplate.remove(query, User.class);

增加:mongoTemplate.save(user);

修改:Query query = new Query(Criteria.where(“id”).is(user.getId()));

Update update = new Update().set(“userName”, user.getUserName()).set(“password”, user.getPassword());

mongoTemplate.updateFirst(query, update, User.class);

查询:Query query = new Query(Criteria.where(“userName”).is(userName));

return mongoTemplate.findOne(query, User.class);


缓存功能采用:Redis

redis是一个非关系型的数据库(not-only-sql即nosql),以键值对的方式存储数据,将数据存放在内存中,

存取速度快,

但是对持久化的支持不够好,所以redis一般配合关系型数据库使用,redis可以做

分布式缓存

,用在数据量大,高并发的情况下.redis通过很多命令进行操作,而且redis不适合保存内容大的数据。

本项目中使用redis作为短信验证码的缓存,当用户点击获取验证码时,后台自动生成一个4位随机数作为验证码

(int)((Math.random()*9+1)*1000)+””;Math.random()为double类型0.0到1.0的随机数。之后将验证码通过腾讯云的短信API发送给相应的手机号(这里由于需要小程序上线才能使用,因此模拟一下),发送成功后将手机号作为key,验证码作为value存储到redis中,

SpringBoot

有已经写好的dao层直接调用即可


stringRedisTemplate

.opsForValue().set(phoneNum, code,300,TimeUnit.SECONDS);并设置过期时间

注意redis的key过期淘汰策略:

  • 被动删除:当读/写一个已经过期的key时,会触发惰性删除策略,直接删除掉这个过期key
  • 主动删除:由于惰性删除策略无法保证冷数据被及时删掉,所以Redis会定期主动淘汰一批已过期的key
  • 当前已用内存超过maxmemory限定时,触发主动清理策略


项目的后端框架采用



SpringBoot:

是 Spring 开源组织下的子项目,是 Spring 组件一站式解决方案,主要是简化了使用 Spring 的难度,简省了繁重的配置,提供了各种启动器,开发者能快速上手

使用 Spring Boot 有什么好处

回顾我们之前的 SSM 项目,搭建过程还是比较繁琐的,需要:

  • 1)配置 web.xml,加载 spring 和 spring mvc
  • 2)配置数据库连接、配置日志文件
  • 3)配置加载配置文件的读取,开启注解
  • 4)配置mapper文件

  • …..

而使用 Spring Boot 来开发项目则只需要非常少的几个配置就可以搭建起来一个 Web 项目,并且利用 IDEA 可以自动生成生成,这简直是太爽了…

  • 划重点:

    简单、快速、方便地搭建项目

    ;对主流开发框架的

    无配置集成

    ;极大

    提高了开发、部署效率

三.难点创新点

(1)查找附近500米的充电宝位置,并且随着中心点的位置移动,自动显示500米内充电宝位置。

解决方法:基于MongoDB 2dSphere索引查找最近的点。MongoDB的地理空间索引。

地理空间索引采用的时geohash 算法:用一个字符串(数字和字母组成的)表示经度和纬度两个坐标。geohash 表示的并不是一个点,而是一个矩形区域,表示坐标位置附近的区域。




Geohash的原理


Geohash的最简单的解释就是:将一个经纬度信息,转换成一个可以排序,可以比较的字符串编码

(1)纬度转换:39.92324转为1011 1000 1100 0111 1001。

首先将纬度范围(-90, 90)平分成两个区间(-90,0)、(0, 90),如果目标纬度位于前一个区间,则编码为0,否则编码为1。

由于39.92324属于(0, 90),所以取编码为1。

然后再将(0, 90)分成 (0, 45), (45, 90)两个区间,而39.92324位于(0, 45),所以编码为0。

以此类推,直到精度符合要求为止,得到纬度编码为1011 1000 1100 0111 1001。

纬度范围

划分区间0

划分区间1

39.92324所属区间

(-90, 90)

(-90, 0.0)

(0.0, 90)

1

(0.0, 90)

(0.0, 45.0)

(45.0, 90)

0

(0.0, 45.0)

(0.0, 22.5)

(22.5, 45.0)

1

(22.5, 45.0)

(22.5, 33.75)

(33.75, 45.0)

1

(33.75, 45.0)

(33.75, 39.375)

(39.375, 45.0)

1

(39.375, 45.0)

(39.375, 42.1875)

(42.1875, 45.0)

0

(39.375, 42.1875)

(39.375, 40.7812)

(40.7812, 42.1875)

0

(39.375, 40.7812)

(39.375, 40.0781)

(40.0781, 40.7812)

0

(39.375, 40.0781)

(39.375, 39.7265)

(39.7265, 40.0781)

1

(39.7265, 40.0781)

(39.7265, 39.9023)

(39.9023, 40.0781)

1

(39.9023, 40.0781)

(39.9023, 39.9902)

(39.9902, 40.0781)

0

(39.9023, 39.9902)

(39.9023, 39.9462)

(39.9462, 39.9902)

0

(39.9023, 39.9462)

(39.9023, 39.9243)

(39.9243, 39.9462)

0

(39.9023, 39.9243)

(39.9023, 39.9133)

(39.9133, 39.9243)

1

(39.9133, 39.9243)

(39.9133, 39.9188)

(39.9188, 39.9243)

1

(39.9188, 39.9243)

(39.9188, 39.9215)

(39.9215, 39.9243)

1

(2)

经度也用同样的算法,对(-180, 180)依次细分,得到116.3906的编码为1101 0010 1100 0100 0100。

经度范围

划分区间0

划分区间1

116.3906所属区间

(-180, 180)

(-180, 0.0)

(0.0, 180)

1

(0.0, 180)

(0.0, 90.0)

(90.0, 180)

1

(90.0, 180)

(90.0, 135.0)

(135.0, 180)

0

(90.0, 135.0)

(90.0, 112.5)

(112.5, 135.0)

1

(112.5, 135.0)

(112.5, 123.75)

(123.75, 135.0)

0

(112.5, 123.75)

(112.5, 118.125)

(118.125, 123.75)

0

(112.5, 118.125)

(112.5, 115.312)

(115.312, 118.125)

1

(115.312, 118.125)

(115.312, 116.718)

(116.718, 118.125)

0

(115.312, 116.718)

(115.312, 116.015)

(116.015, 116.718)

1

(116.015, 116.718)

(116.015, 116.367)

(116.367, 116.718)

1

(116.367, 116.718)

(116.367, 116.542)

(116.542, 116.718)

0

(116.367, 116.542)

(116.367, 116.455)

(116.455, 116.542)

0

(116.367, 116.455)

(116.367, 116.411)

(116.411, 116.455)

0

(116.367, 116.411)

(116.367, 116.389)

(116.389, 116.411)

1

(116.389, 116.411)

(116.389, 116.400)

(116.400, 116.411)

0

(116.389, 116.400)

(116.389, 116.394)

(116.394, 116.400)

0

接下来将经度和纬度的编码合并,奇数位是纬度,偶数位是经度,得到编码 11100 11101 00100 01111 00000 01101 01011 00001。

最后,用0-9、b-z(去掉a, i, l, o)这32个字母进行base32编码,得到(39.92324, 116.3906)的编码为wx4g0ec1。

十进制

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

base32

0

1

2

3

4

5

6

7

8

9

b

c

d

e

f

g

十进制

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

base32

h

j

k

m

n

p

q

r

s

t

u

v

w

x

y

z

注意:由于编码方式形成的是”Z”字形曲线,这样就会存在拐点,说明编码越接近不一定地理位置越近,地理位置越近,编发不一定越近。因此不能完全根据编码决定是否接近。

这样就会存在以下的特点,假如所在位置为红色节点,绿色节点为充电宝的位置,由于上面绿色的与所在位置不在一个区域里,而下面绿色节点与红色节点在一个区域里,这样如果完全按照区域找的话,就会找到更远处的节点。

解决办法是:除了将本地区域的节点查找到,还要将区域周围的8个区域进行查询,具体实现步骤如下:

  1. 坐标值转化为GeoHash编码值
  2. 根据当前区域的GeoHash,推算出周围8个方位区域块的的GeoHash值。
  3. 将这8个区域块中所有节点进行储存,并且一一计算它们到当前坐标的距离,并且计算出最短距离的点。
  4. 考虑存储结构,以及算法实现。

NearQuery nearQuery = NearQuery.near(longitude, latitude).maxDistance(0.2, Metrics.KILOMETERS) .query(new Query(Criteria.where(“status”).is(0)).limit(20));

GeoResults<Kcb> geoResults=mongoTemplate.geoNear(nearQuery, Kcb.class);geoResults.getContent();



版权声明:本文为qq_41653753原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。