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


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

    • 上报地位位置
    • 实现搜附近功能
    • 实现探花功能
    • 用户基本信息维护

    1、上报地理位置

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

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

    在这里插入图片描述

    1.1、dubbo服务

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

    1.1.1、创建工程

    pom.ml文件如下:

    
    <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-dubboartifactId>
            <groupId>cn.itcast.tanhuagroupId>
            <version>1.0-SNAPSHOTversion>
        parent>
        <modelVersion>4.0.0modelVersion>
    
        <artifactId>my-tanhua-dubbo-esartifactId>
    
        <dependencies>
            
            <dependency>
                <groupId>cn.itcast.tanhuagroupId>
                <artifactId>my-tanhua-dubbo-interfaceartifactId>
                <version>1.0-SNAPSHOTversion>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starterartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-data-elasticsearchartifactId>
            dependency>
            
            <dependency>
                <groupId>com.alibaba.bootgroupId>
                <artifactId>dubbo-spring-boot-starterartifactId>
            dependency>
            
            <dependency>
                <groupId>com.alibabagroupId>
                <artifactId>dubboartifactId>
            dependency>
            
            <dependency>
                <groupId>org.apache.zookeepergroupId>
                <artifactId>zookeeperartifactId>
            dependency>
            <dependency>
                <groupId>com.github.sgroschupfgroupId>
                <artifactId>zkclientartifactId>
            dependency>
            <dependency>
                <groupId>io.nettygroupId>
                <artifactId>netty-allartifactId>
            dependency>
            <dependency>
                <groupId>cn.hutoolgroupId>
                <artifactId>hutool-allartifactId>
            dependency>
        dependencies>
    project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    启动类:

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    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 {
    
        @Id
        private Long userId; //用户id
        @GeoPointField
        private 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; //上次更新时间
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    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; //用户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 = 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;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

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

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

    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-data-elasticsearchartifactId>
    dependency>
    <dependency>
        <groupId>cn.hutoolgroupId>
        <artifactId>hutool-allartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    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 {
    
        @Autowired
        private ElasticsearchTemplate elasticsearchTemplate;
    
        /**
         * 初始化索引库
         *
         */
        @PostConstruct
        public void initIndex(){
            //判断索引库是否存在,如果不存在,需要创建
            if(!this.elasticsearchTemplate.indexExists("tanhua")){
                this.elasticsearchTemplate.createIndex(UserLocation.class);
            }
    
            //判断表是否存在,如果不存在,需要创建
            if(!this.elasticsearchTemplate.typeExists("tanhua", "user_location")){
                this.elasticsearchTemplate.putMapping(UserLocation.class);
            }
        }
    
        @Override
        public 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    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 {
    
        @Autowired
        private UserLocationApi userLocationApi;
    
        @Test
        public 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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    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 {
    
        @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();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    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);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    2.1.2、编写实现
    //com.tanhua.dubbo.es.api.UserLocationApiImpl
    
    	/**
         * 查询用户的位置信息
         *
         * @param userId
         * @return
         */
        @Override
        public 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  页面大小
         */
        @Override
        public 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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    2.1.3、单元测试
    //com.tanhua.dubbo.es.TestUserLocationApi
    
    	@Test
        public void testQueryByUserId(){
            UserLocationVo userLocationVo = this.userLocationApi.queryByUserId(1L);
            System.out.println(userLocationVo);
        }
    
        @Test
        public 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));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    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;
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    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();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    2.2.3、TanHuaService
    //com.tanhua.server.service.TanHuaService
    
        public 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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    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;
        @Indexed
        private Long userId; //用户id,自己
        @Indexed
        private Long likeUserId; //喜欢的用户id,对方
        private Long created; //创建时间
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    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);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    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 {
    
        @Autowired
        private MongoTemplate mongoTemplate;
    
        @Autowired
        private 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
         */
        @Override
        public 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());
    
            //将数据存储到MongoDB
            this.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);
        }
    
        @Override
        public 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;
        }
    
        @Override
        public Boolean isMutualLike(Long userId, Long likeUserId) {
            return this.isLike(userId, likeUserId)
                    && this.isLike(likeUserId, userId);
        }
    
        @Override
        public List<Long> queryLikeList(Long userId) {
            // 查询redis
            String 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;
        }
    
        @Override
        public List<Long> queryNotLikeList(Long userId) {
            // 查询redis
            String 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;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    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 {
    
        @Autowired
        private UserLikeApi userLikeApi;
    
        @Test
        public 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));
        }
    
        @Test
        public 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));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    3.2、查询推荐列表dubbo服务

    3.2.1、定义接口
    //com.tanhua.dubbo.server.api.RecommendUserApi
    
    /**
     * 查询探花列表,查询时需要排除不喜欢列表用户
     *
     * @param userId
     * @param count
     * @return
     */
    List<RecommendUser> queryCardList(Long userId, Integer count);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    3.2.2、编写实现
    //com.tanhua.dubbo.server.api.RecommendUserApiImpl
    
        @Override
        public 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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    3.2.3、单元测试
    //com.tanhua.dubbo.server.api.TestRecommendUserApi
    
    @Test
    public void testQueryCardList(){
        this.recommendUserApi.queryCardList(2L, 20)
                .forEach(recommendUser -> System.out.println(recommendUser));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    3.3.2、TanHuaService
    #默认推荐列表
    tanhua.default.recommend.users=2,3,8,10,18,20,24,29,27,32,36,37,56,64,75,88
    
    • 1
    • 2
    /**
     * 查询推荐卡片列表,从推荐列表中随机选取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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    3.4.2、TanHuaService
    //com.tanhua.server.service.TanHuaService
    
    public 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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    4.4.3、MyCenterService
    //com.tanhua.server.service.MyCenterService
    
    public 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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    4.4.4、UserInfoService
    //com.tanhua.server.service.UserInfoService
    
    public boolean updateUserInfoByUserId(UserInfo userInfo) {
        QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userInfo.getUserId());
        return this.userInfoMapper.update(userInfo, queryWrapper) > 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    4.4.5、bug修复

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

    如下:

    //com.tanhua.server.service.MyCenterService
    
    	public 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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    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系统
                }
            }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    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 {
    
        @Autowired
        private 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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
  • 相关阅读:
    DNS协议、ICMP协议、NAT技术
    计算机毕业设计node+vue基于微信小程序的乐团团购系统的设计与实现
    Linker Script链接脚本说明
    netty系列之:channel,ServerChannel和netty中的实现
    运维工程师现在用什么系统
    C++泛型编程--模版
    2008-2020年全国各省劳动生产率
    screen对象
    Docker搭建Mysql主从复制
    【MySQL从入门到精通】【高级篇】(六)MySQL表的存储引擎,InnoDB与MyISAM的对比
  • 原文地址:https://blog.csdn.net/guan1843036360/article/details/127831866