第9章 地址位置功能实现
今日内容介绍

学习目标
- 谁看过我的功能实现
- 我的喜欢的功能实现
- 上报地理位置功能实现
- 搜附近的功能实现
1. 谁看过我
【目标】
掌握谁看过我功能实现
【路径】
1:谁看过我功能分析
2:谁看过我功能实现
【讲解】
记录别人来访了我的主页的信息。
- 保存访问记录
- 查询访问记录
- 根据时间判断
1.1. 数据库

userId:被访问人的用户id
visitorUserId:访问者的用户id
1.2. 服务消费者-谁看过我

1.2.1. mock接口
地址: https://mock.boxuegu.com/project/164/interface/api/64750


1.2.2. VisitorVo
package com.tanhua.domain.vo;
import lombok.Data;
import java.io.Serializable;
@Data
public class VisitorVo implements Serializable {
private Long id;
private String avatar;
private String nickname;
private String gender;
private Integer age;
private String[] tags;
private Integer fateValue;
}
1.2.3. Visitor
package com.tanhua.domain.mongo;
import lombok.Data;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.mapping.Document;
import java.io.Serializable;
@Data
@Document(collection = "visitors")
public class Visitor implements Serializable {
private ObjectId id;
private Long userId; //我的id
private Long visitorUserId; //来访用户id
private String from; //来源,如首页、圈子等
private Long date; //来访时间
private Double score; //得分
}
1.2.4. MomentController
/**
* 谁看过我 /movements/visitors
*/
@RequestMapping(value = "/visitors",method = RequestMethod.GET)
public ResponseEntity findListVisitors() {
List<VisitorVo> list = movementsService.findListVisitors();
return ResponseEntity.ok(list);
}
1.2.5. MomentsService
/**
* 谁看过我
*/
public List<VisitorVo> findListVisitors() {
Long userId = UserHolder.getUserId();
String key ="visitors_time_"+userId;
//1 查询redis看当前用户上次访问时间是否存在
String redisTime = redisTemplate.opsForValue().get(key);
List<Visitor> visitorList = new ArrayList<>();
if(!StringUtils.isEmpty(redisTime)) {
//2 如果时间存在,根据表中date > redis中上次访问时间 + limit 5
visitorList = visitorsApi.findListVisitorsByTime(userId,Long.parseLong(redisTime));
}else {
//3 如果时间不存在, limit 5
visitorList = visitorsApi.findListVisitors(userId);
}
//4 将当前操作时间记录redis
redisTemplate.opsForValue().set(key,System.currentTimeMillis()+"");
if(visitorList == null){
return null;
}
//5.visitorList遍历(将 List<Visitor> 转为 List<VisitorVo>)
List<VisitorVo> visitorVoList = new ArrayList<>();
for (Visitor visitor : visitorList) {
VisitorVo visitorVo = new VisitorVo();
Long visitorUserId = visitor.getVisitorUserId();//访问的用户id
UserInfo userInfo = userInfoApi.findUserInfoByUserId(visitorUserId);//访客的用户信息
BeanUtils.copyProperties(userInfo,visitorVo);//avatar nickname gender age
visitorVo.setId(visitorUserId);//访客用户id
if(!StringUtils.isEmpty(userInfo.getTags())){
visitorVo.setTags(userInfo.getTags().split(","));//标签
}
visitorVo.setFateValue(visitor.getScore().intValue());//缘分值
visitorVoList.add(visitorVo);
}
return visitorVoList;
}
1.3. 服务提供者-谁看过我
1.3.1. VisitorApi
编写VisitorsApi
package com.tanhua.dubbo.api.mongo;
import com.tanhua.domain.mongo.Visitor;
import java.util.List;
/**
* 访问服务接口
*/
public interface VisitorsApi {
/**
* 根据当前用户id 和 上次登录时间 查询5条记录
* @param userId
* @param redisTime
* @return
*/
List<Visitor> findListVisitorsByTime(Long userId, Long redisTime);
/**
* 根据当前用户id 查询5条记录
* @param userId
* @return
*/
List<Visitor> findListVisitors(Long userId);
/**
* 保存访客记录
*/
void saveVisitor(Visitor visitor);
}
1.3.2. VisitorApiImpl
编写VisitorsApiImpl
package com.tanhua.dubbo.api.mongo;
import com.tanhua.domain.mongo.Visitor;
import org.apache.dubbo.config.annotation.Service;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import java.util.List;
/**
* 访客服务接口实现类
*/
@Service
public class VisitorsApiImpl implements VisitorsApi {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 根据当前用户id 和 上次登录时间 查询5条记录
* @param userId
* @param redisTime
* @return
*/
@Override
public List<Visitor> findListVisitorsByTime(Long userId, Long redisTime) {
Query query = new Query();
query.addCriteria(
Criteria.where("userId").is(userId)
.and("date").gt(redisTime)
);
query.limit(5);
query.with(Sort.by(Sort.Direction.DESC,"date"));
return mongoTemplate.find(query,Visitor.class);
}
/**
* 根据当前用户id 查询5条记录
* @param userId
* @return
*/
@Override
public List<Visitor> findListVisitors(Long userId) {
Query query = new Query();
query.addCriteria(
Criteria.where("userId").is(userId)
);
query.limit(5);
query.with(Sort.by(Sort.Direction.DESC,"date"));
return mongoTemplate.find(query,Visitor.class);
}
/**
* 保存访客记录
*/
@Override
public void saveVisitor(Visitor visitor) {
visitor.setId(ObjectId.get());
visitor.setDate(System.currentTimeMillis());
mongoTemplate.save(visitor);
}
}
1.3.3. 构造测试数据
package com.tanhua.dubbo.test;
import com.tanhua.domain.mongo.Visitor;
import com.tanhua.dubbo.api.mongo.VisitorsApi;
import org.apache.commons.lang3.RandomUtils;
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;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class TestVisitors {
@Autowired
private VisitorsApi visitorApi;
@Test
public void testSave(){
for (int i = 0; i < 100; i++) {
Visitor visitor = new Visitor();
visitor.setFrom("首页");
visitor.setUserId(130l);//用户id
visitor.setScore(77d);
visitor.setVisitorUserId(RandomUtils.nextLong(11,50));
this.visitorApi.saveVisitor(visitor);
}
System.out.println("ok");
}
}
1.3.4. 测试
postman测试:

app测试:

【小结】
掌握谁看过我功能实现
2. 我的喜欢
【目标】
掌握互相喜欢、喜欢、粉丝功能实现
【路径】
1: 互相喜欢、喜欢、粉丝统计
2: 查询相互喜欢、我喜欢、粉丝列表分页查询
3:粉丝-喜欢
【讲解】
在我的模块中,将详细展现“喜欢”相关的数据,如下:


2.1. 我的喜欢介绍
- 喜欢
- 我喜欢别人,如:张三喜欢李四,就是喜欢的数据,并不代表李四也喜欢张三。
- 粉丝
- 对于李四而言,张三就是他的粉丝。
- 相互关注(喜欢)
- 如果李四也喜欢张三,那么,张三和李四就是相互喜欢。两个人添加好友关系!
2.2. 服务消费者-互相喜欢、喜欢、粉丝统计

2.2.1. mock接口


2.2.2. CountsVo
package com.tanhua.domain.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;
@Data
@AllArgsConstructor
public class CountsVo implements Serializable {
private Long eachLoveCount; //互相喜欢
private Long loveCount; //喜欢
private Long fanCount; //粉丝
}
2.2.3. UserInfoController
/**
* 互相喜欢、喜欢、粉丝统计
*/
@RequestMapping(value = "/counts",method = RequestMethod.GET)
public ResponseEntity findCounts(){
CountsVo countsVo = userInfoService.findCounts();
return ResponseEntity.ok(countsVo);
}
2.2.4. UserService
/**
* 互相喜欢、喜欢、粉丝统计
*/
public CountsVo findCounts() {
Long userId = UserHolder.getUserId();
//1. 根据当前用户id查询好友表 互相喜欢数量
Long eachLoveCount = friendApi.findEachLoveCount(userId);
//2 根据当前用户id查询喜欢表 喜欢数量
Long loveCount = userLikeApi.findLoveCount(userId);
//3 根据当前用户id查询喜欢表 粉丝数量
Long fanCount = userLikeApi.findFanCount(userId);
return new CountsVo(eachLoveCount,loveCount,fanCount);
}
2.3. 服务提供者-互相喜欢、喜欢、粉丝统计
2.3.1. UserLike
package com.tanhua.domain.mongo;
import lombok.Data;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import java.io.Serializable;
@Data
@Document(collection = "user_like")
public class UserLike implements Serializable {
private ObjectId id;
@Indexed
private Long userId; //用户id,自己
@Indexed
private Long likeUserId; //喜欢的用户id,对方
private Long created; //创建时间
}
2.3.2. UserLikeApi
package com.tanhua.dubbo.api.mongo;
/**
* 喜欢服务接口
*/
public interface UserLikeApi {
/**
*喜欢数量
* @param userId
* @return
*/
Long findLoveCount(Long userId);
/**
* 粉丝数量
* @param userId
* @return
*/
Long findFanCount(Long userId);
}
2.2.3. UserLikeApiImpl
package com.tanhua.dubbo.api.mongo;
import com.tanhua.domain.mongo.UserLike;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
/**
* 喜欢服务接口
*/
@Service
public class UserLikeApiImpl implements UserLikeApi{
@Autowired
private MongoTemplate mongoTemplate;
/**
*喜欢数量
* @param userId
* @return
*/
@Override
public Long findLoveCount(Long userId) {
Query query = new Query();
query.addCriteria(Criteria.where("userId").is(userId));
return mongoTemplate.count(query, UserLike.class);
}
/**
* 粉丝数量
* @param userId
* @return
*/
@Override
public Long findFanCount(Long userId) {
Query query = new Query();
query.addCriteria(Criteria.where("likeUserId").is(userId));
return mongoTemplate.count(query, UserLike.class);
}
}
2.3.4 FriendApi
/**
*互相喜欢数量
* @param userId
* @return
*/
Long findEachLoveCount(Long userId);
2.3.5 FriendApiImpl
/**
*互相喜欢数量
* @param userId
* @return
*/
@Override
public Long findEachLoveCount(Long userId) {
Query query = new Query();
query.addCriteria(Criteria.where("userId").is(userId));
return mongoTemplate.count(query,Friend.class);
}
2.3.6. 测试
postman测试

app测试

2.4. 服务消费者-查询相互喜欢、我喜欢、粉丝列表
谁看过我 分页查询

2.4.1. 接口说明
该接口集成了4个接口,用type做了区分,如下:
1 互相关注 2 我关注 3 粉丝 4 谁看过我

2.4.2. FriendVo
package com.tanhua.domain.vo;
import lombok.Data;
import java.io.Serializable;
@Data
public class FriendVo implements Serializable {
private Long id;
private String avatar;
private String nickname;
private String gender;
private Integer age;
private String city;
private String education;
private Integer marriage; //婚姻状态(0未婚,1已婚)
private Integer matchRate; //匹配度
}
2.4.3. UserInfoController
/**
* 互相喜欢、喜欢、粉丝、谁看过我 - 翻页列表
* 1 互相关注
* 2 我关注
* 3 粉丝
* 4 谁看过我
*/
@RequestMapping(value = "/friends/{type}",method = RequestMethod.GET)
public ResponseEntity findPageFriend(@PathVariable("type") int type,@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int pagesize){
PageResult<FriendVo> friendVoPageResult = userInfoService.findPageFriend(type,page,pagesize);
return ResponseEntity.ok(friendVoPageResult);
}
2.4.4. UserService
/**
* 互相喜欢、喜欢、粉丝、谁看过我 - 翻页列表
* 1 互相关注
* 2 我关注
* 3 粉丝
* 4 谁看过我
*/
public PageResult<FriendVo> findPageFriend(int type, int page, int pagesize) {
Long userId = UserHolder.getUserId();
//1 调用服务 分页查询 互相喜欢、喜欢、粉丝、谁看过我(PageResult<RecommendUser)
PageResult<RecommendUser> pageResult = new PageResult<>();
switch (type){
case 1:
pageResult = userLikeApi.findPageLikeEachOther(userId,page,pagesize);
break;
case 2:
pageResult = userLikeApi.findPageOneSideLike(userId,page,pagesize);
break;
case 3:
pageResult = userLikeApi.findPageFens(userId,page,pagesize);
break;
case 4:
pageResult = userLikeApi.findPageMyVisitors(userId,page,pagesize);
break;
default: break;
}
if(pageResult == null || CollectionUtils.isEmpty(pageResult.getItems())){
return new PageResult<>(10l,10l,1l,1l,null);
}
//2 循环遍历List<RecommendUser> 查询tb_user_info
List<FriendVo> friendVoList = new ArrayList<>();
for (RecommendUser recommendUser : pageResult.getItems()) {
FriendVo friendVo = new FriendVo();
Long friendUserId = recommendUser.getUserId();///展示的用户id
UserInfo userInfo = userInfoApi.findUserInfoByUserId(friendUserId);
BeanUtils.copyProperties(userInfo,friendVo);//除了缘分值 都有
friendVo.setMatchRate(recommendUser.getScore().intValue());//缘分值
friendVo.setId(friendUserId);//展示的用户id(互相喜欢 喜欢 粉丝 谁看过我的用户id)
friendVoList.add(friendVo);
}
//3 构造vo返回
PageResult<FriendVo> voPageResult = new PageResult<>();
BeanUtils.copyProperties(pageResult,voPageResult); //copy counts pagesize pages page
voPageResult.setItems(friendVoList);//设置返回集合Vo
return voPageResult;
}
2.5. 服务提供者-查询相互喜欢、我喜欢、粉丝列表 谁看过我 分页查询
2.5.1. UserLikeApi
/**
* 互相喜欢分页查询
* @param userId
* @param page
* @param pagesize
* @return
*/
PageResult<RecommendUser> findPageLikeEachOther(Long userId, int page, int pagesize);
/**
* 我喜欢分页查询
* @param userId
* @param page
* @param pagesize
* @return
*/
PageResult<RecommendUser> findPageOneSideLike(Long userId, int page, int pagesize);
/**
*粉丝分页查询
* @param userId
* @param page
* @param pagesize
* @return
*/
PageResult<RecommendUser> findPageFens(Long userId, int page, int pagesize);
/**
* 谁看过我分页查询
* @param userId
* @param page
* @param pagesize
* @return
*/
PageResult<RecommendUser> findPageMyVisitors(Long userId, int page, int pagesize);
2.5.2. UserLikeApiImpl
/**
* 互相喜欢分页查询
* @param userId
* @param page
* @param pagesize
* @return
*/
@Override
public PageResult<RecommendUser> findPageLikeEachOther(Long userId, int page, int pagesize) {
//1.查询好友表总记录数
Query query = new Query();
query.addCriteria(Criteria.where("userId").is(userId));
query.limit(pagesize);
query.skip((page-1)*pagesize);
long counts = mongoTemplate.count(query, Friend.class);
//2 分页查询当前页面需要展示的数据
List<Friend> friendList = mongoTemplate.find(query, Friend.class);
if(CollectionUtils.isEmpty(friendList)){
return null;
}
//3.根据好友表中查询好友id再查询推荐用户表 得到缘分值
List<RecommendUser> recommendUserList = new ArrayList<>();
for (Friend friend : friendList) {
Long currentUserId = friend.getUserId(); //当前用户id
Long friendId = friend.getFriendId();//好友用户id
recommendUserList.add(getRecommendUser(currentUserId, friendId));
}
//4.构造PageResult<RecommendUser>分页对象返回
long pages = counts/pagesize + (counts%pagesize > 0 ?1:0);
return new PageResult<>(counts,(long)pagesize,pages,(long)page,recommendUserList);
}
/**
* 我喜欢分页查询
* @param userId
* @param page
* @param pagesize
* @return
*/
@Override
public PageResult<RecommendUser> findPageOneSideLike(Long userId, int page, int pagesize) {
//1.查询好友表总记录数
Query query = new Query();
query.addCriteria(Criteria.where("userId").is(userId));
query.limit(pagesize);
query.skip((page-1)*pagesize);
long counts = mongoTemplate.count(query, UserLike.class);
//2 分页查询当前页面需要展示的数据
List<UserLike> userLikeList = mongoTemplate.find(query, UserLike.class);
if(CollectionUtils.isEmpty(userLikeList)){
return null;
}
//3.根据好友表中查询好友id再查询推荐用户表 得到缘分值
List<RecommendUser> recommendUserList = new ArrayList<>();
for (UserLike userLike : userLikeList) {
Long currentUserId = userLike.getUserId(); //当前用户id
Long friendId = userLike.getLikeUserId();//好友用户id
recommendUserList.add(getRecommendUser(currentUserId, friendId));
}
//4.构造PageResult<RecommendUser>分页对象返回
long pages = counts/pagesize + (counts%pagesize > 0 ?1:0);
return new PageResult<>(counts,(long)pagesize,pages,(long)page,recommendUserList);
}
/**
* 互相喜欢等分页公共方法
* @param currentUserId
* @param friendId
* @return
*/
private RecommendUser getRecommendUser(Long currentUserId, Long friendId) {
Query recommendQuery = new Query();
recommendQuery.addCriteria(Criteria.where("userId").is(friendId).and("toUserId").is(currentUserId));
RecommendUser recommendUser = mongoTemplate.findOne(recommendQuery, RecommendUser.class);
if (recommendUser == null || recommendUser.getScore() == null) {
//设置默认值
recommendUser = new RecommendUser();
recommendUser.setScore(66.6);//默认缘分值
recommendUser.setToUserId(currentUserId);//当前登录用户id
recommendUser.setUserId(friendId);//好友id
}
return recommendUser;
}
/**
*粉丝分页查询
* @param userId
* @param page
* @param pagesize
* @return
*/
@Override
public PageResult<RecommendUser> findPageFens(Long userId, int page, int pagesize) {
//1.查询好友表总记录数
Query query = new Query();
query.addCriteria(Criteria.where("likeUserId").is(userId));
query.limit(pagesize);
query.skip((page-1)*pagesize);
long counts = mongoTemplate.count(query, UserLike.class);
//2 分页查询当前页面需要展示的数据
List<UserLike> userLikeList = mongoTemplate.find(query, UserLike.class);
if(CollectionUtils.isEmpty(userLikeList)){
return null;
}
//3.根据好友表中查询好友id再查询推荐用户表 得到缘分值
List<RecommendUser> recommendUserList = new ArrayList<>();
for (UserLike userLike : userLikeList) {
Long currentUserId = userLike.getLikeUserId(); //当前用户id
Long friendId = userLike.getUserId();//好友用户id
recommendUserList.add(getRecommendUser(currentUserId, friendId));
}
//4.构造PageResult<RecommendUser>分页对象返回
long pages = counts/pagesize + (counts%pagesize > 0 ?1:0);
return new PageResult<>(counts,(long)pagesize,pages,(long)page,recommendUserList);
}
/**
* 谁看过我分页查询
* @param userId
* @param page
* @param pagesize
* @return
*/
@Override
public PageResult<RecommendUser> findPageMyVisitors(Long userId, int page, int pagesize) {
//1.查询好友表总记录数
Query query = new Query();
query.addCriteria(Criteria.where("userId").is(userId));
query.limit(pagesize);
query.skip((page-1)*pagesize);
long counts = mongoTemplate.count(query, Visitor.class);
//2 分页查询当前页面需要展示的数据
List<Visitor> visitorList = mongoTemplate.find(query, Visitor.class);
if(CollectionUtils.isEmpty(visitorList)){
return null;
}
//3.根据好友表中查询好友id再查询推荐用户表 得到缘分值
List<RecommendUser> recommendUserList = new ArrayList<>();
for (Visitor visitor : visitorList) {
Long currentUserId = visitor.getUserId(); //当前用户id
Long friendId = visitor.getVisitorUserId();//好友用户id
recommendUserList.add(getRecommendUser(currentUserId, friendId));
}
//4.构造PageResult<RecommendUser>分页对象返回
long pages = counts/pagesize + (counts%pagesize > 0 ?1:0);
return new PageResult<>(counts,(long)pagesize,pages,(long)page,recommendUserList);
}
2.5.3. 测试
postman测试

app测试





2.6. 服务消费者-喜欢

2.6.1. UserInfoController
/**
* 粉丝-喜欢
* 请求连接:POST /fans/:uid
*/
@PostMapping("/fans/{id}")
public ResponseEntity fansLike(@PathVariable("id") Long likeUserId) {
return userService.fansLike(likeUserId);
}
2.6.2. UserService
@Reference
private FriendApi friendApi;
/**
* 对关注我的粉丝,进行喜欢操作
* likeUserId : 粉丝的用户id
*/
public ResponseEntity fansLike(Long likeUserId) {
//1、删除粉丝的关注数据
userLikeApi.delete(likeUserId,UserHolder.getUserId());
//2、记录双向的好友关系
friendApi.add(UserHolder.getUserId(),likeUserId);
//3、注册好友关系到环信
huanXinTemplate.makeFriends(UserHolder.getUserId(),likeUserId);
return ResponseEntity.ok(null);
}
2.7. 服务提供者-喜欢
2.7.1. UserLikeApi
//喜欢
void delete(Long userId, Long likeUserId);
2.7.2. UserLikeApiImpl
@Override
public void delete(Long userId, Long likeUserId) {
Query query = new Query(
Criteria.where("userId").is(userId)
.and("likeUserId").is(likeUserId)
);
mongoTemplate.remove(query, UserLike.class);
}
2.7.3. 测试

【小结】
掌握互相喜欢、喜欢、粉丝功能实现
3. 地理位置(重点)
【目标】
掌握地址位置功能实现
【路径】
1: 地址位置功能分析
2: 地址位置功能实现
【讲解】
客户端检测用户的地理位置,当变化大于500米时或每隔5分钟,向服务端发送地理位置。
3.1. 服务消费者-地址位置

3.1.1. 接口说明

3.1.2. UserLocationController
package com.tanhua.server.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
/**
* 搜附近-控制层
*/
@RestController
@RequestMapping("/baidu")
public class UserLocationController {
@Autowired
private UserLocationService userLocationService;
/**
* 上报地理位置
*/
@RequestMapping(value = "/location",method = RequestMethod.POST)
public ResponseEntity saveUserLocation(@RequestBody Map params){
Double latitude =(Double) params.get("latitude");///纬度
Double longitude =(Double) params.get("longitude");//经度
String addrStr = (String)params.get("addrStr");//位置描述
userLocationService.saveUserLocation(latitude,longitude,addrStr);
return ResponseEntity.ok(null);
}
}
3.1.3. UserLocationService
package com.tanhua.server.service;
import com.tanhua.server.interceptor.UserHolder;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 搜附近业务逻辑处理层
*/
@Service
@Transactional
public class UserLocationService {
@Reference
private UserLocationApi userLocationApi;
/**
* 上报地理位置
*/
public void saveUserLocation(Double latitude, Double longitude, String addrStr) {
//调用服务 上报地理位置(保存地址 或 更新地址)
userLocationApi.saveUserLocation(latitude, longitude, addrStr, UserHolder.getUserId());
}
}
3.2. 服务提供者-地址位置
3.2.1. UserLocation
package com.tanhua.domain.mongo;
import lombok.Data;
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;
import java.io.Serializable;
@Data
@Document(collection = "user_location")
@CompoundIndex(name = "location_index", def = "{'location': '2dsphere'}")
public class UserLocation implements Serializable {
@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; //上次更新时间
}
3.2.2. UserLocationApi
package com.tanhua.dubbo.api.mongo;
import com.tanhua.domain.vo.UserLocationVo;
import java.util.List;
/**
* 搜附近服务接口
*/
public interface UserLocationApi {
/**
* 上报地理位置
*/
void saveUserLocation(Double latitude, Double longitude, String addrStr, Long userId);
}
3.2.3. UserLocationApiImpl
package com.tanhua.dubbo.api.mongo;
import com.tanhua.domain.mongo.UserLocation;
import com.tanhua.domain.vo.UserLocationVo;
import org.apache.dubbo.config.annotation.Service;
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
public class UserLocationApiImpl implements UserLocationApi{
@Autowired
private MongoTemplate mongoTemplate;
/**
* 上报地理位置
*
* latitude //纬度
* longitude //经度
* “x表示当地的纬度,y表示当地的经度
*/
@Override
public void saveUserLocation(Double latitude, Double longitude, String addrStr, Long userId) {
//1.根据当前登录用户id 查询用户地理位置数据是否存在
Query query = new Query();
query.addCriteria(Criteria.where("userId").is(userId));
long nowTime = System.currentTimeMillis();
if(!mongoTemplate.exists(query, UserLocation.class)) {
//2 如果不存在,则直接保存当前用户地理位置
UserLocation userLocation = new UserLocation();
userLocation.setId(ObjectId.get());//主键id
userLocation.setUserId(userId);//当前用户id
//double x, double y
userLocation.setLocation(new GeoJsonPoint(latitude,longitude));
userLocation.setAddress(addrStr);//地理位置中文
userLocation.setCreated(nowTime);
userLocation.setUpdated(nowTime);
userLocation.setLastUpdated(nowTime);
mongoTemplate.insert(userLocation);
}else {
//3 如果存在,则直接更新当前用户地理位置
Query updateQuery = new Query();
query.addCriteria(Criteria.where("userId").is(userId));
Update update = new Update();
update.set("location",new GeoJsonPoint(latitude,longitude));
update.set("address",addrStr);
update.set("updated",nowTime);
update.set("lastUpdated",nowTime);
mongoTemplate.updateFirst(updateQuery,update,UserLocation.class);
}
}
}
3.2.4. 测试
坐标拾取系统:http://api.map.baidu.com/lbsapi/getpoint/index.html

造测试数据
tanhua-dubbo-service模块test包下
package com.tanhua.dubbo.test;
import com.tanhua.dubbo.api.mongo.UserLocationApi;
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.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserLocationTest {
@Autowired
private UserLocationApi userLocationApi;
/**
* latitude //纬度
* longitude //经度
* “x表示当地的纬度 > y表示当地的经度
*/
@Test
public void saveUserLocation(){
userLocationApi.saveUserLocation(22.582111,113.929778,"深圳黑马程序员",1l);
userLocationApi.saveUserLocation(113.929778,22.582111,"深圳黑马程序员",1l);
userLocationApi.saveUserLocation(113.925528,22.587995,"红荔村肠粉",2l);
userLocationApi.saveUserLocation(113.93814,22.562578,"深圳南头直升机场",3l);
userLocationApi.saveUserLocation(114.064478,22.549528,"深圳市政府",4l);
userLocationApi.saveUserLocation(113.986074,22.547726,"欢乐谷",5l);
userLocationApi.saveUserLocation(113.979399,22.540746,"世界之窗",6l);
userLocationApi.saveUserLocation(114.294924,22.632275,"东部华侨城",7l);
userLocationApi.saveUserLocation(114.314011,22.598196,"大梅沙海滨公园",8l);
userLocationApi.saveUserLocation(113.821705,22.638172,"深圳宝安国际机场",9l);
userLocationApi.saveUserLocation(113.912386,22.566223,"海雅缤纷城(宝安店)",10l);
}
}
【小结】
掌握地址位置功能实现
4、搜附近(重点)
【目标】
掌握搜附近功能实现
【路径】
1: 搜附近功能分析
2: 搜附近功能实现
【讲解】
在首页中点击“搜附近”可以搜索附近的好友,效果如下:

4.1. 服务消费者-搜附近

4.1.1. 接口说明


4.1.2. NearUserVo
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;
}
4.1.3. UserLocationVo
由于UserLocation不能序列化,所以要再定义UserLocationVo进行返回数据。
package com.tanhua.domain.vo;
import com.tanhua.domain.mongo.UserLocation;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Data
public class UserLocationVo implements Serializable {
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;
}
}
4.1.3. UserLocationController
/**
* 搜附近
*/
@RequestMapping(value = "/tanhua/search",method = RequestMethod.GET)
public ResponseEntity searchNearUser(@RequestParam(required=false) String gender, @RequestParam(defaultValue = "2000") String distance){
List<NearUserVo> nearUserVoList = userLocationService.searchNearUser(gender,distance);
return ResponseEntity.ok(nearUserVoList);
}
4.1.4. UserLocationService
/**
* 搜附近
*/
public List<NearUserVo> searchNearUser(String gender, String distance) {
Long userId = UserHolder.getUserId();
//1.调用服务获取附近用户列表数据
List<UserLocationVo> userLocationVoList = userLocationApi.searchNearUser(distance,userId);
if(CollectionUtils.isEmpty(userLocationVoList)){
return null;
}
//2.将当前用户id过滤
List<NearUserVo> nearUserVoList = new ArrayList<>();
for (UserLocationVo userLocationVo : userLocationVoList) {
NearUserVo nearUserVo = new NearUserVo();
if(userLocationVo.getUserId().equals(userId)){
//跳过当条记录,继续循环
continue;
}
//3.查询附近用户信息
UserInfo userInfo = userInfoApi.findUserInfoByUserId(userLocationVo.getUserId());
//4.将附近用户的性别不符合要求的数据过滤
if(!StringUtils.isEmpty(gender)){
if(!gender.equals(userInfo.getGender())){
//跳过用户选择的性别 跟userInfo中 性别不一样
continue;
}
}
BeanUtils.copyProperties(userInfo,nearUserVo);//头像 昵称
nearUserVo.setUserId(userInfo.getId());//附近用户的id
nearUserVoList.add(nearUserVo);
}
return nearUserVoList;
}
4.2. 服务提供者-搜附近
4.2.1. UserLocationApi
/**
* 搜附近
*/
List<UserLocationVo> searchNearUser(String distance, Long userId);
4.2.3. UserLocationApiImpl
/**
* 搜附近
*
* distance:单位 米m ==> 千米 km
*/
@Override
public List<UserLocationVo> searchNearUser(String distance, Long userId) {
//1 根据当前用户id获取当前用户的位置
Query query = new Query();
query.addCriteria(Criteria.where("userId").is(userId));
UserLocation userLocation = mongoTemplate.findOne(query, UserLocation.class);
GeoJsonPoint location = userLocation.getLocation();
//2 根据当前用户位置 和 距离 搜索附近的用户
Circle circle = new Circle(location,new Distance(Double.parseDouble(distance)/1000, Metrics.KILOMETERS));
Query nearQuery = new Query();
nearQuery.addCriteria(Criteria.where("location").withinSphere(circle));
List<UserLocation> userLocationList = mongoTemplate.find(nearQuery, UserLocation.class);
//3.将List<UserLocation> 转换为 List<UserLocationVo>
return UserLocationVo.formatToList(userLocationList);
}
4.2.4. 测试
postman测试

app测试


【小结】
掌握搜附近功能实现
作业
1.谁看过我-保存记录到访客表
2.喜欢-功能实现
总结
1.谁看过我
2.搜附近
3.互相喜欢、喜欢、粉丝统计
4.互相喜欢、喜欢、粉丝统计、谁看过我分页列表查询
5.粉丝-喜欢
浙公网安备 33010602011771号