热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

66探花08即时通讯功能实现(第8章)

第8章即时通讯功能实现今日内容介绍学习目标了解什么是即时通信了解探花交友的消息功能了解即时通信的技术方案了解环信的即时通讯实现环信的用户体系集成实现添加联系人、联系人列

第8章 即时通讯功能实现

今日内容介绍

63590012725


学习目标

  • 了解什么是即时通信

  • 了解探花交友的消息功能

  • 了解即时通信的技术方案

  • 了解环信的即时通讯

  • 实现环信的用户体系集成

  • 实现添加联系人、联系人列表功能

  • 实现点赞、评论、喜欢列表查询


1. 即时通信

【目标】

掌握环信通讯组件使用


【路径】

1:了解即时通讯

2:环信介绍

3:抽取环信组件


【讲解】


1.1. 即时通讯介绍


1.1.1. 什么是即时通信?

1569567156116


1.1.2. 功能说明

在探花交友项目中也提供了类似微信的聊天功能,用户可以和好友或陌生人聊天。

如果是陌生人,通过《聊一下》功能进行打招呼,如果对方同意后,就成为了好友,可以进行聊天了。

陌生人之间如果相互喜欢,那么就会成为好友,也就可以聊天了。

在消息界面中也可以查看:点赞、评论、喜欢、公告等消息信息。

1570760685758

1570760715769

1570760768500


1.1.3. 技术方案

对于高并发的即时通讯实现,还是很有挑战的,所需要考虑的点非常多,除了要实现功能,还要考虑并发、流量、负载、服务器、容灾等等。虽然有难度也并不是高不可攀。

对于现实即时通讯往往有两种方案:



  • 方案一:

    • 自主实现,从设计到架构,再到实现。

    • 技术方面可以采用:Netty + WebSocket + RocketMQ + MongoDB + Redis + ZooKeeper + MySQL

    • 1570761873597



  • 方案二:

    • 对接第三方服务完成。

    • 这种方式简单,只需要按照第三方的api进行对接就可以了。

    • 如:环信、网易、容联云通讯等。



如何选择呢?

如果是中大型企业做项目可以选择自主研发,如果是中小型企业研发中小型的项目,选择第二种方案即可。方案一需要有大量的人力、物力的支持,开发周期长,成本高,但可控性强。方案二,成本低,开发周期短,能够快速的集成起来进行功能的开发,只是在可控性方面来说就差了一些。

探花交友项目选择方案二进行实现。


1.2. 环信介绍

官网:https://www.easemob.com/ 稳定健壮,消息必达,亿级并发的即时通讯云

1570763722654


1.2.1. 开发简介

平台架构:

集成:

环信和用户体系的集成主要发生在2个地方,服务器端集成和客户端集成。

1570776683692

探花集成:



  • 探花前端使用AndroidSDK进行集成

    • 文档:http://docs-im.easemob.com/im/android/sdk/import



  • 后端集成用户体系

    • 文档:http://docs-im.easemob.com/im/server/ready/user




1.2.2. 环信Console

需要使用环信平台,那么必须要进行注册,登录之后即可创建应用。环信100以内的用户免费使用,100以上就要注册企业版了。

企业版价格:

1570778131775

创建应用:

1570778173832

创建完成:

1570778297121


1.2.3. 接口说明


1.2.3.1. Appkey 数据结构

当您申请了 AppKey 后,会得到一个 xxxx#xxxx 格式的字符串,字符串只能由小写字母数字组成,AppKey是环信应用的唯一标识。

前半部分 org_name 是在多租户体系下的唯一租户标识,后半部分 app_name 是租户下的app唯一标识(在环信后台创建一个app时填写的应用 id 即是 app_name )。

下述的 REST API 中,/{org_name}/{app_name}的请求,均是针对一个唯一的appkey进行的。目前环信注册的appkey暂不能由用户自己完成删除操作,如果对 APP 删除需要联系环信操作完成。





















Appkeyxxxx分隔符xxxx描述
环信应用的唯一标识org_name#app_nameapp_name只能是字母、数字、横线组合。长度不能超过32

1.2.3.2. 环信 ID 数据结构

环信作为一个聊天通道,只需要提供环信 ID (也就是 IM 用户名)和密码就够了。

























名称字段名数据类型描述
环信 IDusernameString在 AppKey 的范围内唯一用户名。
用户密码passwordString用户登录环信使用的密码。

1.2.3.3. 获取管理员权限

环信提供的 REST API 需要权限才能访问,权限通过发送 HTTP 请求时携带 token 来体现,下面描述获取 token 的方式。说明:API 描述的时候使用到的 {APP 的 client_id} 之类的这种参数需要替换成具体的值。

重要提醒:获取 token 时服务器会返回 token 有效期,具体值参考接口返回的 expires_in 字段值。由于网络延迟等原因,系统不保证 token 在此值表示的有效期内绝对有效,如果发现 token 使用异常请重新获取新的 token,比如“http response code”返回 401。另外,请不要频繁向服务器发送获取 token 的请求,同一账号发送此请求超过一定频率会被服务器封号,切记,切记!!

client_id 和 client_secret 可以在环信管理后台的APP 详情页面看到。


HTTP Request
















请求方式请求路径
img/{org_name}/{app_name}/token

Request Headers
















参数说明
Content-Typeapplication/json

Request Body
























参数说明
grant_typeclient_credentials
client_idApp的client_id,可在app详情页找到
client_secretApp的client_secret,可在app详情页找到

Response Body
























参数说明
access_token有效的token字符串
expires_intoken 有效时间,以秒为单位,在有效期内不需要重复获取
application当前 App 的 UUID 值

1570779400739


1.3. 抽取环信组件(了解)

63590156609

url:国内一区地址 url: http://a1.easemob.com/
orgName:1112190901181842
appName:tanhua119
Client ID:YXA6YbMJQfW_Q-miKVQajI4ZJQ
ClientSecret:YXA6oLQ-m3KF1qzbU4YG9FlJ92veUfY

tanhua-commons模块中抽取环信组件


1.3.1. pom文件

tanhua-commons模块pom文件加入依赖如下



org.springframework.boot
spring-boot-starter-web


1.3.2. HuanXinProperties

tanhua-commons模块properties包下

package com.tanhua.commons.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "tanhua.huanxin")
@Data
public class HuanXinProperties {
private String url;
private String orgName;
private String appName;
private String clientId;
private String clientSecret;
public String getHuanXinUrl() {
return this.url
+ this.orgName + "/"
+ this.appName;
}
}

1.3.3. HuanXinUser

tanhua-commons模块vo包下

package com.tanhua.commons.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;
@Data
@AllArgsConstructor
public class HuanXinUser implements Serializable {
private String username;
private String password;
private String nickname;
}

1.3.4.TanHuaException

package com.tanhua.commons.exception;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 自定义异常
*/
@Data
@NoArgsConstructor
public class TanHuaException extends RuntimeException {
private Object errData;
public TanHuaException(String errMessage){
super(errMessage);
}
public TanHuaException(Object data){
super();
this.errData = data;
}
}

1.3.4. HuanXinTemplate

tanhua-commons模块templates包下

package com.tanhua.commons.templates;
import com.alibaba.fastjson.JSON;
import com.tanhua.commons.exception.TanHuaException;
import com.tanhua.commons.properties.HuanXinProperties;
import com.tanhua.commons.vo.HuanXinUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import java.util.*;
/**
* 即时通讯模板类
*/
@Slf4j
public class HuanXinTemplate {
@Autowired
private RestTemplate restTemplate;
private HuanXinProperties props;
private long tokenDuration;
private String token;
public HuanXinTemplate(HuanXinProperties properties){
this.props = properties;
}
/**
* 发送消息
* @param target 接收方
* @param msg
*/
public void sendMsg(String target, String msg) {
String url = props.getHuanXinUrl();
url+="/messages";
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type","application/json");
headers.add("Authorization","Bearer " + getToken());
Map requestBody = new HashMap();
requestBody.put("target_type","users");
requestBody.put("target", Arrays.asList(target));
Map msgParam = new HashMap();
msgParam.put("msg", msg);
msgParam.put("type", "txt");
requestBody.put("msg", msgParam);
HttpEntity> httpEntity = new HttpEntity>(requestBody,headers);
ResponseEntity respOnseEntity= restTemplate.postForEntity(url, httpEntity, String.class);
try {
int statusCode = responseEntity.getStatusCode().value();
log.info("发送消息*****url:{}******sendMsg:{}******statusCode{}*****",url,msg,statusCode);
if(200 != statusCode){
throw new TanHuaException("发送信息失败!");
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 交友
* @param userId
* @param friendId
*/
public void makeFriends(Long userId, Long friendId){
String token = getToken();
String url = props.getHuanXinUrl();
url+="/users/"+ userId.toString()+"/contacts/users/" + friendId.toString();
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type","application/json");
headers.add("Authorization","Bearer " + token);
HttpEntity httpEntity = new HttpEntity(headers);
ResponseEntity respOnseEntity= restTemplate.postForEntity(url, httpEntity, String.class);
int statusCode = responseEntity.getStatusCode().value();
log.info("交友*****url:{}******userId:{}******friendId{}*****",url,userId,friendId);
if(200 != statusCode){
throw new TanHuaException("添加好友失败!");
}
}
/**
* 注册用户
* @param userId
*/
public void register(Long userId){
// 获取授权令牌
String token = getToken();
// 注册用户rest api地址
String url = props.getHuanXinUrl();
url+="/users";
// 请求体内容
HuanXinUser user = new HuanXinUser(userId.toString(),"123456","1");
// 请求头信息
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type","application/json");
headers.add("Authorization","Bearer " + token);
// 发送请求
ResponseEntity respOnseEntity= restTemplate.postForEntity(url, new HttpEntity(user, headers), String.class);
int statusCode = responseEntity.getStatusCode().value();
log.info("注册用户*****url:{}******userId:{}*****",url,userId);
if(200 != statusCode){
throw new TanHuaException("用户注册失败,HuanXin httpCode:"+statusCode);
}
}
/**
* 注册用户
*/
public void registerBatch(){
// 获取授权令牌
String token = getToken();
// 注册用户rest api地址
String url = props.getHuanXinUrl();
url+="/users";
// 请求体内容
HuanXinUser user = new HuanXinUser("1","123456","1");
List list = new ArrayList();
for (int i = 10; i <20 ; i++) {
list.add(new HuanXinUser(i+"",i+"",String.format("今晚打老虎_%05d",i)));
}
// 请求头信息
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type","application/json");
headers.add("Authorization","Bearer " + token);
// 发送请求
ResponseEntity respOnseEntity= restTemplate.postForEntity(url, new HttpEntity(list, headers), String.class);
int statusCode = responseEntity.getStatusCode().value();
log.info("注册用户*****url:{}******userId:{}*****",url,list);
if(200 != statusCode){
throw new TanHuaException("用户注册失败,HuanXin httpCode:"+statusCode);
}
}
/**
* 获取授权
* @return
*/
private String getToken(){
if(tokenDuration return applyNewToken();
}
return token;
}
/**
* 获取管理员的授权令牌
* @return
*/
private String applyNewToken(){
String url = props.getHuanXinUrl();
url+="/token";
Map paramMap = new HashMap();
paramMap.put("grant_type","client_credentials");
paramMap.put("client_id",props.getClientId());
paramMap.put("client_secret",props.getClientSecret());
ResponseEntity resEntity = restTemplate.postForEntity(url, paramMap, String.class);
int statusCode = resEntity.getStatusCode().value();
log.info("获取管理员的授权令牌*****url:{}******statusCode:{}*****",url,statusCode);
if(200 != statusCode){
throw new TanHuaException("获取环信token失败");
}
Map resultMap = JSON.parseObject(resEntity.getBody(),Map.class);
long expiresInSecOnds= (int)resultMap.get("expires_in");
tokenDuration = System.currentTimeMillis()-10*60*1000 + expiresInSeconds*1000;
token = (String) resultMap.get("access_token");
return token;
}
}

1.3.5. CommonsAutoConfiguration

tanhua-commons模块修改CommonsAutoConfiguration自动装配类中

package com.tanhua.commons;
... ...
/**
* 自动配置类
*/
@Configuration
@EnableConfigurationProperties({SmsProperties.class, OssProperties.class, FaceProperties.class, HuanXinProperties.class})
public class CommonsAutoConfiguration {
... ...
@Bean
public HuanXinTemplate huanXinTemplate(HuanXinProperties huanXinProperties){
return new HuanXinTemplate(huanXinProperties);
}

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder){
return builder.build();
}
}

1.3.6. application.yml

tanhua-server模块的application.yml文件加入配置如下

tanhua:
...
# im
huanxin:
url: http://a1.easemob.com/
orgName: 1112190901181842
appName: tanhua
clientId: YXA6fVCzHj7rQjySx-6Ffsq2PA
clientSecret: YXA62etJXe7EZloCx-SjHByJ5uc4HQQ

1.3.7. 测试

tanhua-server模块的test包下

package com.tanhua.server.test;
import com.alibaba.fastjson.JSON;
import com.tanhua.commons.templates.HuanXinTemplate;
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;
import java.util.HashMap;
import java.util.Map;
@SpringBootTest
@RunWith(SpringRunner.class)
public class HuanXinTest {
@Autowired
private HuanXinTemplate huanXinTemplate;
@Test
public void testRegister(){
huanXinTemplate.register(1l);
huanXinTemplate.register(2l);
}
@Test
public void registerBatch(){
huanXinTemplate.registerBatch();
}
@Test
public void makeFriend(){
huanXinTemplate.makeFriends(1l,2l);
}
@Test
public void sendMsg() {
String ab = "{\"userId\": \"1\",\"nickname\":\"黑马小妹\",\"strangerQuestion\": \"你喜欢去看蔚蓝的大海还是去爬巍峨的高山?\",\"reply\": \"我喜欢秋天的落叶,夏天的泉水,冬天的雪地,只要有你一切皆可~\"}";
Map map = new HashMap<>();
map.put("userId", "1");
map.put("nickname", "小师妹");
map.put("strangerQuestion", "你喜欢去看蔚蓝的大海还是去爬巍峨的高山");
map.put("reply", "我喜欢秋天的落叶,夏天的泉水,冬天的雪地,只要有你一切皆可~");
String msg = JSON.toJSONString(map);
System.out.println(msg);
huanXinTemplate.sendMsg("2",msg);
}
}

【小结】

掌握环信组件使用


2. 应用集成(重点)

【目标】

掌握环信通讯集成使用


【路径】

1:注册环信用户

2:查询环信用户信息

3:发送消息给客户端


【讲解】

61432878229

将用户体系集成的逻辑写入到tanhua-server系统中。



  • 探花用户注册时需要将用户信息注册到环信系统中

    • 对于老数据:编写单元测试方法批量的注册到环信

    • 对于新数据:loginVerification注册到环信



  • APP从服务端获取当前用户的环信用户密码,自动登入环信系统

  • APP自动获取环信服务器发送的信息数据


2.1. 注册环信用户

在用户登录逻辑中,当第一次注册时,将用户信息注册到环信

tanhua-server模块的service包下

@Autowired
private HuanXinTemplate huanXinTemplate;
/**
* 注册登录-第一步:验证码校验(登录)
*/
public Map loginVerification(String phone, String verificationCode) {
......
//用户不存在,自动注册用户
if (user == null) {
......
//注册环信通讯
huanXinTemplate.register(user.getId());
}
}

2.2. 查询环信用户信息

在app中,用户登录后需要根据用户名密码登录环信,由于用户名密码保存在后台,所以需要提供接口进行返回。

1570852095822

tanhua-server模块的controller包下编写HuanXinController实现

package com.tanhua.server.controller;
import com.tanhua.commons.vo.HuanXinUser;
import com.tanhua.domain.db.User;
import com.tanhua.server.interceptor.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* 登录环信云-由app来实现
* 后台主要返回当前登录的用户信息
*/
@RestController
@RequestMapping("/huanxin")
@Slf4j
public class HuanxinUserController {
/**
* 返回app需要当前用户的账号 密码
*/
@RequestMapping(value = "/user",method = RequestMethod.GET)
public ResponseEntity huanxinUser(){
log.debug("************环信登录成功了**************");
User user = UserHolder.getUser();
//用户id //密码 //昵称
HuanXinUser huanXinUser = new HuanXinUser(user.getId().toString(),"123456","小红");
return ResponseEntity.ok(huanXinUser);
}
}

2.3. 发送消息给客户端

目前已经完成了用户体系的对接,下面我们进行测试发送消息,场景是这样的:

image-20201108104329308

1570853314493

点击“聊一下”,就会给对方发送一条陌生人信息,这个消息由系统发送完成。

我们暂时通过环信的控制台进行发送: 1570853400508

消息内容:

{"userId": "1","nickname":"黑马小妹","strangerQuestion": "你喜欢去看蔚蓝的大海还是去爬巍峨的高山?","reply": "我喜欢秋天的落叶,夏天的泉水,冬天的雪地,只要有你一切皆可~"}

1570853459084

1570853651822

1570853668888

可以看到已经接收到了消息。


2.4. 测试注意

1.保证用户一定是已经注册的,否则不能使用环信云

2.如果用户之间需要发送消息(即时通讯),一定先登录(由app来实现)

3.清除数据,重新进行配置

63590574736

63590591339

4.配置环信

63590565183

5.消息模块-显示加载失败,请联系管理员

63590560002

6.如果再次打开失败,重新登录一次

7.app如果使用了a用户登录了,只能使用a用户操作。


【小结】

掌握环信通讯集成使用


3. 消息管理(重点)

【目标】

掌握消息管理实现


【路径】

1:查看用户详情

2:查看陌生人消息

3:回复陌生人消息

4:添加联系人

5:联系人列表


【讲解】

客户端完成消息管理共有三步



  • 查看用户详情,点击聊一下查看陌生人问题

  • 填写内容,发送陌生人消息

  • 如果对方感兴趣,可以回复陌生人消息,双方成为好友


3.1. 服务消费者-查看用户详情

在首页可以查看感兴趣人的详细资料。点击“聊一下”,可以查看对方的问题

61433244399


3.1.1. mock接口

image-20201107213518294


3.1.2. TodayBestController

/**
* 佳人信息
*/
@RequestMapping(value = "/{id}/personalInfo",method = RequestMethod.GET)
public ResponseEntity personalInfo(@PathVariable("id") Long personId){
TodayBestVo todayBestVo=todayBestService.personalInfo(personId);
return ResponseEntity.ok(todayBestVo);
}

3.1.3. TodayBestService

/**
* 佳人信息
*/
public TodayBestVo personalInfo(Long personId) {
Long userId = UserHolder.getUserId();//当前用户id
//1 根据当前用户id 推荐的用户id查询缘分值
RecommendUser recommendUser = recommendUserApi.queryScoreById(userId,personId);
//2 调用getTodayBestVo得到佳人信息
TodayBestVo todayBestVo = getTodayBestVo(recommendUser);
//3 返回佳人信息
return todayBestVo;
}

3.2. 服务提供者-查看用户详情


3.2.1. RecommendUserApi

tanhua-dubbo-interface模块编写查询用户缘分值方法

/**
* 根据当前用户id 和 佳人用户id 查询缘分值
* @param userId
* @param personId
* @return
*/
RecommendUser queryScoreById(Long userId, Long personId);

3.2.2. RecommendUserApiImpl

tanhua-dubbo-service模块编写查询用户缘分值方法

/**
* 根据当前用户id 和 佳人用户id 查询缘分值
* @param userId
* @param personId
* @return
*/
@Override
public RecommendUser queryScoreById(Long userId, Long personId) {
Query query = new Query();
query.addCriteria(Criteria.where("userId").is(personId).and("toUserId").is(userId));
RecommendUser recommendUser = mongoTemplate.findOne(query, RecommendUser.class);
if(recommendUser == null || recommendUser.getScore() == null){
recommendUser = new RecommendUser();
recommendUser.setUserId(personId);//推荐的用户id
recommendUser.setToUserId(userId);//给谁推荐的用户
recommendUser.setScore(66.6);//缘分值
}
return recommendUser;
}

3.2.3 测试

为方便测试可以修改RecommendUserApiImpl类中findPage方法

query.with(Sort.by(Sort.Order.asc("userId")));

61346987427


3.3. 服务消费者-查询陌生人问题

点击“聊一下”,可以查看对方的问题

61433276236


3.3.1. mock接口

image-20201107213537641


3.3.2. TodayBestController

/**
* 查询陌生人问题
*/
@RequestMapping(value = "/strangerQuestions",method = RequestMethod.GET)
public ResponseEntity strangerQuestions(Long userId){
String cOntent=todayBestService.strangerQuestions(userId);
return ResponseEntity.ok(content);
}

3.3.3. TodayBestService

/**
* 查询陌生人问题
*/
public String strangerQuestions(Long userId) {
Question question = questionApi.findByUserId(userId);
String strangerQuestion = "约吗?";//默认值
if (question != null && !StringUtils.isEmpty(question.getTxt())) {
strangerQuestion = question.getTxt();
}
return strangerQuestion;
}

3.3.4. 测试

61347028435


3.3.服务消费者-回复陌生人消息

需求:



  • 通过服务器端,给目标用户发送一条陌生人消息

61433323933


3.3.1. mock接口

image-20201107213558481


3.3.2. TodayBestController

/**
* 回复陌生人问题
*/
@RequestMapping(value = "/strangerQuestions",method = RequestMethod.POST)
public ResponseEntity replyQuestions(@RequestBody Map params){
Long userId = Long.parseLong(params.get("userId").toString());
String reply = (String)params.get("reply");
todayBestService.replyQuestions(userId,reply);
return ResponseEntity.ok(null);
}

3.3.3. TodayBestService

TodayBestService编写方法,完成回复陌生人消息功能

/**
* 回复陌生人问题
*/
public void replyQuestions(Long personId, String reply) {
Long currentUserId = UserHolder.getUserId();
//1.根据当前登录id查询用户信息
UserInfo userInfo = userInfoApi.findUserInfoByUserId(currentUserId);
//2根据陌生人用户id查询陌生人问题
Question question = questionApi.findByUserId(personId);
String strangerQuestion = "约吗?";//默认值
if (question != null && !StringUtils.isEmpty(question.getTxt())) {
strangerQuestion = question.getTxt();
}
//3构造消息调用环信发送即时通讯消息
//{"userId": "1","nickname":"黑马小妹",
// "strangerQuestion": "你喜欢去看蔚蓝的大海还是去爬巍峨的高山?",
// "reply": "我喜欢秋天的落叶,夏天的泉水,冬天的雪地,只要有你一切皆可~"}
Map msgMap = new HashMap<>();
msgMap.put("userId",currentUserId.toString());//当前用户的id
msgMap.put("nickname",userInfo.getNickname());//当前用户的昵称
msgMap.put("strangerQuestion",strangerQuestion);//佳人的问题
msgMap.put("reply",reply);//当前用户的回复的内容
//将map转为string
String msg = JSON.toJSONString(msgMap);
huanXinTemplate.sendMsg(personId.toString(),msg);
}

3.3.4. 测试

发送消息测试

61347611387

接收消息测试

61347617708

postman:

63591190129


3.4. 服务消费者-添加联系人

用户获取陌生人消息后,点击“聊一下”,就会成为联系人(好友)。

实现:



  • 将好友写入到MongoDB中

  • 将好友关系注册到环信

61448373742


3.4.1. mock接口

1570886419453


3.4.2. IMController

package com.tanhua.server.controller;
import com.tanhua.server.service.IMService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
/**
* 消息管理-控制层
*/
@RestController
@RequestMapping("/messages")
@Slf4j
public class IMController {
@Autowired
private IMService imService;
/**
* 联系人添加
*/
@RequestMapping(value = "/contacts",method = RequestMethod.POST)
public ResponseEntity saveContacts(@RequestBody Map params){
Long persOnId= Long.parseLong(params.get("userId").toString());
imService.saveContacts(personId);
return ResponseEntity.ok(null);
}
}

3.4.3. IMService

IMService补充添加联系人方法

package com.tanhua.server.service;
import com.tanhua.commons.templates.HuanXinTemplate;
import com.tanhua.dubbo.api.mongo.FriendApi;
import com.tanhua.server.interceptor.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 消息管理-业务逻辑处理层
*/
@Service
@Transactional
@Slf4j //日志注解
public class IMService {
@Reference
private FriendApi friendApi;
@Autowired
private HuanXinTemplate huanXinTemplate;
/**
* 联系人添加
*/
public void saveContacts(Long personId) {
Long userId = UserHolder.getUserId();
//1 调用好友服务方法 往好友表插入2条记录
friendApi.saveContacts(personId,userId);
//2 调用环信云 成为好友
huanXinTemplate.makeFriends(userId,personId);
}
}

3.5. 服务提供者-添加联系人


3.5.1. FriendApi

创建FriendApi添加好友的方法

package com.tanhua.dubbo.api.mongo;
/**
* 好友管理服务接口
*/
public interface FriendApi {
/**
* 联系人添加
* @param personId
* @param userId
*/
void saveContacts(Long personId, Long userId);
}

3.5.2. FriendApiImpl

创建FriendApiImpl添加好友的方法

package com.tanhua.dubbo.api.mongo;
import com.tanhua.domain.mongo.Friend;
import org.apache.dubbo.config.annotation.Service;
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;
/**
* 联系人服务接口实现类
*/
@Service
public class FriendApiImpl implements FriendApi{
@Autowired
private MongoTemplate mongoTemplate;
/**
* 联系人添加
* @param personId
* @param userId
*/
@Override
public void saveContacts(Long personId, Long userId) {
//1.先查询personId userId记录是否存在
Query query1 = new Query();
query1.addCriteria(Criteria.where("userId").is(personId).and("friendId").is(userId));
if(!mongoTemplate.exists(query1, Friend.class)) {
//2.如果不存在则保存personId userId
Friend friend = new Friend();
friend.setId(ObjectId.get());//主键id
friend.setUserId(personId);//
friend.setFriendId(userId);//
friend.setCreated(System.currentTimeMillis());
mongoTemplate.insert(friend);
}
Query query2 = new Query();
query2.addCriteria(Criteria.where("userId").is(userId).and("friendId").is(personId));
//3.先查询userId personId 记录是否存在
if(!mongoTemplate.exists(query2, Friend.class)) {
//4.如果不存在则保存 userId personId
Friend friend = new Friend();
friend.setId(ObjectId.get());//主键id
friend.setUserId(userId);//
friend.setFriendId(personId);//
friend.setCreated(System.currentTimeMillis());
mongoTemplate.insert(friend);
}
}
}

3.5.3. 测试

postman测试:

61347778405

61347781745

可以看到好友已经添加成功。

app测试:注册新用户测试

61347773035


3.6. 服务消费者-联系人列表

61434595052


3.6.1. mock接口

1570938475069

响应数据结构:

1570938948137


3.6.2. ContactVo

package com.tanhua.domain.vo;
import lombok.Data;
@Data
public class ContactVo {
private Long id;
private String userId;
private String avatar;
private String nickname;
private String gender;
private Integer age;
private String city;
}

3.6.3. IMController

IMController中添加查询联系人列表方法

/**
* 联系人列表
*/
@RequestMapping(value = "/contacts",method = RequestMethod.GET)
public ResponseEntity findPageContacts(@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int pagesize, String keyword){
PageResult pageResult = imService.findPageContacts(page,pagesize,keyword);
return ResponseEntity.ok(pageResult);
}

3.6.4. IMService

IMService中添加查询联系人方法

/**
* 联系人列表
*/
public PageResult findPageContacts(int page, int pagesize, String keyword) {
Long userId = UserHolder.getUserId();
//1.查询好友表
PageResult friendPageResult = friendApi.findPageContacts(page,pagesize,userId);
if(friendPageResult == null || CollectionUtils.isEmpty(friendPageResult.getItems())){
return new PageResult<>(0l,10l,0l,1l,null);
}
//2查询 tb_userInfo
List listCOntactVo= new ArrayList<>();
long id = 1l;
for (Friend friend : friendPageResult.getItems()) {
ContactVo cOntactVo= new ContactVo();
Long friendId = friend.getFriendId();//根据好友id查询好友信息
UserInfo userInfo = userInfoApi.findUserInfoByUserId(friendId);
BeanUtils.copyProperties(userInfo,contactVo);//avatar nickname gender age city
contactVo.setId(id);//编号
contactVo.setUserId(friendId.toString());//好友用户id
listContactVo.add(contactVo);
id++;
}
//3 构造vo返回
PageResult voPageResult = new PageResult<>();
BeanUtils.copyProperties(friendPageResult,voPageResult);
voPageResult.setItems(listContactVo);
return voPageResult;
}

3.7. 服务提供者-联系人列表


3.7.1. FriendApi

在FriendApi中添加分页查询方法

/**
* 好友联系人分页查询
* @param page
* @param pagesize
* @param userId
* @return
*/
PageResult findPageContacts(int page, int pagesize, Long userId);

3.7.2. FriendApiImpl

在FriendApiImpl中添加分页查询方法

/**
* 好友联系人分页查询
* @param page
* @param pagesize
* @param userId
* @return
*/
@Override
public PageResult findPageContacts(int page, int pagesize, Long userId) {
Query query = new Query();
query.addCriteria(Criteria.where("userId").is(userId));//当前用户id查询好友ids
query.limit(pagesize);
query.skip((page-1)*pagesize);
long counts = mongoTemplate.count(query, Friend.class);
//2,查询当前页面数据
List friendList = mongoTemplate.find(query, Friend.class);
//3.封装PageResult对象返回
//pages //总页数
long pages = counts/pagesize + (counts%pagesize > 0 ?1:0);
return new PageResult<>(counts,(long)pagesize,pages,(long)page,friendList);
}

3.7.3. 测试

61348025496

61348027608

61348029315


【小结】

掌握消息管理功能


4. 点赞评论喜欢列表查询

【目标】

掌握点赞评论喜欢功能


【路径】

1:点赞评论喜欢功能分析

2:点赞评论喜欢功能实现


【讲解】


4.1. 服务消费者-点赞评论喜欢

63592327684

61434753158


4.1.1. MessageVo

根据接口定义vo对象。

package com.tanhua.domain.vo;
import lombok.Data;
@Data
public class MessageVo {
private String id;
private String avatar;
private String nickname;
private String createDate;
}

4.1.2. IMController

/**
* 点赞列表 //评论类型,1-点赞,2-评论,3-喜欢
*/
@RequestMapping(value = "/likes",method = RequestMethod.GET)
public ResponseEntity likes(@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int pagesize){
PageResult pageResult = imService.findPageComments(page,pagesize,1);
return ResponseEntity.ok(pageResult);
}
/**
* 评论列表 //评论类型,1-点赞,2-评论,3-喜欢
*/
@RequestMapping(value = "/comments",method = RequestMethod.GET)
public ResponseEntity comments(@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int pagesize){
PageResult pageResult = imService.findPageComments(page,pagesize,2);
return ResponseEntity.ok(pageResult);
}
/**
* 喜欢列表 //评论类型,1-点赞,2-评论,3-喜欢
*/
@RequestMapping(value = "/loves",method = RequestMethod.GET)
public ResponseEntity loves(@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int pagesize){
PageResult pageResult = imService.findPageComments(page,pagesize,3);
return ResponseEntity.ok(pageResult);
}

4.1.3. IMService

/**
* 点赞列表 //评论类型,1-点赞,2-评论,3-喜欢
*/
public PageResult findPageComments(int page, int pagesize, int type) {
Long userId = UserHolder.getUserId();
//1.根据publishUserId = 当前用户id and type=1 2 3来进行分页查询
PageResult pageCommentsMessages = commentApi.findPageCommentsMessages(page,pagesize,type,userId);
if(pageCommentsMessages == null || CollectionUtils.isEmpty(pageCommentsMessages.getItems())){
return new PageResult<>(0l,10l,0l,1l,null);
}
//2.查询tb_userInfo
List messageVoList = new ArrayList<>();
for (Comment comment : pageCommentsMessages.getItems()) {
MessageVo messageVo= new MessageVo();
Long commentUserId = comment.getUserId();//评论人的用户id
UserInfo userInfo = userInfoApi.findUserInfoByUserId(commentUserId);
BeanUtils.copyProperties(userInfo,messageVo);//头像 昵称
messageVo.setId(userInfo.getId().toString());//评论人用户id
String createDate = new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date(comment.getCreated()));
messageVo.setCreateDate(createDate);//评论时间
messageVoList.add(messageVo);
}
//3.构造vo返回
PageResult voPageResult = new PageResult<>();
BeanUtils.copyProperties(pageCommentsMessages,voPageResult);
voPageResult.setItems(messageVoList);
return voPageResult;
}

4.2. 服务提供者-点赞评论喜欢


4.2.1. Comment

点赞、评论、喜欢列表应该是别人对我发布的信息做了操作之后显示的数据,所以查询条件是发布人的id作为查询条件。

修改Comment对象,增加 publishUserId 字段。

package com.tanhua.domain.mongo;
import lombok.Data;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.mapping.Document;
import java.io.Serializable;
@Data
@Document(collection = "quanzi_comment")
public class Comment implements Serializable {
private ObjectId id;
private ObjectId publishId; //发布id
private Integer commentType; //评论类型,1-点赞,2-评论,3-喜欢
private Integer pubType; //评论内容类型: 1-对动态操作 2-对视频操作 3-对评论操作
private String content; //评论内容
private Long userId; //评论人
private Integer likeCount = 0; //点赞数
private Long created; //发表时间
private Long publishUserId; //被评论人ID
//动态选择更新的字段
public String getCol() {
return this.commentType == 1 ? "likeCount" : commentType==2? "commentCount"
: "loveCount";
}
}

4.2.2. CommentApi

/**
* 通过发布者id和评论的类型分页查询
*
* @param userId
* @param commentType
* @param page
* @param pageSize
* @return
*/
PageResult findByUserId(Integer page, Integer pageSize,Integer commentType,Long userId);

4.2.3. CommentApiImpl

/**
* 点赞列表 //评论类型,1-点赞,2-评论,3-喜欢
*/
@Override
public PageResult findPageCommentsMessages(int page, int pagesize, int type, Long userId) {
//1.根据publishUserId=userId and type = 1 2 3 分页查询评论表
Query query = new Query();
query.addCriteria(
Criteria.where("publishUserId").is(userId)
.and("commentType").is(type)
);
query.limit(pagesize);
query.skip((page - 1) * pagesize);
long counts = mongoTemplate.count(query, Comment.class);
//2,查询当前页面数据
List commentList = mongoTemplate.find(query, Comment.class);
//3.封装PageResult对象返回
//pages //总页数
long pages = counts / pagesize + (counts % pagesize > 0 ? 1 : 0);
return new PageResult<>(counts, (long) pagesize, pages, (long) page, commentList);
}

4.2.4. CommentApiImpl

修改点赞 评论 喜欢

/**
* 动态点赞
* 动态喜欢
*
* @param comment
* @return
*/
@Override
public int saveComment(Comment comment) {
//1保存点赞记录到评论表 、
comment.setId(ObjectId.get());//主键id
comment.setCreated(System.currentTimeMillis());//点赞时间
//设置被评论的用户id
Publish publish = mongoTemplate.findById(comment.getPublishId(), Publish.class);
Long publishUserId = publish.getUserId();
comment.setPublishUserId(publishUserId);
mongoTemplate.insert(comment);
//2更新发布表点赞数量+1 、喜欢数量+1 评论数量+1 更新评论表 点赞数量+1
updateCount(comment, 1);
//3查询发布表点赞 喜欢 评论数量返回 查询评论表点赞数量返回
return findCount(comment);
}

4.2.5. 测试

61348453122

61348455023

61348456651

61348457962

61348459356


【小结】

掌握点赞评论喜欢列表查询功能


总结

  1. 了解环信云使用



  2. 应用集成(探花项目 用上 环信云)--面试 整个流程-重点掌握

    a.注册

    b.登录

    c.即时通讯

    注意:测试注意严格按照课上说的7点



  3. 聊一下-查询用户详情 查询陌生人问题 回复陌生人问题(首页推荐用户-聊一下-输入回答的内容,点击聊一下)-重点掌握

    注意:消息 key value都是string类型 发消息的用户id特别注意下





  4. 添加联系人



  5. 联系人列表



  6. 点赞评论喜欢列表查询

    注意:comment表中新增一个被评论人用户id





推荐阅读
  • 本文介绍了在Ubuntu 11.10 x64环境下安装Android开发环境的步骤,并提供了解决常见问题的方法。其中包括安装Eclipse的ADT插件、解决缺少GEF插件的问题以及解决无法找到'userdata.img'文件的问题。此外,还提供了相关插件和系统镜像的下载链接。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • Redis API
    安装启动最简启动命令行输入验证动态参数启动配置文件启动常用配置通用命令keysbdsize计算key的总数exists判断是否存在delkeyvalue删除指定的keyvalue成 ... [详细]
  • 负载均衡_Nginx反向代理动静分离负载均衡及rewrite隐藏路径详解(Nginx Apache MySQL Redis)–第二部分
    nginx反向代理、动静分离、负载均衡及rewrite隐藏路径详解 ... [详细]
  • Android图形架构学习笔记(待修改)
    以下简单总结来自Android官网,稍作总结:https:source.android.google.cndevicesgraphics概览Andr ... [详细]
  • Monkey《大话移动——Android与iOS应用测试指南》的预购信息发布啦!
    Monkey《大话移动——Android与iOS应用测试指南》的预购信息已经发布,可以在京东和当当网进行预购。感谢几位大牛给出的书评,并呼吁大家的支持。明天京东的链接也将发布。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • 微信开放外链的第二阶段:腾讯和阿里巴巴的博弈
    2021年11月30日,微信开始进行“开放外链”的第二阶段,允许在微信个人会话中打开外部链接和在微信群中打开电商链接。虽然这是腾讯和阿里巴巴都能接受的阶段性结果,但双方都不会太满意。接下来几个月,腾讯和阿里将展开复杂的博弈,我们作为外人很难看清全过程。工信部从未要求腾讯无条件开放微信API,本次开放的也只是普通的HTTP链接。 ... [详细]
  • 本文介绍了互联网思维中的三个段子,涵盖了餐饮行业、淘品牌和创业企业的案例。通过这些案例,探讨了互联网思维的九大分类和十九条法则。其中包括雕爷牛腩餐厅的成功经验,三只松鼠淘品牌的包装策略以及一家创业企业的销售额增长情况。这些案例展示了互联网思维在不同领域的应用和成功之道。 ... [详细]
author-avatar
Rac__hel黄蓉
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有