探花交友_第8章_搜附近以及探花功能实现

文章目录

  • 探花交友_第8章_搜附近以及探花功能实现
    • 1、上报地理位置
      • 1.1、dubbo服务
        • 1.1.1、创建工程
        • 1.1.2、定义pojo
        • 1.1.3、定义dubbo接口
        • 1.1.4、编写实现
        • 1.1.5、单元测试
      • 1.2、APP接口
        • 1.2.1、BaiduController
        • 1.2.2、BaiduService
      • 1.3、测试
    • 在这里插入图片描述
    • 2、搜附近
      • 2.1、dubbo服务
        • 2.1.1、定义接口方法
        • 2.1.2、编写实现
        • 2.1.3、单元测试
      • 2.2、APP接口服务
        • 2.2.1、NearUserVo
        • 2.2.2、TanHuaController
        • 2.2.3、TanHuaService
        • 2.2.4、测试
    • 3、探花
      • 3.1、喜欢的dubbo服务
        • 3.1.1、实体对象
        • 3.1.2、定义接口
        • 3.1.3、编写实现
        • 3.1.4、单元测试
      • 3.2、查询推荐列表dubbo服务
        • 3.2.1、定义接口
        • 3.2.2、编写实现
        • 3.2.3、单元测试
      • 3.3、查询推荐列表APP接口实现
        • 3.3.1、TanHuaController
        • 3.3.2、TanHuaService
        • 3.3.3、测试
      • 3.4、左滑右滑
        • 3.4.1、TanHuaController
        • 3.4.2、TanHuaService
        • 3.4.3、测试
    • 4、用户资料
      • 4.1、基本信息
        • 4.4.1、接口信息
        • 在这里插入图片描述
        • 4.4.2、MyCenterController
        • 4.4.3、MyCenterService
        • 4.4.4、UserInfoService
        • 4.4.5、bug修复
      • 4.2、更新头像
        • 4.2.2、MyCenterController
  • 上报地位位置
  • 实现搜附近功能
  • 实现探花功能
  • 用户基本信息维护

1、上报地理位置

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

用户的地理位置存储到Elasticsearch中,需要使用环境提供的ES集群,如下:

1.1、dubbo服务

用户地理位置的服务独立一个新的工程来实现,名字为:my-tanhua-dubbo-es。

1.1.1、创建工程

pom.ml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>my-tanhua-dubbo</artifactId><groupId>cn.itcast.tanhua</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>my-tanhua-dubbo-es</artifactId><dependencies><!--引入interface依赖--><dependency><groupId>cn.itcast.tanhua</groupId><artifactId>my-tanhua-dubbo-interface</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency><!--dubbo的springboot支持--><dependency><groupId>com.alibaba.boot</groupId><artifactId>dubbo-spring-boot-starter</artifactId></dependency><!--dubbo框架--><dependency><groupId>com.alibaba</groupId><artifactId>dubbo</artifactId></dependency><!--zk依赖--><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId></dependency><dependency><groupId>com.github.sgroschupf</groupId><artifactId>zkclient</artifactId></dependency><dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId></dependency></dependencies>
</project>

application.properties文件:

# Spring boot application
spring.application.name = itcast-tanhua-dubbo-es# dubbo 扫描包配置
dubbo.scan.basePackages = com.tanhua.dubbo.es
dubbo.application.name = dubbo-provider-es#dubbo 对外暴露的端口信息
dubbo.protocol.name = dubbo
dubbo.protocol.port = 20882#dubbo注册中心的配置
dubbo.registry.address = zookeeper://192.168.31.81:2181
dubbo.registry.client = zkclient
dubbo.registry.timeout = 60000 #ES集群配置
spring.data.elasticsearch.cluster-name=es-tanhua-cluster
spring.data.elasticsearch.cluster-nodes=192.168.31.81:9300,192.168.31.81:9301,192.168.31.81:9302

启动类:

package com.tanhua.dubbo.es;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;@SpringBootApplication(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class}) //排除mongo的自动配置
public class ESApplication {public static void main(String[] args) {SpringApplication.run(ESApplication.class, args);}
}

1.1.2、定义pojo

在my-tanhua-dubbo-interface中创建:

package com.tanhua.dubbo.server.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.elasticsearch.common.geo.GeoPoint;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.GeoPointField;@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(indexName = "tanhua", type = "user_location", shards = 6, replicas = 2)
public class UserLocation {@Idprivate Long userId; //用户id@GeoPointFieldprivate GeoPoint location; //lon:经度 lat:纬度@Field(type = FieldType.Keyword)private String address; //位置描述@Field(type = FieldType.Long)private Long created; //创建时间@Field(type = FieldType.Long)private Long updated; //更新时间@Field(type = FieldType.Long)private Long lastUpdated; //上次更新时间
}
package com.tanhua.dubbo.server.vo;import cn.hutool.core.bean.BeanUtil;
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 Long userId; //用户idprivate 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 = BeanUtil.toBean(userLocation, UserLocationVo.class);userLocationVo.setLongitude(userLocation.getLocation().getLon());userLocationVo.setLatitude(userLocation.getLocation().getLat());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;}
}

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

在my-tanhua-dubbo-interface中添加依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId>
</dependency>

1.1.3、定义dubbo接口

在my-tanhua-dubbo-interface工程中。

package com.tanhua.dubbo.server.api;public interface UserLocationApi {/*** 更新用户地理位置** @param userId 用户id* @param longitude 经度* @param latitude 纬度* @param address 地址名称* @return*/Boolean updateUserLocation(Long userId, Double longitude, Double latitude, String address);}

1.1.4、编写实现

package com.tanhua.dubbo.es.api;import cn.hutool.core.util.ObjectUtil;
import com.alibaba.dubbo.config.annotation.Service;
import com.tanhua.dubbo.server.api.UserLocationApi;
import com.tanhua.dubbo.server.pojo.UserLocation;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.common.geo.GeoPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.*;import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;@Service(version = "1.0.0")
@Slf4j
public class UserLocationApiImpl implements UserLocationApi {@Autowiredprivate ElasticsearchTemplate elasticsearchTemplate;/*** 初始化索引库**/@PostConstructpublic void initIndex(){//判断索引库是否存在,如果不存在,需要创建if(!this.elasticsearchTemplate.indexExists("tanhua")){this.elasticsearchTemplate.createIndex(UserLocation.class);}//判断表是否存在,如果不存在,需要创建if(!this.elasticsearchTemplate.typeExists("tanhua", "user_location")){this.elasticsearchTemplate.putMapping(UserLocation.class);}}@Overridepublic Boolean updateUserLocation(Long userId, Double longitude, Double latitude, String address) {//查询个人的地理位置数据,如果不存在,需要新增,如果是存在数据,更新数据try {GetQuery getQuery = new GetQuery();getQuery.setId(String.valueOf(userId));UserLocation userLocation = this.elasticsearchTemplate.queryForObject(getQuery, UserLocation.class);if(ObjectUtil.isEmpty(userLocation)){//新增数据userLocation = new UserLocation();userLocation.setUserId(userId);userLocation.setAddress(address);userLocation.setCreated(System.currentTimeMillis());userLocation.setUpdated(userLocation.getCreated());userLocation.setLastUpdated(userLocation.getCreated());userLocation.setLocation(new GeoPoint(latitude, longitude));IndexQuery indexQuery = new IndexQueryBuilder().withObject(userLocation).build();//保存数据到ES中this.elasticsearchTemplate.index(indexQuery);}else {//更新数据//更新的字段Map<String,Object> map = new HashMap<>();map.put("location", new GeoPoint(latitude, longitude));map.put("updated", System.currentTimeMillis());map.put("lastUpdated", userLocation.getUpdated());map.put("address", address);UpdateRequest updateRequest = new UpdateRequest();updateRequest.doc(map);UpdateQuery updateQuery = new UpdateQueryBuilder().withId(String.valueOf(userId)).withClass(UserLocation.class).withUpdateRequest(updateRequest).build();//更新数据this.elasticsearchTemplate.update(updateQuery);}return true;} catch (Exception e) {log.error("更新地理位置失败~ userId = " + userId + ", longitude = " + longitude + ", latitude = " + latitude + ", address = " + address, e);}return false;}
}

1.1.5、单元测试

package com.tanhua.dubbo.es;import com.tanhua.dubbo.server.api.UserLocationApi;
import com.tanhua.dubbo.server.vo.PageInfo;
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.SpringRunner;@RunWith(SpringRunner.class)
@SpringBootTest
public class TestUserLocationApi {@Autowiredprivate UserLocationApi userLocationApi;@Testpublic void testUpdateUserLocation() {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, "张江高科");}}

1.2、APP接口

接口文档:https://mock-java.itheima.net/project/35/interface/api/557

1.2.1、BaiduController

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.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.Map;@RestController
@RequestMapping("baidu")
public class BaiduController {@Autowiredprivate 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();}
}

1.2.2、BaiduService

package com.tanhua.server.service;import com.alibaba.dubbo.config.annotation.Reference;
import com.tanhua.common.pojo.User;
import com.tanhua.common.utils.UserThreadLocal;
import com.tanhua.dubbo.server.api.UserLocationApi;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;@Service
@Slf4j
public class BaiduService {@Reference(version = "1.0.0")private UserLocationApi userLocationApi;public Boolean updateLocation(Double longitude, Double latitude, String address) {User user = UserThreadLocal.get();try {return this.userLocationApi.updateUserLocation(user.getId(), longitude, latitude, address);} catch (Exception e) {log.error("更新地理位置失败~ userId = " + user.getId() + ", longitude = " + longitude + ", latitude = " + latitude + ", address = " + address, e);}return false;}}

1.3、测试

2、搜附近

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

实现思路:根据当前用户的位置,查询附近范围内的用户。范围是可以设置的。

2.1、dubbo服务

2.1.1、定义接口方法

//com.tanhua.dubbo.server.api.UserLocationApi/*** 查询用户地理位置** @param userId* @return*/UserLocationVo queryByUserId(Long userId);/*** 根据位置搜索** @param longitude 经度* @param latitude  纬度* @param distance  距离(米)* @param page      页数* @param pageSize  页面大小*/PageInfo<UserLocationVo> queryUserFromLocation(Double longitude, Double latitude, Double distance, Integer page, Integer pageSize);

2.1.2、编写实现

//com.tanhua.dubbo.es.api.UserLocationApiImpl/*** 查询用户的位置信息** @param userId* @return*/@Overridepublic UserLocationVo queryByUserId(Long userId) {GetQuery getQuery = new GetQuery();getQuery.setId(String.valueOf(userId));UserLocation userLocation = this.elasticsearchTemplate.queryForObject(getQuery, UserLocation.class);if(ObjectUtil.isNotEmpty(userLocation)){return UserLocationVo.format(userLocation);}return null;}/*** 根据位置搜索** @param longitude 经度* @param latitude  纬度* @param distance  距离(米)* @param page      页数* @param pageSize  页面大小*/@Overridepublic PageInfo<UserLocationVo> queryUserFromLocation(Double longitude, Double latitude, Double distance, Integer page, Integer pageSize) {PageInfo<UserLocationVo> pageInfo = new PageInfo<>();pageInfo.setPageNum(page);pageInfo.setPageSize(pageSize);String fieldName = "location";//实现了SearchQuery接口,构造分页、排序NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();//分页PageRequest pageRequest = PageRequest.of(page - 1, pageSize);searchQueryBuilder.withPageable(pageRequest);BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();//以一个点为中心,指定范围查询GeoDistanceQueryBuilder geoDistanceQueryBuilder = new GeoDistanceQueryBuilder(fieldName);//中心点geoDistanceQueryBuilder.point(new GeoPoint(latitude, longitude));//距离(画圆的半径)单位:公里geoDistanceQueryBuilder.distance(distance / 1000, DistanceUnit.KILOMETERS);boolQueryBuilder.must(geoDistanceQueryBuilder);searchQueryBuilder.withQuery(boolQueryBuilder);//排序,由近到远排序GeoDistanceSortBuilder geoDistanceSortBuilder = new GeoDistanceSortBuilder(fieldName, latitude, longitude);geoDistanceSortBuilder.order(SortOrder.ASC); //正序排序geoDistanceSortBuilder.unit(DistanceUnit.KILOMETERS); //设置单位searchQueryBuilder.withSort(geoDistanceSortBuilder);AggregatedPage<UserLocation> aggregatedPage = this.elasticsearchTemplate.queryForPage(searchQueryBuilder.build(), UserLocation.class);if(CollUtil.isEmpty(aggregatedPage.getContent())){return pageInfo;}pageInfo.setRecords(UserLocationVo.formatToList(aggregatedPage.getContent()));return pageInfo;}

2.1.3、单元测试

//com.tanhua.dubbo.es.TestUserLocationApi@Testpublic void testQueryByUserId(){UserLocationVo userLocationVo = this.userLocationApi.queryByUserId(1L);System.out.println(userLocationVo);}@Testpublic void testQueryUserFromLocation(){UserLocationVo userLocationVo = this.userLocationApi.queryByUserId(1L);PageInfo<UserLocationVo> pageInfo = this.userLocationApi.queryUserFromLocation(userLocationVo.getLongitude(),userLocationVo.getLatitude(), 5000d, 1, 10);pageInfo.getRecords().forEach(vo -> System.out.println(vo));}

2.2、APP接口服务

文档地址:https://mock-java.itheima.net/project/35/interface/api/611

2.2.1、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;}

2.2.2、TanHuaController

//com.tanhua.server.controller.TanHuaController/*** 搜附近** @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.tanHuaService.queryNearUser(gender, distance);return ResponseEntity.ok(list);} catch (Exception e) {e.printStackTrace();}return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();}

2.2.3、TanHuaService

//com.tanhua.server.service.TanHuaServicepublic List<NearUserVo> queryNearUser(String gender, String distance) {//查询当前用户的位置User user = UserThreadLocal.get();UserLocationVo userLocationVo = this.userLocationApi.queryByUserId(user.getId());if(ObjectUtil.isEmpty(userLocationVo)){return ListUtil.empty();}PageInfo<UserLocationVo> pageInfo = this.userLocationApi.queryUserFromLocation(userLocationVo.getLongitude(),userLocationVo.getLatitude(),Convert.toDouble(distance),1,50);List<UserLocationVo> records = pageInfo.getRecords();if(CollUtil.isEmpty(records)){return ListUtil.empty();}//构造筛选条件List<Object> userIdList = CollUtil.getFieldValues(records, "userId");QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();queryWrapper.in("user_id", userIdList);if(StrUtil.equalsIgnoreCase(gender, "man")){queryWrapper.eq("sex", SexEnum.MAN);}else if(StrUtil.equalsIgnoreCase(gender, "woman")){queryWrapper.eq("sex", SexEnum.WOMAN);}List<UserInfo> userInfoList = this.userInfoService.queryUserInfoList(queryWrapper);List<NearUserVo> result = new ArrayList<>();for (UserLocationVo locationVo : records) {//排除自己if(ObjectUtil.equals(locationVo.getUserId(), user.getId())){continue;}for (UserInfo userInfo : userInfoList) {if(ObjectUtil.equals(locationVo.getUserId(), userInfo.getUserId())){NearUserVo nearUserVo = new NearUserVo();nearUserVo.setUserId(userInfo.getUserId());nearUserVo.setAvatar(userInfo.getLogo());nearUserVo.setNickname(userInfo.getNickName());result.add(nearUserVo);break;}}}return result;}

2.2.4、测试

3、探花

探花功能是将推荐的好友随机的通过卡片的形式展现出来,用户可以选择左滑、右滑操作,左滑:“不喜欢”,右滑:“喜欢”。

喜欢:如果双方喜欢,那么就会成为好友。

如果已经喜欢或不喜欢的用户在列表中不再显示。

3.1、喜欢的dubbo服务

用户的喜欢与不喜欢列表需要保存在redis中,为了防止redis中的数据丢失,同时需要将数据保存到mongodb进行持久化保存。

3.1.1、实体对象

package com.tanhua.dubbo.server.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "user_like")
public class UserLike implements java.io.Serializable {private static final long serialVersionUID = 6739966698394686523L;private ObjectId id;@Indexedprivate Long userId; //用户id,自己@Indexedprivate Long likeUserId; //喜欢的用户id,对方private Long created; //创建时间}

3.1.2、定义接口

package com.tanhua.dubbo.server.api;import java.util.List;public interface UserLikeApi {/*** 喜欢** @param userId* @param likeUserId* @return*/Boolean likeUser(Long userId, Long likeUserId);/*** 不喜欢** @param userId* @param likeUserId* @return*/Boolean notLikeUser(Long userId, Long likeUserId);/*** 是否相互喜欢** @param userId* @param likeUserId* @return*/Boolean isMutualLike(Long userId, Long likeUserId);/*** 查询喜欢列表** @param userId* @return*/List<Long> queryLikeList(Long userId);/*** 查询不喜欢列表** @param userId* @return*/List<Long> queryNotLikeList(Long userId);}

3.1.3、编写实现

package com.tanhua.dubbo.server.api;import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import com.alibaba.dubbo.config.annotation.Service;
import com.tanhua.dubbo.server.pojo.UserLike;
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.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;import java.util.ArrayList;
import java.util.List;
import java.util.Set;@Service(version = "1.0.0")
public class UserLikeApiImpl implements UserLikeApi {@Autowiredprivate MongoTemplate mongoTemplate;@Autowiredprivate RedisTemplate<String,String> redisTemplate;public static final String LIKE_REDIS_KEY_PREFIX = "USER_LIKE_";public static final String NOT_LIKE_REDIS_KEY_PREFIX = "USER_NOT_LIKE_";/*** 喜欢** @param userId* @param likeUserId* @return*/@Overridepublic Boolean likeUser(Long userId, Long likeUserId) {//判断该用户是否已经喜欢,如果已经喜欢就返回if(this.isLike(userId, likeUserId)){return false;}UserLike userLike = new UserLike();userLike.setId(ObjectId.get());userLike.setUserId(userId);userLike.setLikeUserId(likeUserId);userLike.setCreated(System.currentTimeMillis());//将数据存储到MongoDBthis.mongoTemplate.save(userLike);//用户的喜欢数据保存到redis//用户1:key -> USER_LIKE_1 , value -> 2, "1"//用户1:key -> USER_LIKE_1 , value -> 3, "1"//用户2:key -> USER_LIKE_2 , value -> 4, "1"String redisKey = this.getLikeRedisKey(userId);String hashKey = String.valueOf(likeUserId);this.redisTemplate.opsForHash().put(redisKey, hashKey, "1");//判断,喜欢的用户是否在不喜欢的列表中,如果在,就需要删除数据if(this.isNotLike(userId, likeUserId)){redisKey = this.getNotLikeRedisKey(userId);this.redisTemplate.opsForHash().delete(redisKey, hashKey);}return true;}/*** 获取喜欢数据的redis key** @param userId* @return*/private String getLikeRedisKey(Long userId){return LIKE_REDIS_KEY_PREFIX + userId;}/*** 获取不喜欢数据的redis key** @param userId* @return*/private String getNotLikeRedisKey(Long userId){return NOT_LIKE_REDIS_KEY_PREFIX + userId;}/*** 是否喜欢** @param userId* @param likeUserId* @return*/private Boolean isLike(Long userId, Long likeUserId){String redisKey = this.getLikeRedisKey(userId);String hashKey = String.valueOf(likeUserId);return this.redisTemplate.opsForHash().hasKey(redisKey, hashKey);}/*** 是否不喜欢** @param userId* @param likeUserId* @return*/private Boolean isNotLike(Long userId, Long likeUserId){String redisKey = this.getNotLikeRedisKey(userId);String hashKey = String.valueOf(likeUserId);return this.redisTemplate.opsForHash().hasKey(redisKey, hashKey);}@Overridepublic Boolean notLikeUser(Long userId, Long likeUserId) {//判断用户是否已经不喜欢,如果已经不喜欢,就返回if(this.isNotLike(userId, likeUserId)){return false;}//将用户保存到不喜欢列表中String redisKey = this.getNotLikeRedisKey(userId);String hashKey = String.valueOf(likeUserId);this.redisTemplate.opsForHash().put(redisKey, hashKey, "1");//判断用户是否在喜欢列表中,如果存在的话,需要删除数据if(this.isLike(userId, likeUserId)){//删除MongoDB数据Query query = Query.query(Criteria.where("userId").is(userId).and("likeUserId").is(likeUserId));this.mongoTemplate.remove(query, UserLike.class);//删除redis中的数据redisKey = this.getLikeRedisKey(userId);this.redisTemplate.opsForHash().delete(redisKey, hashKey);}return true;}@Overridepublic Boolean isMutualLike(Long userId, Long likeUserId) {return this.isLike(userId, likeUserId)&& this.isLike(likeUserId, userId);}@Overridepublic List<Long> queryLikeList(Long userId) {// 查询redisString redisKey = this.getLikeRedisKey(userId);Set<Object> keys = this.redisTemplate.opsForHash().keys(redisKey);if(CollUtil.isEmpty(keys)){return ListUtil.empty();}List<Long> result = new ArrayList<>(keys.size());keys.forEach(o -> result.add(Convert.toLong(o)));return result;}@Overridepublic List<Long> queryNotLikeList(Long userId) {// 查询redisString redisKey = this.getNotLikeRedisKey(userId);Set<Object> keys = this.redisTemplate.opsForHash().keys(redisKey);if(CollUtil.isEmpty(keys)){return ListUtil.empty();}List<Long> result = new ArrayList<>(keys.size());keys.forEach(o -> result.add(Convert.toLong(o)));return result;}
}

3.1.4、单元测试

package com.tanhua.dubbo.server.api;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 TestUserLikeApi {@Autowiredprivate UserLikeApi userLikeApi;@Testpublic void testUserLike() {System.out.println(this.userLikeApi.likeUser(1L, 2L));System.out.println(this.userLikeApi.likeUser(1L, 3L));System.out.println(this.userLikeApi.likeUser(1L, 4L));System.out.println(this.userLikeApi.notLikeUser(1L, 5L));System.out.println(this.userLikeApi.notLikeUser(1L, 6L));System.out.println(this.userLikeApi.likeUser(1L, 5L));System.out.println(this.userLikeApi.notLikeUser(1L, 2L));}@Testpublic void testQueryList(){this.userLikeApi.queryLikeList(1L).forEach(a -> System.out.println(a));System.out.println("-------");this.userLikeApi.queryNotLikeList(1L).forEach(a -> System.out.println(a));}
}

3.2、查询推荐列表dubbo服务

3.2.1、定义接口

//com.tanhua.dubbo.server.api.RecommendUserApi/*** 查询探花列表,查询时需要排除不喜欢列表用户** @param userId* @param count* @return*/
List<RecommendUser> queryCardList(Long userId, Integer count);

3.2.2、编写实现

//com.tanhua.dubbo.server.api.RecommendUserApiImpl@Overridepublic List<RecommendUser> queryCardList(Long userId, Integer count) {//设置分页以及排序,按照得分倒序排序PageRequest pageRequest = PageRequest.of(0, count, Sort.by(Sort.Order.desc("score")));//排除已喜欢或不喜欢的用户List<Long> userIds = new ArrayList<>();//查询喜欢列表userIds.addAll(this.userLikeApi.queryLikeList(userId));//查询不喜欢列表userIds.addAll(this.userLikeApi.queryNotLikeList(userId));//构造查询条件Criteria criteria = Criteria.where("toUserId").is(userId);if(CollUtil.isNotEmpty(userIds)){//加入到查询条件中,排除这些用户criteria.andOperator(Criteria.where("userId").nin(userIds));}Query query = Query.query(criteria).with(pageRequest);List<RecommendUser> recommendUserList = this.mongoTemplate.find(query, RecommendUser.class);return recommendUserList;}

3.2.3、单元测试

//com.tanhua.dubbo.server.api.TestRecommendUserApi@Test
public void testQueryCardList(){this.recommendUserApi.queryCardList(2L, 20).forEach(recommendUser -> System.out.println(recommendUser));
}

3.3、查询推荐列表APP接口实现

接口文档:https://mock-java.itheima.net/project/35/interface/api/593

3.3.1、TanHuaController

/*** 探花** @return*/@GetMapping("cards")public ResponseEntity<List<TodayBest>> queryCardsList() {try {List<TodayBest> list = this.tanHuaService.queryCardsList();return ResponseEntity.ok(list);} catch (Exception e) {e.printStackTrace();}return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();}

3.3.2、TanHuaService

#默认推荐列表
tanhua.default.recommend.users=2,3,8,10,18,20,24,29,27,32,36,37,56,64,75,88
/*** 查询推荐卡片列表,从推荐列表中随机选取10个用户** @return*/public List<TodayBest> queryCardsList() {User user = UserThreadLocal.get();int count = 50;//查询到的50条数据,并不是用来直接展现,需要从这50条数据中随机返回一些数据List<RecommendUser> recommendUserList = this.recommendUserService.queryCardList(user.getId(), count);if (CollUtil.isEmpty(recommendUserList)) {recommendUserList = new ArrayList<>();//默认推荐列表List<String> list = StrUtil.split(defaultRecommendUsers, ',');for (String userId : list) {RecommendUser recommendUser = new RecommendUser();recommendUser.setToUserId(user.getId());recommendUser.setUserId(Convert.toLong(userId));recommendUserList.add(recommendUser);}}//计算展现的数量,默认展现10个int showCount = Math.min(10, recommendUserList.size());List<RecommendUser> result = new ArrayList<>();for (int i = 0; i < showCount; i++) {//TODO 可能重复int index = RandomUtil.randomInt(0, recommendUserList.size());RecommendUser recommendUser = recommendUserList.get(index);result.add(recommendUser);}List<Object> userIdList = CollUtil.getFieldValues(result, "userId");List<UserInfo> userInfoList = this.userInfoService.queryUserInfoByUserIdList(userIdList);List<TodayBest> todayBests = new ArrayList<>();for (UserInfo userInfo : userInfoList) {TodayBest todayBest = new TodayBest();todayBest.setId(userInfo.getUserId());todayBest.setAge(userInfo.getAge());todayBest.setAvatar(userInfo.getLogo());todayBest.setGender(userInfo.getSex().name().toLowerCase());todayBest.setNickname(userInfo.getNickName());todayBest.setTags(Convert.toStrArray(StrUtil.split(userInfo.getTags(), ',')));todayBest.setFateValue(0L);todayBests.add(todayBest);}return todayBests;}

3.3.3、测试

效果:

3.4、左滑右滑

左滑:“不喜欢”,右滑:“喜欢”,如果双方喜欢,那么就会成为好友。

喜欢的接口文档:https://mock-java.itheima.net/project/35/interface/api/599

不喜欢的接口文档:https://mock-java.itheima.net/project/35/interface/api/605

3.4.1、TanHuaController

/*** 喜欢** @param likeUserId* @return*/
@GetMapping("{id}/love")
public ResponseEntity<Void> likeUser(@PathVariable("id") Long likeUserId) {try {this.tanHuaService.likeUser(likeUserId);return ResponseEntity.ok(null);} catch (Exception e) {e.printStackTrace();}return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}/*** 不喜欢** @param likeUserId* @return*/
@GetMapping("{id}/unlove")
public ResponseEntity<Void> notLikeUser(@PathVariable("id") Long likeUserId) {try {this.tanHuaService.notLikeUser(likeUserId);return ResponseEntity.ok(null);} catch (Exception e) {e.printStackTrace();}return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}

3.4.2、TanHuaService

//com.tanhua.server.service.TanHuaServicepublic Boolean likeUser(Long likeUserId) {User user = UserThreadLocal.get();Boolean result = this.userLikeApi.likeUser(user.getId(), likeUserId);if (!result) {return false;}if (this.userLikeApi.isMutualLike(user.getId(), likeUserId)) {//相互喜欢成为好友this.imService.contactUser(likeUserId);}return true;
}public Boolean notLikeUser(Long likeUserId) {User user = UserThreadLocal.get();return this.userLikeApi.notLikeUser(user.getId(), likeUserId);
}

3.4.3、测试

user_like表,可以看到已经相互喜欢了:

tanhua_users表,可以看到相互是好友了:

环信平台:

4、用户资料

在我的中心模块中,可以对个人信息做修改。

4.1、基本信息

在前面实现的查询个人信息接口中,已经返回个人基本数据,所以可以直接展现出个人信息,下面只需要进行实现数据的保存即可。

4.4.1、接口信息

接口地址:https://mock-java.itheima.net/project/35/interface/api/887

请求参数:

4.4.2、MyCenterController

//com.tanhua.server.controller.MyCenterController/*** 更新用户信息** @param userInfoVo* @return*/
@PutMapping
public ResponseEntity<Void> updateUserInfo(@RequestBody UserInfoVo userInfoVo){try {Boolean bool = this.myCenterService.updateUserInfo(userInfoVo);if(bool){return ResponseEntity.ok(null);}} catch (Exception e) {e.printStackTrace();}return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}

4.4.3、MyCenterService

//com.tanhua.server.service.MyCenterServicepublic Boolean updateUserInfo(UserInfoVo userInfoVo) {User user = UserThreadLocal.get();UserInfo userInfo = new UserInfo();userInfo.setUserId(user.getId());userInfo.setAge(Integer.valueOf(userInfoVo.getAge()));userInfo.setSex(StringUtils.equalsIgnoreCase(userInfoVo.getGender(), "man") ? SexEnum.MAN : SexEnum.WOMAN);userInfo.setBirthday(userInfoVo.getBirthday());userInfo.setCity(userInfoVo.getCity());userInfo.setEdu(userInfoVo.getEducation());userInfo.setIncome(StringUtils.replaceAll(userInfoVo.getIncome(), "K", ""));userInfo.setIndustry(userInfoVo.getProfession());userInfo.setMarriage(userInfoVo.getMarriage() == 1 ? "已婚" : "未婚");return this.userInfoService.updateUserInfoByUserId(userInfo);
}

4.4.4、UserInfoService

//com.tanhua.server.service.UserInfoServicepublic boolean updateUserInfoByUserId(UserInfo userInfo) {QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();queryWrapper.eq("user_id", userInfo.getUserId());return this.userInfoMapper.update(userInfo, queryWrapper) > 0;
}

4.4.5、bug修复

在之前的查询个人信息中接口中,返回数据中的性别数据有误,需要返回man或woman。

如下:

//com.tanhua.server.service.MyCenterServicepublic UserInfoVo queryUserInfoByUserId(Long userId) {if (ObjectUtil.isEmpty(userId)) {//如果查询id为null,就表示查询当前用户信息userId = UserThreadLocal.get().getId();}//查询用户信息UserInfo userInfo = this.userInfoService.queryUserInfoByUserId(userId);if (ObjectUtil.isEmpty(userInfo)) {return null;}UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class, "marriage");userInfoVo.setGender(userInfo.getSex().getValue() == 1 ? "man" : "women");userInfoVo.setMarriage(StrUtil.equals("已婚", userInfo.getMarriage()) ? 1 : 0);return userInfoVo;}

4.2、更新头像

上传头像使用sso中的上传逻辑即可,只是路径不同,所以我们只需要修改nginx配置和在sso中定义Controller即可。

接口文档:https://mock-java.itheima.net/project/35/interface/api/881

#user  nobody;
worker_processes  1;#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;#pid        logs/nginx.pid;events {worker_connections  1024;
}http {include       mime.types;default_type  application/octet-stream;#log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '#                  '$status $body_bytes_sent "$http_referer" '#                  '"$http_user_agent" "$http_x_forwarded_for"';#access_log  logs/access.log  main;sendfile        on;#tcp_nopush     on;#keepalive_timeout  0;keepalive_timeout  65;#gzip  on;server {listen       80;server_name  localhost;#charset koi8-r;#access_log  logs/host.access.log  main;#error_page  404              /404.html;# redirect server error pages to the static page /50x.html#error_page   500 502 503 504  /50x.html;location = /50x.html {root   html;}location /user/ {  #请求路径中凡是以/user/开头的请求,转发到sso系统client_max_body_size  300m;  #设置最大的请求体大小,解决大文件上传不了的问题proxy_connect_timeout 300s;  #代理连接超时时间proxy_send_timeout 300s;  #代理发送数据的超时时间proxy_read_timeout 300s;  #代理读取数据的超时时间proxy_pass   http://127.0.0.1:18080;  #转发请求}location /users/header {  #请求路径中凡是以/user/header开头的请求,转发到sso系统client_max_body_size  300m;  #设置最大的请求体大小,解决大文件上传不了的问题proxy_connect_timeout 300s;  #代理连接超时时间proxy_send_timeout 300s;  #代理发送数据的超时时间proxy_read_timeout 300s;  #代理读取数据的超时时间proxy_pass   http://127.0.0.1:18080;  #转发请求}location / {   #上面未匹配到的在这里处理client_max_body_size  300m;proxy_connect_timeout 300s;proxy_send_timeout 300s;proxy_read_timeout 300s;proxy_pass   http://127.0.0.1:18081;  #转发请求到server系统}}}

4.2.2、MyCenterController

在sso工程中定义MyCenterController。

package com.tanhua.sso.controller;import com.tanhua.sso.vo.ErrorResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;@RestController
@RequestMapping("users")
public class MyCenterController {@Autowiredprivate UserInfoController userInfoController;/*** 上传头像** @param file* @param token* @return*/@PostMapping("header")public ResponseEntity<Object> saveLogo(@RequestParam("headPhoto") MultipartFile file, @RequestHeader("Authorization") String token) {return this.userInfoController.saveUserLogo(file, token);}
}

探花交友_第8章_搜附近以及探花功能实现相关推荐

  1. 探花交友_第1章_项目介绍以及实现登录功能_第1节_功能介绍

    探花交友_第1章_项目介绍以及实现登录功能_第1节_功能介绍 文章目录 探花交友_第1章_项目介绍以及实现登录功能_第1节_功能介绍 1.功能介绍 1.1.功能列表 1.2.注册登录 1.3.交友 1 ...

  2. 探花交友_第3章_今日佳人功能实现

    探花交友_第3章_今日佳人功能实现 文章目录 探花交友_第3章_今日佳人功能实现 1.首页 2.系统架构 2.1.nginx服务 2.1.1.部署安装 2.1.2.配置 2.1.3.测试 2.2.搭建 ...

  3. 探花交友_第10章_实现推荐功能

    探花交友_第10章_实现推荐功能 文章目录 探花交友_第10章_实现推荐功能 1.了解推荐系统 1.1.什么是推荐系统? 1.2.电商是推荐系统的先行者 1.3.推荐系统业务流程 1.4.协同过滤推荐 ...

  4. 探花交友_第2章_环境搭建(新版)

    探花交友_第2章_环境搭建(新版) 文章目录 探花交友_第2章_环境搭建(新版) 课程介绍 <探花交友> 1.项目介绍 1.1.项目背景 1.2.市场分析 1.3.目标用户群体 1.4.使 ...

  5. 探花交友_第10章_搭建后台系统(新版)

    探花交友_第10章_搭建后台系统(新版) 文章目录 探花交友_第10章_搭建后台系统(新版) 1.1 概述 1.2 API网关 1.2.1 搭建网关 依赖 引导类 跨域问题配置类 配置文件 测试 1. ...

  6. 探花交友_第9章_小视频方案(新版)

    探花交友_第9章_小视频方案(新版) 文章目录 探花交友_第9章_小视频方案(新版) 1. 我的访客 1.1 需求分析 1.1.1 功能说明 1.1.2 数据库表 1.2 记录访客数据 tanhua- ...

  7. 探花交友_第12章_实现推荐系统(新版)

    探花交友_第12章_实现推荐系统(新版) 文章目录 探花交友_第12章_实现推荐系统(新版) 1.了解推荐系统 1.1.什么是推荐系统? 1.2.电商是推荐系统的先行者 1.3.推荐系统业务流程 1. ...

  8. 探花交友_第11章_数据统计与内容审核(新版)

    探花交友_第11章_数据统计与内容审核(新版) 文章目录 探花交友_第11章_数据统计与内容审核(新版) 1.用户冻结解冻 1.1 用户冻结 ManageController ManageServic ...

  9. 探花交友_第6章_圈子互动(新版)

    探花交友_第6章_圈子互动(新版) 文章目录 探花交友_第6章_圈子互动(新版) 课程说明 1. 动态查询 1.1 查询好友动态 1.1.1 接口文档 1.1.2 代码步骤 1.1.3 代码实现 ta ...

最新文章

  1. UE商城资源 Kitsune狐狸女孩
  2. 026_html表单
  3. php 单利模式实例化,php-单例模式实现mysql实例化对象
  4. python画图程序没有图_python画图 - v0
  5. Vue「六」前端路由、vue-router
  6. jsp springmvc 视图解析器_SpringMVC工作原理
  7. android 布局颜色设置颜色设置,怎么在Android中利用view设置布局颜色
  8. 自定义控件的构建(10)
  9. bzoj 1620: [Usaco2008 Nov]Time Management 时间管理(贪心)
  10. Disruptor(无锁并发框架)-发布
  11. flowable工作流_使用Bash Shell实现flowable配置文件修改定制
  12. Python numpy 中 keepdims 的含义
  13. Varnish的基本应用详解
  14. 电脑c盘空间不足怎么清理_C盘空间不足怎么办?如何给C盘扩容?
  15. 基于C++实现DBSCAN聚类算法
  16. 转载:如何使用RFT自动打开IE
  17. z-index诡异事件之背锅侠
  18. 第十一届蓝桥杯国赛题目
  19. html向服务器发送请求有哪些方法,HTTP协议客户端是如何向服务器发送请求
  20. 玩转 Defcon 黑客大会,这里有份装 X 指南

热门文章

  1. 数学日记-最小二乘法本质
  2. JavaScript调用麦克风并录制wav音频
  3. 禁用免费版小红伞的弹窗广告
  4. 腾讯后台开发技术总监浅谈过载保护 小心雪崩效应
  5. 2月14魔兽服务器维护,《魔兽世界怀旧服》2月14日再开两组服务器 免费转服同步上线...
  6. JavaEE 5 (4/28)
  7. 题解-bzoj2560 串珠子
  8. [案例研究]—superJumper 3.游戏中的物体与主游戏逻辑
  9. 【多式联运】基于matlab遗传算法求解多式联运运输问题(考虑碳交易)【含Matlab源码 1997期】
  10. 文艺小清新 古镇留踪