You are a wizard, Harry!

系列文章目录

1. 项目介绍及环境配置
2. 短信验证码登录
3. 用户信息
4. MongoDB
5.推荐好友列表/MongoDB集群/动态发布与查看
6. 圈子动态/圈子互动
7. 即时通讯(基于第三方API)
8. 附近的人(百度地图APi)
9. 小视频
10.网关配置
11.后台管理


文章目录

  • 系列文章目录
  • 一、推荐好友列表
    • 1. 页面展示
    • 2. 接口文档
    • 3. 编码实现
      • ⑴. 控制层 - 获取请求参数
      • ⑵. Service层 - 调用API分页查询
      • ⑶. API接口 - 分页查询推荐好友
      • ⑷. API实现类 - 完成推荐用户id查询用户详情
      • ⑸. 构造Dto对象
      • ⑹. Postman测试
    • 4. 代码优化 - 减少UserInfoApi频繁调用
      • ⑴. API接口 - 获取请求参数
      • ⑵. API接口实现类 - 批量查询用户详情
      • ⑶. 测试类
      • ⑷. Service层优化 - 减少UserInfoApi频繁调用
      • ⑸. Postman测试
  • 二、MongoDB集群
    • 1. 单点问题分析
    • 2. 解决方案
    • 3. 副本集群
      • ⑴. 执行原理
      • ⑵. 特点
    • 4. 分片集群
      • ⑴. 分片服务(Shard Server)
      • ⑵. 配置服务(Config Server)
      • ⑶. 路由服务(Route Server)
      • ⑷. 分片策略
      • ⑸. 特点
  • 三、发布动态
    • 1. 页面展示
    • 2. 表设计方案
      • ⑴. 方案一
      • ⑵. 方案二
      • ⑶. 方案三
      • ⑷. 表结构
    • 3. 接口文档
    • 4. 编码实现
      • ⑴. 搭建提供者环境
        • ①. 实体类
        • ②. API接口
        • ③. API实现类
      • ⑵. 自增序列
        • ①. 实体
        • ②. 工具类
      • ⑶. 测试
      • ⑷. 接口文档
      • ⑸. Controller获取请求参数
      • ⑹. Service完成逻辑处理
      • ⑺. API接口
      • ⑻. API实现类
      • ⑼. 测试类
    • 5. 代码优化
      • ⑴. 方法抽取
      • ⑵. 引导类开启异步调用
      • ⑶. 调用异步方法
    • 6. 页面效果
  • 四、查询我的动态
    • 1. 页面展示
    • 2. 接口文档
    • 3. 编码实现
      • ⑴. 构建vo对象
      • ⑵. 控制层获取请求参数
      • ⑶. Service层封装vo对象
      • ⑷. API接口
      • ⑸. API实现类查询动态数据
    • 4. 测试
      • ⑴. postman
      • ⑵. 页面效果

一、推荐好友列表

1. 页面展示

2. 接口文档

3. 编码实现

⑴. 控制层 - 获取请求参数

编辑tanhua-app-server/src/main/java/com/tanhua/server/controller/TanhuaController.java 文件:

@RestController
@RequestMapping("/tanhua")
public class TanhuaController {@Autowiredprivate TanhuaService tanhuaService;/*** 今日佳人*/@GetMapping("/todayBest")public ResponseEntity todayBest() {TodayBest vo = tanhuaService.todayBest();return ResponseEntity.ok(vo);}/*** 查询分页推荐好友列表*/@GetMapping("recommendation")public ResponseEntity recommendation(RecommendUserDto dto) {PageResult pr = tanhuaService.recommendation(dto);return ResponseEntity.ok(pr);}
}

⑵. Service层 - 调用API分页查询

编辑 tanhua-app-server/src/main/java/com/tanhua/server/service/TanhuaService.java 文件:

@Service
public class TanhuaService {@DubboReferenceprivate RecommendUserApi recommendUserApi;@DubboReferenceprivate UserInfoApi userInfoApi;// 查询今日佳人数据public TodayBest todayBest() {// 1. 获取用户idLong userId = UserHolder.getUserId();// 2. 调用api查询RecommendUser recommendUser = recommendUserApi.queryWithMaxScore(userId);// 3. 设置默认值if(recommendUser == null) {recommendUser = new RecommendUser();recommendUser.setUserId(1l);recommendUser.setScore(99d);}// 4. 将recommendUser 转化成 todayBest对象UserInfo userInfo = userInfoApi.findById(recommendUser.getUserId());TodayBest vo = TodayBest.init(userInfo, recommendUser);// 5. 返回return vo;}// 查询分页推荐好友列表public PageResult recommendation(RecommendUserDto dto) {// 1. 获取用户idLong userId = UserHolder.getUserId();// 2. 调用RecommendUserApi查询数据列表(PageResult -- RecommendUser)PageResult pr = recommendUserApi.queryRecommendUserList(dto.getPage(), dto.getPagesize(), userId);// 3. 获取分页中的RecommendUser数据列表List<RecommendUser> items = (List<RecommendUser>) pr.getItems();// 4. 判断列表数据是否为空if (items == null) {return pr;}// 5. 循环RecommendUser推荐列表,根据推荐的用户id查询用户详情List<TodayBest> list = new ArrayList<>();for (RecommendUser item : items) {Long recommendUserId = item.getUserId();UserInfo userInfo = userInfoApi.findById(recommendUserId);if(userInfo != null) {// 条件判断if(!StringUtils.isEmpty(dto.getGender()) && !dto.getGender().equals(userInfo.getGender())) {continue;}if (dto.getAge() != null && dto.getAge() < userInfo.getAge()) {continue;}TodayBest vo = TodayBest.init(userInfo, item);list.add(vo);}}// 6. 构造返回值pr.setItems(list);return pr;}
}

⑶. API接口 - 分页查询推荐好友

编辑 tanhua-dubbo/tanhua-dubbo-interface/src/main/java/com/tanhua/dubbo/api/RecommendUserApi.java 文件:

public interface RecommendUserApi {// 查询今日佳人RecommendUser queryWithMaxScore(Long toUserId);// 分页查询推荐好友列表PageResult queryRecommendUserList(Integer page, Integer pagesize, Long toUserId);
}

⑷. API实现类 - 完成推荐用户id查询用户详情

编辑tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/api/RecommendUserApiImpl.java 文件:

@DubboService
public class RecommendUserApiImpl implements RecommendUserApi{@Autowiredprivate MongoTemplate mongoTemplate;// 查询今日佳人public RecommendUser queryWithMaxScore(Long toUserId) {// 根据toUserId查询,根据评分score排序,获取第一条// 1. 构建CriteriaCriteria criteria = Criteria.where("toUserId").is(toUserId);// 2. 构建QueryQuery query = Query.query(criteria).with(Sort.by(Sort.Order.desc("score"))).limit(1); // 查询第一条(第一页第一条)// 3. 调用mongoTemplate查询return mongoTemplate.findOne(query, RecommendUser.class);}// 分页查询推荐好友列表public PageResult queryRecommendUserList(Integer page, Integer pagesize, Long toUserId) {// 1. 构建Criteria对象Criteria criteria = Criteria.where("toUserId").is(toUserId);// 2. 构造query对象Query query = Query.query(criteria);// 3. 查询总数long count = mongoTemplate.count(query, RecommendUser.class);// 4. 查询数据列表query.with(Sort.by(Sort.Order.desc("score"))).limit(pagesize).skip((page - 1) * pagesize);List<RecommendUser> list = mongoTemplate.find(query, RecommendUser.class);// 5. 构造返回值return new PageResult(page, pagesize, count, list);}
}

⑸. 构造Dto对象

新建 tanhua-model/src/main/java/com/tanhua/model/dto/RecommendUserDto.java 文件:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class RecommendUserDto {private Integer page = 1; //当前页数private Integer pagesize = 10; //页尺寸private String gender; //性别 man womanprivate String lastLogin; //近期登陆时间private Integer age; //年龄private String city; //居住地private String education; //学历
}

⑹. Postman测试

4. 代码优化 - 减少UserInfoApi频繁调用

  • 在UserInfoApi中根据条件一次性获取所有用户列表
  • 在Service层进行用户筛选

⑴. API接口 - 获取请求参数

编辑 tanhua-dubbo/tanhua-dubbo-interface/src/main/java/com/tanhua/dubbo/api/UserInfoApi.java 文件:

public interface UserInfoApi {public void save(UserInfo userInfo);public void update(UserInfo userInfo);// 根据id查询UserInfo findById(Long id);/*** 批量查詢用戶詳情*      返回值:Map<id, UserInfo>*/Map<Long, UserInfo> findByIds(List<Long> userIds, UserInfo info);
}

⑵. API接口实现类 - 批量查询用户详情

编辑 tanhua-dubbo/tanhua-dubbo-db/src/main/java/com/tanhua/dubbo/api/UserInfoApiImpl.java 文件:

@DubboService
public class UserInfoApiImpl implements UserInfoApi{@Autowiredprivate UserInfoMapper userInfoMapper;@Overridepublic void save(UserInfo userInfo) {userInfoMapper.insert(userInfo);}@Overridepublic void update(UserInfo userInfo) {userInfoMapper.updateById(userInfo);}@Overridepublic UserInfo findById(Long id) {return userInfoMapper.selectById(id);}/*** 批量查询用戶详情* @param userIds 用户id列表* @param info 用户详情信息* @return*/@Overridepublic Map<Long, UserInfo> findByIds(List<Long> userIds, UserInfo info) {// 1. 用户id列表QueryWrapper qw = new QueryWrapper<>();qw.in("id", userIds);// 2. 添加筛选条件if(info != null) {if(info.getAge() != null) {qw.lt("age", info.getAge());}if(!StringUtils.isEmpty(info.getGender())) {qw.eq("gender", info.getGender());}}// 3. 获取用户详情列表 转换成map集合List<UserInfo> list = userInfoMapper.selectList(qw);Map<Long, UserInfo> map = CollUtil.fieldValueMap(list, "id");// 4. 返回return map;}
}

⑶. 测试类

新建 tanhua-app-server/src/test/java/com/tanhua/test/UserInfoApiTest.java 文件:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = AppServerApplication.class)
public class UserInfoApiTest {@DubboReferenceprivate UserInfoApi userInfoApi;@Testpublic void testFindUsers() {List ids = new ArrayList<>();ids.add(1l);ids.add(2l);ids.add(3l);ids.add(4l);// 添加判断条件UserInfo userInfo = new UserInfo();userInfo.setAge(23);// Map map = userInfoApi.findByIds(ids, null);Map map = userInfoApi.findByIds(ids, userInfo);map.forEach((k, v) -> System.out.println(k + "---" + v));}
}

⑷. Service层优化 - 减少UserInfoApi频繁调用

编辑 tanhua-app-server/src/main/java/com/tanhua/server/service/TanhuaService.java 文件:

@Service
public class TanhuaService {@DubboReferenceprivate RecommendUserApi recommendUserApi;@DubboReferenceprivate UserInfoApi userInfoApi;// 查询今日佳人数据public TodayBest todayBest() {// 1. 获取用户idLong userId = UserHolder.getUserId();// 2. 调用api查询RecommendUser recommendUser = recommendUserApi.queryWithMaxScore(userId);// 3. 设置默认值if(recommendUser == null) {recommendUser = new RecommendUser();recommendUser.setUserId(1l);recommendUser.setScore(99d);}// 4. 将recommendUser 转化成 todayBest对象UserInfo userInfo = userInfoApi.findById(recommendUser.getUserId());TodayBest vo = TodayBest.init(userInfo, recommendUser);// 5. 返回return vo;}// 查询分页推荐好友列表public PageResult recommendation(RecommendUserDto dto) {// 1. 获取用户idLong userId = UserHolder.getUserId();// 2. 调用RecommendUserApi查询数据列表(PageResult -- RecommendUser)PageResult pr = recommendUserApi.queryRecommendUserList(dto.getPage(), dto.getPagesize(), userId);// 3. 获取分页中的RecommendUser数据列表List<RecommendUser> items = (List<RecommendUser>) pr.getItems();// 4. 判断列表数据是否为空if (items == null) {return pr;}//        // 5. 循环RecommendUser推荐列表,根据推荐的用户id查询用户详情
//        List<TodayBest> list = new ArrayList<>();
//        for (RecommendUser item : items) {//            Long recommendUserId = item.getUserId();
//            UserInfo userInfo = userInfoApi.findById(recommendUserId);
//            if(userInfo != null) {//                // 条件判断
//                if(!StringUtils.isEmpty(dto.getGender()) && !dto.getGender().equals(userInfo.getGender())) {//                    continue;
//                }
//                if (dto.getAge() != null && dto.getAge() < userInfo.getAge()) {//                    continue;
//                }
//                TodayBest vo = TodayBest.init(userInfo, item);
//                list.add(vo);
//            }
//        }
//// 5. 提取所有推荐的用户id列表List<Long> ids = CollUtil.getFieldValues(items, "userId", Long.class);UserInfo userInfo = new UserInfo();userInfo.setAge(dto.getAge());userInfo.setGender(dto.getGender());// 6. 构建查询条件,批量查询所有的用户详情Map<Long, UserInfo> map = userInfoApi.findByIds(ids, userInfo);// 7. 循环推荐的数据列表,构建vo对象List<TodayBest> list = new ArrayList<>();for (RecommendUser item : items) {UserInfo info = map.get(item.getUserId());if(info != null) {TodayBest vo = TodayBest.init(info, item);list.add(vo);}}// 6. 构造返回值pr.setItems(list);return pr;}
}

⑸. Postman测试


二、MongoDB集群

1. 单点问题分析

单机 MongoDB 并不适用于企业场景,存在两个问题亟需解决

  • 单点故障: 单一MongoDB提供服务,在服务器宕机时造成整体应用崩溃
  • 海量数据存储: 单一MongoDB,并不能支持海量数据存储

2. 解决方案

为了 解决单点故障海量数据存储 问题,MongoDB提供了三种集群形式来支持:

  • Master-Slaver(主从集群): 是一种主从副本的模式,目前已经 不推荐 使用
  • Replica Set (副本集群): 模式取代了 Master-Slaver 模式,是一种互为主从的关系。可以解决 单点故障 问题
  • Sharding (分片集群): 可以解决单点故障和 海量数据存储 问题

3. 副本集群

⑴. 执行原理

  • 包括主节点和副本节点/从节点
  • 主节点只能有一个,可以完成数据读写操作
  • 副本节点可以有多个,只能完成读操作
  • 多节点间有心跳检测,并进行数据同步
  • 主节点宕机后,副本节点选举新的主节点
  • 当主节点挂掉后,副本节点会进行选举,从中选举出一个新的主节点

⑵. 特点

单点故障、适用于中小型应用(数据量适中)、故障转移、读写分离

4. 分片集群

Sharding (分片集群)该模式适合处理大量数据,它将数据分开存储,不同服务器保存不同的数据,所有服务器数据的总和即为整个数据集。

⑴. 分片服务(Shard Server)

⑵. 配置服务(Config Server)

⑶. 路由服务(Route Server)

⑷. 分片策略

MongoDB 通过分片策略,决定数据存储的分片服务器。mongoDB 有两种分片策略,根据集合字段来指定。

  • 范围指定: 将指定字段的数据按照范围进行划分,根据范围获取分片服务器
  • 数据Hash: 将指定字段的数据进行Hash计算,获取存储的分片服务器

⑸. 特点

支持海量数据存储、分片集群内部结构、分片策略


三、发布动态

1. 页面展示

2. 表设计方案

需求:

  • 查看个人发布的动态
  • 查看好友发布的动态
  • 指定好友可见/不可见

⑴. 方案一

最简单的设计思路,包含 好友表动态表

  • 保存动态时,向动态表添加记录即可
  • 查询好友动态时
    • 先查询当前用户的所有好友
    • 根据好友,查询好友发布的所有动态

优点: 开发难度较小、易于理解
缺点: 动态对特定好友可见/不可见,实现难度较大、效率较低

⑵. 方案二


基于设计1的改造版本,在动态表中添加可见人字段

  • 保存动态
  • 查询当前用户好友
    • 保存动态,将 可见好友存入动态表
    • 查询好友动态

优点: 开发难度较小、可以完成所有业务功能
缺点: 索引空间占用、效率较低

⑶. 方案三


时间线表保存好友发布的动态(ID)

  • 发布动态
  • 查询好友动态

优点: 可以完成所有业务功能、数据结构清晰
缺点: 开发难度较复杂
分片规则: 动态表(根据用户id)、分片时间线表(根据好友id分片)

⑷. 表结构

好友关系表(friends):

{"_id": ObjectId("6018bc055098b2230031e2da"),"created": NumberLong("1612233733056"),"userId": NumberLong("1"),"friendId": NumberLong("106"),"_class": "com.itheima.domain.mongo.Friend"
}

动态表(movement):

{"_id": ObjectId("5e82dc416401952928c211d8"),"pid": NumberLong("10064"),"userId": NumberLong("6"),"textContent": "最悲伤却又是最痛苦的谎言,就是我还好,没有关系。","medias": ["https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/photo/7/1.jpg","https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/photo/7/1564567349498.jpg","https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/photo/7/1564567352977.jpg","https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/photo/7/1564567360406.jpg"],"longitude": "121.588627","latitude": "30.935781","state": NumberInt("0"),"locationName": "中国上海市奉贤区人民路445弄","created": NumberLong("1585634369493"),"_class": "com.tanhua.dubbo.server.pojo.Publish"
}

时间线表(movement_timeline):

{"_id": ObjectId("609cf6538743d448c02c61f0"),"movementId": ObjectId("609cf6538743d448c02c61ef"),"userId": NumberLong("106"),"friendId": NumberLong("1"),"created": NumberLong("1620899411043"),"_class": "com.tanhua.model.mongo.MovementTimeLine"
}

3. 接口文档

4. 编码实现

⑴. 搭建提供者环境

①. 实体类

新建 tanhua-model/src/main/java/com/tanhua/model/mongo/Friend.java 文件:

/*** 好友表:好友关系表*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "friend")
public class Friend implements java.io.Serializable{private static final long serialVersionUID = 6003135946820874230L;private ObjectId id;private Long userId; //用户idprivate Long friendId; //好友idprivate Long created; //时间}

新建 tanhua-model/src/main/java/com/tanhua/model/mongo/Movement.java 文件:

//动态详情表
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "movement")
public class Movement implements java.io.Serializable {private ObjectId id; //主键id// redis实现, 或者使用MongoDB实现private Long pid; //Long类型,用于推荐系统的模型(自动增长)private Long created; //发布时间private Long userId;private String textContent; //文字private List<String> medias; //媒体数据,图片或小视频 urlprivate String longitude; //经度private String latitude; //纬度private String locationName; //位置名称private Integer state = 0;//状态 0:未审(默认),1:通过,2:驳回
}

新建 tanhua-model/src/main/java/com/tanhua/model/mongo/MovementTimeLine.java 文件:

/*** 好友时间线表,用于存储好友发布的数据*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "movement_timeLine")
public class MovementTimeLine implements java.io.Serializable {private static final long serialVersionUID = 9096178416317502524L;private ObjectId id;private ObjectId movementId;//动态idprivate Long userId;   //发布动态用户idprivate Long friendId; // 可见好友idprivate Long created; //发布的时间
}

②. API接口

新建 tanhua-dubbo/tanhua-dubbo-interface/src/main/java/com/tanhua/dubbo/api/MovementApi.java 文件:

public interface MovementApi {}

③. API实现类

新建 tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/api/MovementApiImpl.java 文件:

@DubboService
public class MovementApiImpl implements MovementApi {@Autowiredprivate MongoTemplate mongoTemplate;
}

⑵. 自增序列

①. 实体

新建 tanhua-model/src/main/java/com/tanhua/model/mongo/Sequence.java 文件:

@Document(collection = "sequence")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Sequence {private ObjectId id;private long seqId; //自增序列private String collName;  //集合名称
}

②. 工具类

新建 tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/utils/IdWorker.java 文件:

@Component
public class IdWorker {@Autowiredprivate MongoTemplate mongoTemplate;public Long getNextId(String collName) {Query query = new Query(Criteria.where("collName").is(collName));Update update = new Update();update.inc("seqId", 1);FindAndModifyOptions options = new FindAndModifyOptions();options.upsert(true);options.returnNew(true);Sequence sequence = mongoTemplate.findAndModify(query, update, options, Sequence.class);return sequence.getSeqId();}
}

⑶. 测试

新建 tanhua-dubbo/tanhua-dubbo-mongo/src/test/java/com/tanhua/dubbo/IdWorkerTest.java 测试类:

@RunWith(SpringRunner.class)
@SpringBootTest
public class IdWorkerTest {@Autowiredprivate IdWorker idWorker;@Testpublic void test() {Long id = idWorker.getNextId("test");System.out.println(id);}
}

⑷. 接口文档

⑸. Controller获取请求参数

新建 tanhua-app-server/src/main/java/com/tanhua/server/controller/MovementController.java 文件:

@RestController
@RequestMapping("/movements")
public class MovementController {@Autowiredprivate MovementService movementService;/*** 发布动态* @return*/@PostMappingpublic ResponseEntity movements(Movement movement, MultipartFile imageContent[]) throws IOException {movementService.publishMovement(movement, imageContent);return ResponseEntity.ok(null);}
}

⑹. Service完成逻辑处理

新建 tanhua-app-server/src/main/java/com/tanhua/server/service/MovementService.java 文件:

@Service
public class MovementService {@Autowiredprivate OssTemplate ossTemplate;@DubboReferenceprivate MovementApi movementApi;/*** 发布动态*/public void publishMovement(Movement movement, MultipartFile[] imageContent) throws IOException {// 1. 判断发布动态的内容是否存在if(StringUtils.isEmpty(movement.getTextContent())) {throw new BusinessException(ErrorResult.contentError());}// 2. 获取当前登录的用户idLong userId = UserHolder.getUserId();// 3. 将文件内容上传到阿里云OSS, 获取请求地址List<String> medias = new ArrayList<>();for (MultipartFile multipartFile : imageContent) {String upload = ossTemplate.upload(multipartFile.getOriginalFilename(), multipartFile.getInputStream());medias.add(upload);}// 4. 将数据封装到movement对象movement.setUserId(userId);movement.setMedias(medias);//5. 调用API完成动态发布movementApi.publish(movement);}
}

⑺. API接口

编辑 tanhua-dubbo/tanhua-dubbo-interface/src/main/java/com/tanhua/dubbo/api/MovementApi.java 文件:

public interface MovementApi {// 发布动态void publish(Movement movement);
}

⑻. API实现类

编辑 tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/api/MovementApiImpl.java 文件:

@DubboService
public class MovementApiImpl implements MovementApi {@Autowiredprivate MongoTemplate mongoTemplate;@Autowiredprivate IdWorker idWorker;// 发布动态public void publish(Movement movement) {try {// 1. 保存动态详情// 1.1 设置PID(同名时序列自增)movement.setPid(idWorker.getNextId("movement"));// 1.2 设置时间movement.setCreated(System.currentTimeMillis());// 1.3 保存动态mongoTemplate.save(movement);// 2. 查询当前用户的好友数据Criteria criteria = Criteria.where("userId").is(movement.getUserId());Query query = Query.query(criteria);List<Friend> friends = mongoTemplate.find(query, Friend.class);// 3. 循环好友数据, 构建时间线数据存入数据库for (Friend friend : friends) {MovementTimeLine timeLine = new MovementTimeLine();timeLine.setMovementId(movement.getId());timeLine.setUserId(friend.getUserId());timeLine.setFriendId(friend.getFriendId());timeLine.setCreated(System.currentTimeMillis());mongoTemplate.save(timeLine);}} catch (Exception e) {// 忽略事物处理e.printStackTrace();}}
}

⑼. 测试类

新建 tanhua-app-server/src/test/java/com/tanhua/test/MovementApiTest.java 文件:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = AppServerApplication.class)
public class MovementApiTest {@DubboReferenceprivate MovementApi movementApi;@Testpublic void testPublish() {Movement movement = new Movement();movement.setUserId(106l);movement.setTextContent("你的酒窝没有酒,我却醉的像条狗");List<String> list = new ArrayList<>();list.add("https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/images/tanhua/avatar_1.png");list.add("https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/images/tanhua/avatar_2.png");movement.setMedias(list);movement.setLatitude("40.066355");movement.setLongitude("116.350426");movement.setLocationName("中国北京市昌平区建材城西路16号");movementApi.publish(movement);}
}

5. 代码优化

大量的时间线数据同步写入的问题如何解决 ?
@Async: Spring提供的异步处理注解,被此注解标注的方法会在新的线程中执行,其实就相当于我们自己new Thread。

⑴. 方法抽取

新建 tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/utils/TimeLineService.java 文件:

@Component
public class TimeLineService {@Autowiredprivate MongoTemplate mongoTemplate;@Async // 异步多线程调用public void saveTimeLine(Long userId, ObjectId movementId) {// 2. 查询当前用户的好友数据Criteria criteria = Criteria.where("userId").is(userId);Query query = Query.query(criteria);List<Friend> friends = mongoTemplate.find(query, Friend.class);// 睡眠时间try {Thread.sleep(10000);} catch (InterruptedException e) {throw new RuntimeException(e);}// 3. 循环好友数据, 构建时间线数据存入数据库for (Friend friend : friends) {MovementTimeLine timeLine = new MovementTimeLine();timeLine.setMovementId(movementId);timeLine.setUserId(friend.getUserId());timeLine.setFriendId(friend.getFriendId());timeLine.setCreated(System.currentTimeMillis());mongoTemplate.save(timeLine);}}
}

⑵. 引导类开启异步调用

编辑 tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/DubboMongoApplication.java 文件:

@SpringBootApplication
@EnableAsync // 开启Spring @Async支持
public class DubboMongoApplication {public static void main(String[] args) {SpringApplication.run(DubboMongoApplication.class,args);}
}

⑶. 调用异步方法

编辑 tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/api/MovementApiImpl.java 文件:

@DubboService
public class MovementApiImpl implements MovementApi {@Autowiredprivate MongoTemplate mongoTemplate;@Autowiredprivate IdWorker idWorker;@Autowiredprivate TimeLineService timeLineService;// 发布动态public void publish(Movement movement) {try {// 1. 保存动态详情// 1.1 设置PID(同名时序列自增)movement.setPid(idWorker.getNextId("movement"));// 1.2 设置时间movement.setCreated(System.currentTimeMillis());// 1.3 保存动态mongoTemplate.save(movement);//            // 2. 查询当前用户的好友数据
//            Criteria criteria = Criteria.where("userId").is(movement.getUserId());
//            Query query = Query.query(criteria);
//            List<Friend> friends = mongoTemplate.find(query, Friend.class);
//
//            // 3. 循环好友数据, 构建时间线数据存入数据库
//            for (Friend friend : friends) {//                MovementTimeLine timeLine = new MovementTimeLine();
//                timeLine.setMovementId(movement.getId());
//                timeLine.setUserId(friend.getUserId());
//                timeLine.setFriendId(friend.getFriendId());
//                timeLine.setCreated(System.currentTimeMillis());
//                mongoTemplate.save(timeLine);
//            }// 异步多线程调用timeLineService.saveTimeLine(movement.getUserId(), movement.getId());} catch (Exception e) {// 忽略事物处理e.printStackTrace();}}
}

6. 页面效果


四、查询我的动态

1. 页面展示

2. 接口文档

3. 编码实现

⑴. 构建vo对象

新建 tanhua-model/src/main/java/com/tanhua/model/vo/MovementsVo.java 文件:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MovementsVo  implements Serializable {private String id; //动态idprivate Long userId; //用户idprivate String avatar; //头像private String nickname; //昵称private String gender; //性别 man womanprivate Integer age; //年龄private String[] tags; //标签private String textContent; //文字动态private String[] imageContent; //图片动态private String distance; //距离private String createDate; //发布时间 如: 10分钟前private Integer likeCount; //点赞数private Integer commentCount; //评论数private Integer loveCount; //喜欢数private Integer hasLiked; //是否点赞(1是,0否)private Integer hasLoved; //是否喜欢(1是,0否)public static MovementsVo init(UserInfo userInfo, Movement item) {MovementsVo vo = new MovementsVo();//设置动态数据BeanUtils.copyProperties(item, vo);vo.setId(item.getId().toHexString());//设置用户数据BeanUtils.copyProperties(userInfo, vo);if(!StringUtils.isEmpty(userInfo.getTags())) {vo.setTags(userInfo.getTags().split(","));}//图片列表vo.setImageContent(item.getMedias().toArray(new String[]{}));//距离vo.setDistance("500米");Date date = new Date(item.getCreated());vo.setCreateDate(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(date));//设置是否点赞(后续处理)vo.setHasLoved(0);vo.setHasLiked(0);return vo;}
}

⑵. 控制层获取请求参数

编辑 tanhua-app-server/src/main/java/com/tanhua/server/controller/MovementController.java 文件:

@RestController
@RequestMapping("/movements")
public class MovementController {@Autowiredprivate MovementService movementService;/*** 发布动态* @return*/@PostMappingpublic ResponseEntity movements(Movement movement, MultipartFile imageContent[]) throws IOException {movementService.publishMovement(movement, imageContent);return ResponseEntity.ok(null);}/*** 查询我的动态*/@GetMapping("/all")public ResponseEntity findByUserId(Long userId,@RequestParam(defaultValue = "1") Integer page,@RequestParam(defaultValue = "10") Integer pagesize) {PageResult pr = movementService.findByUserId(userId, page, pagesize);return ResponseEntity.ok(pr);}
}

⑶. Service层封装vo对象

编辑 tanhua-app-server/src/main/java/com/tanhua/server/service/MovementService.java 文件:

@Service
public class MovementService {@Autowiredprivate OssTemplate ossTemplate;@DubboReferenceprivate MovementApi movementApi;@DubboReferenceprivate UserInfoApi userInfoApi;/*** 发布动态*/public void publishMovement(Movement movement, MultipartFile[] imageContent) throws IOException {// 1. 判断发布动态的内容是否存在if(StringUtils.isEmpty(movement.getTextContent())) {throw new BusinessException(ErrorResult.contentError());}// 2. 获取当前登录的用户idLong userId = UserHolder.getUserId();// 3. 将文件内容上传到阿里云OSS, 获取请求地址List<String> medias = new ArrayList<>();for (MultipartFile multipartFile : imageContent) {// String upload = ossTemplate.upload(multipartFile.getOriginalFilename(), multipartFile.getInputStream());// !!! 阿里云OSS收费, 这里暂时跳过String upload = "https://img-blog.csdnimg.cn/bb419b491ec1445d84046aa1852946bd.jpeg";medias.add(upload);}// 4. 将数据封装到movement对象movement.setUserId(userId);movement.setMedias(medias);//5. 调用API完成动态发布movementApi.publish(movement);}// 查询我的动态public PageResult findByUserId(Long userId, Integer page, Integer pagesize) {// 1. 根据用户id, 调用API查询个人动态内容(PageResult -- Movement)PageResult pr = movementApi.findByUserId(userId, page, pagesize);// 2. 获取PageResult中item列表对象List<Movement> items = (List<Movement>) pr.getItems();// 3. 非空判断if(items == null) {return pr;}// 4. 循环数据列表UserInfo userInfo = userInfoApi.findById(userId);List<MovementsVo> vos = new ArrayList<>();for (Movement item : items) {// 5. 一个Movement构建一个VO对象MovementsVo vo = MovementsVo.init(userInfo, item);vos.add(vo);}// 6. 构建返回值pr.setItems(vos);return pr;}
}

⑷. API接口

编辑 tanhua-dubbo/tanhua-dubbo-interface/src/main/java/com/tanhua/dubbo/api/MovementApi.java 文件:

public interface MovementApi {// 发布动态void publish(Movement movement);// 查询我的动态PageResult findByUserId(Long userId, Integer page, Integer pagesize);
}

⑸. API实现类查询动态数据

编辑 tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/api/MovementApiImpl.java 文件:

@DubboService
public class MovementApiImpl implements MovementApi {@Autowiredprivate MongoTemplate mongoTemplate;@Autowiredprivate IdWorker idWorker;@Autowiredprivate TimeLineService timeLineService;// 发布动态public void publish(Movement movement) {try {// 1. 保存动态详情// 1.1 设置PID(同名时序列自增)movement.setPid(idWorker.getNextId("movement"));// 1.2 设置时间movement.setCreated(System.currentTimeMillis());// 1.3 保存动态mongoTemplate.save(movement);//            // 2. 查询当前用户的好友数据
//            Criteria criteria = Criteria.where("userId").is(movement.getUserId());
//            Query query = Query.query(criteria);
//            List<Friend> friends = mongoTemplate.find(query, Friend.class);
//
//            // 3. 循环好友数据, 构建时间线数据存入数据库
//            for (Friend friend : friends) {//                MovementTimeLine timeLine = new MovementTimeLine();
//                timeLine.setMovementId(movement.getId());
//                timeLine.setUserId(friend.getUserId());
//                timeLine.setFriendId(friend.getFriendId());
//                timeLine.setCreated(System.currentTimeMillis());
//                mongoTemplate.save(timeLine);
//            }// 异步多线程调用timeLineService.saveTimeLine(movement.getUserId(), movement.getId());} catch (Exception e) {// 忽略事物处理e.printStackTrace();}}// 查询我的动态@Overridepublic PageResult findByUserId(Long userId, Integer page, Integer pagesize) {Criteria criteria = Criteria.where("userId").is(userId);Query query = Query.query(criteria).skip((page  - 1) * pagesize).limit(pagesize).with(Sort.by(Sort.Order.desc("created")));List<Movement> movements = mongoTemplate.find(query, Movement.class);return new PageResult(page, pagesize, 0l, movements);}
}

4. 测试

⑴. postman

⑵. 页面效果

Dobbo微服务项目实战(详细介绍+案例源码) - 5.推荐好友列表/MongoDB集群/动态发布与查看相关推荐

  1. java 探花交友项目day5 推荐好友列表 MongoDB集群 发布动态,查询动态 圈子功能

    推荐好友列表 需求分析 推荐好友:分页形式查询推荐的用户列表,根据评分排序显示 代码实现: tanhuaController: /**  * 查询分页推荐好友列表  */ @GetMapping(&q ...

  2. Dobbo微服务项目实战(详细介绍+案例源码) - 3.用户信息

    想出去旅游,想出去玩,想大吃大喝 0_o 系列文章目录 1. 项目介绍及环境配置 2. 短信验证码登录 3. 用户信息 4. MongoDB 5. 推荐好友列表/MongoDB集群/动态发布与查看 6 ...

  3. Dobbo微服务项目实战(详细介绍+案例源码) - 4.MongoDB

    安能摧眉折腰事权贵,使我不得开心颜 系列文章目录 1. 项目介绍及环境配置 2. 短信验证码登录 3. 用户信息 4.MongoDB 5. 推荐好友列表/MongoDB集群/动态发布与查看 6. 圈子 ...

  4. SpringBoot交友APP项目实战(详细介绍+案例源码) - 9.小视频(SpringCache缓存)

    有人相爱,有人跳海 系列文章目录 1. 项目介绍及环境配置 2. 短信验证码登录 3. 用户信息 4. MongoDB 5. 推荐好友列表/MongoDB集群/动态发布与查看 6. 圈子动态/圈子互动 ...

  5. SpringBoot交友APP项目实战(详细介绍+案例源码) - 10.网关配置

    系列文章目录 1. 项目介绍及环境配置 2. 短信验证码登录 3. 用户信息 4. MongoDB 5. 推荐好友列表/MongoDB集群/动态发布与查看 6. 圈子动态/圈子互动 7. 即时通讯(基 ...

  6. SpringBoot交友APP项目实战(详细介绍+案例源码) - 7.即时通讯(基于第三方API)

    陌上花开,可缓缓归矣 系列文章目录 1. 项目介绍及环境配置 2. 短信验证码登录 3. 用户信息 4. MongoDB 5. 推荐好友列表/MongoDB集群/动态发布与查看 6. 圈子动态/圈子互 ...

  7. Spring Cloud Alibaba 大型微服务项目实战

    作者介绍 程序员十三,多年一线开发经验,历任高级开发工程师.后端主程.技术部门主管等职位.同时也是开源项目的爱好者和贡献者.掘金优秀作者.CSDN 博客专家.实体图书作者.专栏作者.视频讲师. 小册介 ...

  8. 微服务开源框架TARS的RPC源码解析 之 初识TARS C++服务端

    作者:Cony 导语:微服务开源框架TARS的RPC调用包含客户端与服务端,<微服务开源框架TARS的RPC源码解析>系列文章将从初识客户端.客户端的同步及异步调用.初识服务端.服务端的工 ...

  9. [附源码]计算机毕业设计JAVA面向服装集群企业的个性化定制服务系统

    [附源码]计算机毕业设计JAVA面向服装集群企业的个性化定制服务系统 项目运行 环境配置: Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Ec ...

最新文章

  1. OpenCV中的光流及视频特征点追踪
  2. 重磅!单目深度估计方法:算法梳理与代码实现
  3. c语言开发游戏趋势,都9012年了,为何我还坚持用C语言开发游戏
  4. php post收不到值,php 取不到POST 值
  5. 正则表达式注意事项以及常用方法
  6. 沟通CTBS助六和集团实现财务集中管理
  7. 将一句话的单词进行倒置,标点符号不倒换。比如一句话:“i love you.”倒换后变为you. love i
  8. NIO(一)——缓冲区Buffer
  9. 【Xamarin挖墙脚系列:Xamarin的核心】
  10. [wp7游戏]wp7~~竞赛类游戏~~集合贴~~
  11. 若依框架使用笔记(一)
  12. 超六类网线和六类网线的区别—Vecloud
  13. 多种方法在Markdown加入上划线、中划线、下划线
  14. 25匹马选出最快的3匹马
  15. 强化学习DRL--策略学习(Actor-Critic)
  16. 正则表达式 “.*“匹配任意字符
  17. 揭秘美国云计算 大企业是第一推动力
  18. 默克尔树_默克尔树:正在使用中
  19. 安利一个最适合程序员的小众高薪兼职!
  20. CVPR论文复现争议后续:华人一作苦战两月给出有态度的分析结果

热门文章

  1. 【两万字】面试官:听说你很懂集合源码,接我二十道问题!
  2. java 完全匹配_正则表达式的完全匹配和部分匹配
  3. Swift4.0判断本函数是否在其它类有相同的方法
  4. Netty5的例子,简单介绍Netty的用法
  5. 关于在KEIL下使用j-link读不到芯片的问题
  6. java实现srt协议,的Java API的SRT字幕
  7. 小技巧!如何把小图拼接成长图,将长图切成小图
  8. python项目开发实战第2版pdf_《树莓派开发实战++第2版》.pdf
  9. 圣经中有关天使的记载
  10. Docker 容器健康检查机制