地理位置服务:MongoDB实现 - 使用样例

地理位置服务:MongoDB实现 - 使用样例

1 更新用户地理位置

客户端检测用户的地理位置,当变化大于500米时或每隔5分钟,向服务端发送地理位置。

dubbo - pojo

package com.tanhua.dubbo.server.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.index.CompoundIndex;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "user_location")
@CompoundIndex(name = "location_index", def = "{'location': '2dsphere'}")
public class UserLocation implements java.io.Serializable{

    private static final long serialVersionUID = 4508868382007529970L;

    @Id
    private ObjectId id;
    @Indexed
    private Long userId; //用户id
    private GeoJsonPoint location; //x:经度 y:纬度
    private String address; //位置描述
    private Long created; //创建时间
    private Long updated; //更新时间
    private Long lastUpdated; //上次更新时间

}

dubbo - 接口

package com.tanhua.dubbo.server.api;

public interface UserLocationApi {

    /**
     * 更新用户地理位置
     *
     * @return
     */
    String updateUserLocation(Long userId, Double longitude, Double latitude, String address);

}

dubbo - 实现

package com.tanhua.dubbo.server.api;

import com.alibaba.dubbo.config.annotation.Service;
import com.tanhua.dubbo.server.pojo.UserLocation;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

@Service(version = "1.0.0")
public class UserLocationApiImpl implements UserLocationApi {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Override
    public String updateUserLocation(Long userId, Double longitude, Double latitude, String address) {

        UserLocation userLocation = new UserLocation();
        userLocation.setAddress(address);
        userLocation.setLocation(new GeoJsonPoint(longitude, latitude));
        userLocation.setUserId(userId);

        Query query = Query.query(Criteria.where("userId").is(userLocation.getUserId()));
        UserLocation ul = this.mongoTemplate.findOne(query, UserLocation.class);
        if (ul == null) {
            //新增
            userLocation.setId(ObjectId.get());
            userLocation.setCreated(System.currentTimeMillis());
            userLocation.setUpdated(userLocation.getCreated());
            userLocation.setLastUpdated(userLocation.getCreated());

            this.mongoTemplate.save(userLocation);

            return userLocation.getId().toHexString();
        } else {
            //更新
            Update update = Update
                    .update("location", userLocation.getLocation())
                    .set("updated", System.currentTimeMillis())
                    .set("lastUpdated", ul.getUpdated());
            this.mongoTemplate.updateFirst(query, update, UserLocation.class);
        }

        return ul.getId().toHexString();
    }
    
}

server - APP服务接口实现

package com.tanhua.server.controller;


import com.tanhua.server.service.BaiduService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
@RequestMapping("baidu")
public class BaiduController {

    @Autowired
    private BaiduService baiduService;

    /**
     * 更新位置
     *
     * @param param
     * @return
     */
    @PostMapping("location")
    public ResponseEntity<Void> updateLocation(@RequestBody Map<String, Object> param) {
        try {
            Double longitude = Double.valueOf(param.get("longitude").toString());
            Double latitude = Double.valueOf(param.get("latitude").toString());
            String address = param.get("addrStr").toString();

            Boolean bool = this.baiduService.updateLocation(longitude, latitude, address);
            if (bool) {
                return ResponseEntity.ok(null);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }
}
package com.tanhua.server.service;

import com.alibaba.dubbo.config.annotation.Reference;
import com.tanhua.dubbo.server.api.UserLocationApi;
import com.tanhua.server.pojo.User;
import com.tanhua.server.utils.UserThreadLocal;
import org.springframework.stereotype.Service;

@Service
public class BaiduService {

    @Reference(version = "1.0.0")
    private UserLocationApi userLocationApi;

    public Boolean updateLocation(Double longitude, Double latitude, String address) {
        try {
            User user = UserThreadLocal.get();
            this.userLocationApi.updateUserLocation(user.getId(), longitude, latitude, address);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

}

2 搜附近功能

在首页中点击“搜附近”可以搜索附近的好友,效果如下:

dubbo - 接口

package com.tanhua.dubbo.server.api;

import com.tanhua.dubbo.server.vo.UserLocationVo;

import java.util.List;

public interface UserLocationApi {

    /**
     * 更新用户地理位置
     *
     * @return
     */
    String updateUserLocation(Long userId, Double longitude, Double latitude, String address);

    /**
     * 查询用户地理位置
     *
     * @param userId
     * @return
     */
    UserLocationVo queryByUserId(Long userId);

    /**
     * 根据地理位置查询用户
     *
     * @param longitude
     * @param latitude
     * @return
     */
    List<UserLocationVo> queryUserFromLocation(Double longitude, Double latitude, Integer range);
}

由于UserLocation不能序列化,所以要再定义UserLocationVo进行返回数据。

package com.tanhua.dubbo.server.vo;

import com.tanhua.dubbo.server.pojo.UserLocation;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserLocationVo implements java.io.Serializable {

    private static final long serialVersionUID = 4133419501260037769L;

    private String id;
    private Long userId; //用户id
    private Double longitude; //经度
    private Double latitude; //维度
    private String address; //位置描述
    private Long created; //创建时间
    private Long updated; //更新时间
    private Long lastUpdated; //上次更新时间

    public static final UserLocationVo format(UserLocation userLocation) {
        UserLocationVo userLocationVo = new UserLocationVo();
        userLocationVo.setAddress(userLocation.getAddress());
        userLocationVo.setCreated(userLocation.getCreated());
        userLocationVo.setId(userLocation.getId().toHexString());
        userLocationVo.setLastUpdated(userLocation.getLastUpdated());
        userLocationVo.setUpdated(userLocation.getUpdated());
        userLocationVo.setUserId(userLocation.getUserId());
        userLocationVo.setLongitude(userLocation.getLocation().getX());
        userLocationVo.setLatitude(userLocation.getLocation().getY());
        return userLocationVo;
    }

    public static final List<UserLocationVo> formatToList(List<UserLocation> userLocations) {
        List<UserLocationVo> list = new ArrayList<>();
        for (UserLocation userLocation : userLocations) {
            list.add(format(userLocation));
        }
        return list;
    }
}

dubbo - 实现

package com.tanhua.dubbo.server.api;

import com.alibaba.dubbo.config.annotation.Service;
import com.tanhua.dubbo.server.pojo.UserLocation;
import com.tanhua.dubbo.server.vo.UserLocationVo;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import java.util.List;

@Service(version = "1.0.0")
public class UserLocationApiImpl implements UserLocationApi {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Override
    public UserLocationVo queryByUserId(Long userId) {
        Query query = Query.query(Criteria.where("userId").is(userId));
        UserLocation userLocation = this.mongoTemplate.findOne(query, UserLocation.class);
        if (userLocation != null) {
            return UserLocationVo.format(userLocation);
        }
        return null;
    }

    @Override
    public List<UserLocationVo> queryUserFromLocation(Double longitude, Double latitude, Integer range) {

        //中心点
        GeoJsonPoint geoJsonPoint = new GeoJsonPoint(longitude, latitude);

        //转换为2dsphere的距离
        Distance distance = new Distance(range / 1000, Metrics.KILOMETERS);

        //画一个圆
        Circle circle = new Circle(geoJsonPoint, distance);
        Query query = Query.query(Criteria.where("location").withinSphere(circle));
        return UserLocationVo.formatToList(this.mongoTemplate.find(query, UserLocation.class));
    }
}

dubbo - 测试

坐标查询: http://api.map.baidu.com/lbsapi/getpoint/

package com.tanhua.dubbo.server.api;

import com.tanhua.dubbo.server.vo.UserLocationVo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class TestUserLocationApi {

    @Autowired
    private UserLocationApi userLocationApi;

    @Test
    public void testQuery() {
        Double longitude = 121.512253;
        Double latitude = 31.24094;
        Integer range = 1000;
        List<UserLocationVo> userLocationVos = this.userLocationApi.queryUserFromLocation(longitude, latitude, range);
        for (UserLocationVo userLocationVo : userLocationVos) {
            System.out.println(userLocationVo);
        }
    }

    @Test
    public void testSaveTestData() {
        this.userLocationApi.updateUserLocation(1L, 121.512253,31.24094, "金茂大厦");
        this.userLocationApi.updateUserLocation(2L, 121.506377, 31.245105, "东方明珠广播电视塔");
        this.userLocationApi.updateUserLocation(10L, 121.508815,31.243844, "陆家嘴地铁站");
        this.userLocationApi.updateUserLocation(12L, 121.511999,31.239185, "上海中心大厦");
        this.userLocationApi.updateUserLocation(25L, 121.493444,31.240513, "上海市公安局");
        this.userLocationApi.updateUserLocation(27L, 121.494108,31.247011, "上海外滩美术馆");
        this.userLocationApi.updateUserLocation(30L, 121.462452,31.253463, "上海火车站");
        this.userLocationApi.updateUserLocation(32L, 121.81509,31.157478, "上海浦东国际机场");
        this.userLocationApi.updateUserLocation(34L, 121.327908,31.20033, "虹桥火车站");
        this.userLocationApi.updateUserLocation(38L, 121.490155,31.277476, "鲁迅公园");
        this.userLocationApi.updateUserLocation(40L, 121.425511,31.227831, "中山公园");
        this.userLocationApi.updateUserLocation(43L, 121.594194,31.207786, "张江高科");
    }

}

server - controller

1571993019268

1571993038155

/**
     * 搜附近
     *
     * @param gender
     * @param distance
     * @return
     */
@GetMapping("search")
public ResponseEntity<List<NearUserVo>> queryNearUser(@RequestParam(value = "gender", required = false) String gender,
                                                      @RequestParam(value = "distance", defaultValue = "2000") String distance) {
    try {
        List<NearUserVo> list = this.todayBestService.queryNearUser(gender, distance);
        return ResponseEntity.ok(list);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
package com.tanhua.server.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class NearUserVo {

    private Long userId;
    private String avatar;
    private String nickname;

}

service

@Reference(version = "1.0.0")
private UserLocationApi userLocationApi;

public List<NearUserVo> queryNearUser(String gender, String distance) {
    User user = UserThreadLocal.get();
    // 查询当前用户的位置信息
    UserLocationVo userLocationVo = this.userLocationApi.queryByUserId(user.getId());
    Double longitude = userLocationVo.getLongitude();
    Double latitude = userLocationVo.getLatitude();

    // 根据当前用户的位置信息查询附近的好友
    List<UserLocationVo> userLocationList = this.userLocationApi
        .queryUserFromLocation(longitude, latitude, Integer.valueOf(distance));

    if (CollectionUtils.isEmpty(userLocationList)) {
        return Collections.emptyList();
    }

    List<Long> userIds = new ArrayList<>();
    for (UserLocationVo locationVo : userLocationList) {
        userIds.add(locationVo.getUserId());
    }

    QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
    queryWrapper.in("user_id", userIds);
    if (StringUtils.equalsIgnoreCase(gender, "man")) {
        queryWrapper.in("sex", SexEnum.MAN);
    } else if (StringUtils.equalsIgnoreCase(gender, "woman")) {
        queryWrapper.in("sex", SexEnum.WOMAN);
    }

    List<UserInfo> userInfoList = this.userInfoService.queryUserInfoList(queryWrapper);

    List<NearUserVo> nearUserVoList = new ArrayList<>();

    for (UserLocationVo locationVo : userLocationList) {

        if (locationVo.getUserId().longValue() == user.getId().longValue()) {
            // 排除自己
            continue;
        }

        for (UserInfo userInfo : userInfoList) {
            if (locationVo.getUserId().longValue() == userInfo.getUserId().longValue()) {
                NearUserVo nearUserVo = new NearUserVo();

                nearUserVo.setUserId(userInfo.getUserId());
                nearUserVo.setAvatar(userInfo.getLogo());
                nearUserVo.setNickname(userInfo.getNickName());

                nearUserVoList.add(nearUserVo);
                break;
            }
        }
    }

    return nearUserVoList;
}

测试

1571995491106

1571995502064

1572011224361

posted @ 2021-01-26 11:30  60kmph  阅读(270)  评论(0编辑  收藏  举报