我是靠谱客的博主 暴躁舞蹈,这篇文章主要介绍Dobbo微服务项目实战(详细介绍+案例源码) - 5.推荐好友列表/MongoDB集群/动态发布与查看系列文章目录一、推荐好友列表二、MongoDB集群三、发布动态四、查询我的动态,现在分享给大家,希望可以做个参考。

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 文件:

复制代码
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
@RestController @RequestMapping("/tanhua") public class TanhuaController { @Autowired private 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 文件:

复制代码
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
@Service public class TanhuaService { @DubboReference private RecommendUserApi recommendUserApi; @DubboReference private UserInfoApi userInfoApi; // 查询今日佳人数据 public TodayBest todayBest() { // 1. 获取用户id Long 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. 获取用户id Long 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 文件:

复制代码
1
2
3
4
5
6
7
8
9
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 文件:

复制代码
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
@DubboService public class RecommendUserApiImpl implements RecommendUserApi{ @Autowired private MongoTemplate mongoTemplate; // 查询今日佳人 public RecommendUser queryWithMaxScore(Long toUserId) { // 根据toUserId查询,根据评分score排序,获取第一条 // 1. 构建Criteria Criteria criteria = Criteria.where("toUserId").is(toUserId); // 2. 构建Query Query 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 文件:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Data @NoArgsConstructor @AllArgsConstructor public class RecommendUserDto { private Integer page = 1; //当前页数 private Integer pagesize = 10; //页尺寸 private String gender; //性别 man woman private 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 文件:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 文件:

复制代码
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
@DubboService public class UserInfoApiImpl implements UserInfoApi{ @Autowired private UserInfoMapper userInfoMapper; @Override public void save(UserInfo userInfo) { userInfoMapper.insert(userInfo); } @Override public void update(UserInfo userInfo) { userInfoMapper.updateById(userInfo); } @Override public UserInfo findById(Long id) { return userInfoMapper.selectById(id); } /** * 批量查询用戶详情 * @param userIds 用户id列表 * @param info 用户详情信息 * @return */ @Override public 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 文件:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@RunWith(SpringRunner.class) @SpringBootTest(classes = AppServerApplication.class) public class UserInfoApiTest { @DubboReference private UserInfoApi userInfoApi; @Test public 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 文件:

复制代码
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
@Service public class TanhuaService { @DubboReference private RecommendUserApi recommendUserApi; @DubboReference private UserInfoApi userInfoApi; // 查询今日佳人数据 public TodayBest todayBest() { // 1. 获取用户id Long 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. 获取用户id Long 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):

复制代码
1
2
3
4
5
6
7
8
{ "_id": ObjectId("6018bc055098b2230031e2da"), "created": NumberLong("1612233733056"), "userId": NumberLong("1"), "friendId": NumberLong("106"), "_class": "com.itheima.domain.mongo.Friend" }

动态表(movement):

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{ "_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):

复制代码
1
2
3
4
5
6
7
8
9
{ "_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 文件:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/** * 好友表:好友关系表 */ @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; //用户id private Long friendId; //好友id private Long created; //时间 }

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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//动态详情表 @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; //媒体数据,图片或小视频 url private 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 文件:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/** * 好友时间线表,用于存储好友发布的数据 */ @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;//动态id private Long userId; //发布动态用户id private Long friendId; // 可见好友id private Long created; //发布的时间 }
②. API接口

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

复制代码
1
2
3
public interface MovementApi { }
③. API实现类

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

复制代码
1
2
3
4
5
6
7
@DubboService public class MovementApiImpl implements MovementApi { @Autowired private MongoTemplate mongoTemplate; }

⑵. 自增序列

①. 实体

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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
@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 文件:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component public class IdWorker { @Autowired private 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 测试类:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RunWith(SpringRunner.class) @SpringBootTest public class IdWorkerTest { @Autowired private IdWorker idWorker; @Test public void test() { Long id = idWorker.getNextId("test"); System.out.println(id); } }

在这里插入图片描述


⑷. 接口文档

在这里插入图片描述


⑸. Controller获取请求参数

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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RestController @RequestMapping("/movements") public class MovementController { @Autowired private MovementService movementService; /** * 发布动态 * @return */ @PostMapping public 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 文件:

复制代码
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
@Service public class MovementService { @Autowired private OssTemplate ossTemplate; @DubboReference private MovementApi movementApi; /** * 发布动态 */ public void publishMovement(Movement movement, MultipartFile[] imageContent) throws IOException { // 1. 判断发布动态的内容是否存在 if(StringUtils.isEmpty(movement.getTextContent())) { throw new BusinessException(ErrorResult.contentError()); } // 2. 获取当前登录的用户id Long 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 文件:

复制代码
1
2
3
4
5
6
public interface MovementApi { // 发布动态 void publish(Movement movement); }

⑻. API实现类

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

复制代码
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
@DubboService public class MovementApiImpl implements MovementApi { @Autowired private MongoTemplate mongoTemplate; @Autowired private 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 文件:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@RunWith(SpringRunner.class) @SpringBootTest(classes = AppServerApplication.class) public class MovementApiTest { @DubboReference private MovementApi movementApi; @Test public 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 文件:

复制代码
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
@Component public class TimeLineService { @Autowired private 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 文件:

复制代码
1
2
3
4
5
6
7
8
9
@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 文件:

复制代码
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
@DubboService public class MovementApiImpl implements MovementApi { @Autowired private MongoTemplate mongoTemplate; @Autowired private IdWorker idWorker; @Autowired private 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 文件:

复制代码
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
@Data @NoArgsConstructor @AllArgsConstructor public class MovementsVo implements Serializable { private String id; //动态id private Long userId; //用户id private String avatar; //头像 private String nickname; //昵称 private String gender; //性别 man woman private 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 文件:

复制代码
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
@RestController @RequestMapping("/movements") public class MovementController { @Autowired private MovementService movementService; /** * 发布动态 * @return */ @PostMapping public 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 文件:

复制代码
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
@Service public class MovementService { @Autowired private OssTemplate ossTemplate; @DubboReference private MovementApi movementApi; @DubboReference private UserInfoApi userInfoApi; /** * 发布动态 */ public void publishMovement(Movement movement, MultipartFile[] imageContent) throws IOException { // 1. 判断发布动态的内容是否存在 if(StringUtils.isEmpty(movement.getTextContent())) { throw new BusinessException(ErrorResult.contentError()); } // 2. 获取当前登录的用户id Long 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 文件:

复制代码
1
2
3
4
5
6
7
8
9
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 文件:

复制代码
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
@DubboService public class MovementApiImpl implements MovementApi { @Autowired private MongoTemplate mongoTemplate; @Autowired private IdWorker idWorker; @Autowired private 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(); } } // 查询我的动态 @Override public 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集群/动态发布与查看系列文章目录一、推荐好友列表二、MongoDB集群三、发布动态四、查询我的动态的全部内容,更多相关Dobbo微服务项目实战(详细介绍+案例源码)内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(71)

评论列表共有 0 条评论

立即
投稿
返回
顶部