【探花交友DAY 07】即时通讯模块的实现

1. 即时通讯模块设计

1.1 什么是即时通讯

即时通讯简称IM(Instant Message),是指能够即时地发送和接受互联网消息等服务。常见的即时通讯服务有QQ,微信等。在我们探花交友项目中,也运用到了即时通讯技术。两个陌生人之间只要满足互相喜欢的条件,就可以互相发送即时通讯消息。

即时通讯模块如下图所示:

image-20230101201123347

image-20230101201208697

1.2 技术选型

目前实现即时通讯的方案主要有一下两种方案:

  • 方案一:自主实现,技术方面会用到Netty + WebSocket + RocketMQ + MongoDB + Redis + ZooKeeper + MySQL

image-20230101201424217

  • 方案二:自己实现的方案比较复杂,适合于大型互联网公司,对于中小型应用来说,我们可以借助第三方服务实现即时通讯的功能,常见的服务提供商有:环信,网易等。

我们探花交友项目采取环信实现即时通讯。

1.3 环信的工作流程

  1. 首先所有注册的用户都会拥有一个唯一的环信用户名和密码。也就是说在完成用户注册的同时,我们也应该为这个用户创建一个环信账号。账号和密码保存到user表中。

image-20230101202201849

  1. 要想实现好友之间的通讯,环信还需要记录好友之间的关系,因此在我们添加好友的相关代码中,还需要将用户的好友信息注册到环信中。

image-20230101202343525

  1. 当用户登陆探花交友服务器时,需要获取到自己账户的环信账号和密码,然后客户端会自动登录到环信服务器。

image-20230101202619183

  1. 两个手机端都链接到环信服务器后,就可以进行实时的聊天了,聊天实际上走的是环信的服务器,和本地的探花交友服务器之间没有交互信息。

image-20230101202647062

1.4 抽取环信组件

和之前阿里云短信服务一样,这些第三方服务我们通常抽取变成可以自动装配的工具类。抽取一共三个步骤,编写perperties类,编写template类,以及在配置类中创建bean对象。

@Data
@ConfigurationProperties(prefix = "tanhua.huanxin")
public class HuanXinProperties {private String appKey;private String ClientId;private String secretKey;
}
@Slf4j
public class HuanXinTemplate {private EMService service;public HuanXinTemplate(HuanXinProperties properties) {EMProperties emProperties = EMProperties.builder().setAppkey(properties.getAppKey()).setClientId(properties.getClientId()).setClientSecret(properties.getSecretKey()).build();service = new EMService(emProperties);}//创建环信用户public Boolean createUser(String username,String password) {try {//创建环信用户service.user().create(username.toLowerCase(), password).block();return true;}catch (Exception e) {e.printStackTrace();log.error("创建环信用户失败~");}return false;}//添加联系人public Boolean addContact(String username1,String username2) {try {//创建环信用户service.contact().add(username1,username2).block();return true;}catch (Exception e) {log.error("添加联系人失败~");}return false;}//删除联系人public Boolean deleteContact(String username1,String username2) {try {//创建环信用户service.contact().remove(username1,username2).block();return true;}catch (Exception e) {log.error("删除联系人失败~");}return false;}//发送消息public Boolean sendMsg(String username,String content) {try {//接收人用户列表Set<String> set = CollUtil.newHashSet(username);//文本消息EMTextMessage message = new EMTextMessage().text(content);//发送消息  from:admin是管理员发送service.message().send("admin","users",set,message,null).block();return true;}catch (Exception e) {log.error("删除联系人失败~");}return false;}
}
@Bean
public HuanXinTemplate huanXinTemplate(HuanXinProperties huanXinProperties) {return new HuanXinTemplate(huanXinProperties);
}

2. 即时通讯模块的实现

使用第三方即时通讯技术,最重要的部分就是用户体系的集成。我们改造的计划如下

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

    • 对于老用户,使用单元测试批量注册到环信
    • 对于新用户,改在用户注册代码,在注册的时候自动注册到环信
  • 用户登陆到客户端,需要获取当前登陆用户的环信账号和密码,登录到环信系统

    • 编写相关接口实现
  • APP自动根据环信账户登陆到环信

2.1 注册环信用户

修改用户登陆逻辑,当新用户注册的时候,同时注册环信,并将用户名和密码写入到数据库中

image-20230101211158416

public Map loginVerification(String phone, String code) {// 1.从Redis中获取到验证码String redisCode = this.redisTemplate.opsForValue().get(VERIFICATION_CODE_PREFIX + phone);// 2.比较验证码if (StringUtils.isEmpty(redisCode) || !redisCode.equals(code)) {// 验证码无效或者验证码错误throw new BusinessException(ErrorResult.loginError());}// 3.判断用户是否已经存在User user = userApi.findByMobile(phone);// 4.如果不存在则新建用户boolean isNew = false;if (user == null) {// 用户不存在user = new User();user.setMobile(phone);user.setPassword(DigestUtils.md5Hex("123456"));Long id = this.userApi.save(user);user.setId(id);isNew = true;// 将用户注册到环信// 1. 生成环信的用户名和密码String hxUser = Constants.HX_USER_PREFIX + user.getId();// 2. 保存到环信Boolean flag = this.huanXinTemplate.createUser(hxUser, Constants.INIT_PASSWORD);// 3. 如果保存成功,则将用户名密码保存到数据库中if(flag) {user.setHxUser(hxUser);user.setHxPassword(Constants.INIT_PASSWORD);this.userApi.updateHx(user);}}// 5.生成Token 保存id和phoneMap tokenMap = new HashMap();tokenMap.put("id", user.getId());tokenMap.put("mobile", phone);String token = JwtUtils.getToken(tokenMap);// 6.封装结果Map retMap = new HashMap();retMap.put("token", token);retMap.put("isNew", isNew);return retMap;
}

2.2 查询登陆用户的环信账户和密码

当用户登陆到APP后,前端会自动向后端发送请求,查询当前登陆用户的环信账户,然后根据账户和密码登陆到环信服务器。下面编写接口用户前端获取登陆用户的环信账号和密码。接口如下:

image-20230101203858954

为了保存数据,我们创建VO对象

@Data
@NoArgsConstructor
@AllArgsConstructor
public class HuanXinUserVo {private String username;private String password;
}

下面是Controller以及Service相关代码

/*** 根据环信id查询出对应的用户详情* 这一个主要是在用户聊天时显示用户的头像等信息** @param huanxinId 环信用户名* @return*/
@GetMapping("/userinfo")
public ResponseEntity getUserInfoByHxId(String huanxinId) {UserInfoVo userInfo = this.messageService.getUserInfoByHxId(huanxinId);return ResponseEntity.ok(userInfo);
}
public UserInfoVo getUserInfoByHxId(String huanxinId) {// 获取到用户idLong userId = Long.parseLong(huanxinId.substring(2));// 根据用户id查询用户详情UserInfo userInfo = userInfoApi.getUserInfoById(userId);UserInfoVo vo = new UserInfoVo();BeanUtils.copyProperties(userInfo, vo);if (userInfo.getAge() != null) {vo.setAge(userInfo.getAge().toString());}return vo;
}

2.3 发送消息给客户端

目前已经完成了用户体系的对接工作,下面来测试一下,我们需要修改客户端的相关配置信息

image-20230101204313923

image-20230101204352927

然后在页面上发送一条消息

image-20230101204456206

image-20230101204504583

可以看到,客户端已经可以收到消息了。

2.4 老数据的处理

目前我们的系统中已经有了一些注册的老用户,但是他没还没有注册环信,我们编写一个单元测试方法批量的注册环信,并写入数据库。

注意:使用测试账号最多支持100个用户

@Test
public void register() {for (int i = 1; i < 106; i++) {User user = userApi.findById(Long.valueOf(i));if(user != null) {Boolean create = template.createUser("hx" + user.getId(), Constants.INIT_PASSWORD);if (create){user.setHxUser("hx" + user.getId());user.setHxPassword(Constants.INIT_PASSWORD);userApi.update(user);}}}
}

到此为止,推送通知已经实现,接下来我们要实现的就是两个用户之间进行通讯,在实现这个功能之前,我们需要先编写联系人管理和添加好友等相关功能模块。

3. 联系人管理

联系人管理包含了一下业务:

  • 当看到感兴趣的用户,可以点击进入用户详情页面查看陌生人问题
  • 用户回答陌生人问题,会给目标联系人一条推送消息,显示问题回答的答案。推送消息由服务端发送
  • 如果这个用户对这个答案满意,那么可以添加联系人为好友。
  • 成为好友后,可以在联系人列表中查看到该好友。
  • 点击该好友的头像即可开始聊天功能。

下面我们来逐一实现。

image-20230101211255629

3.1 查看用户详情

当用户对某一个用户该兴趣,那么可以点击进入用户详情页面。这个功能相对简单,只需要根据前端传递的用户id进行查询,返回对应的VO对象即可。

image-20230101205356695

具体的接口描述如下图所示:

image-20230101205440941

/*** 查询佳人详情** @param userId 用户id* @return*/
@GetMapping("/{id}/personalInfo")
public ResponseEntity getTodayBestById(@PathVariable(name = "id") Long userId) {TodayBest todayBest = this.tanhuaService.getTodayBestById(userId);return ResponseEntity.ok(todayBest);
}
public TodayBest getTodayBestById(Long userId) {// 1. 查询UserInfo表UserInfo userInfo = this.userInfoApi.getUserInfoById(userId);// 2. 查询RecommendUser表RecommendUser user = this.recommendUserApi.getRecommendUserByUserId(userId);// 记录访问记录Visitors visitors = new Visitors();visitors.setDate(System.currentTimeMillis());visitors.setVisitorUserId(UserHolder.getUserId());visitors.setUserId(userId);visitors.setVisitDate(new SimpleDateFormat("yyyyMMdd").format(new Date()));visitors.setScore(user.getScore());visitors.setFrom("圈子");this.visitorApi.save(visitors);// 3. 构建VOreturn TodayBest.init(userInfo, user);
}

3.2 查看陌生人问题

进入到用户主页后,点击聊一聊就可以查看陌生人问题。具体接口如下:

image-20230101205950303

/*** 获取陌生人问题* @param userId 用户id* @return*/
@GetMapping("/strangerQuestions")
public ResponseEntity strangerQuestions(Long userId) {String question = this.tanhuaService.getQuestionByUserId(userId);return ResponseEntity.ok(question);
}
public String getQuestionByUserId(Long userId) {return this.questionApi.getQuestionByUserId(userId);
}

3.3 回复陌生人问题

用户查询完陌生人问题后,可以进行回复,回复完成后系统会向该用户发送一条通知。接口如下:

image-20230101210130813

/*** 回复陌生人问题* @param map* @return*/
@PostMapping("/strangerQuestions")
public ResponseEntity replyQuestion(@RequestBody Map map) {Long userId = Long.parseLong(map.get("userId").toString());String reply = map.get("reply").toString();this.tanhuaService.replyQuestion(userId, reply);return ResponseEntity.ok(null);
}
public void replyQuestion(Long userId, String reply) {// 1. 发送信息包含 当前用户id 当前用户环信id,昵称,问题和回答Long currentUserId = UserHolder.getUserId();String currentHxId = HX_USER_PREFIX + currentUserId;String nickName = this.userInfoApi.getUserInfoById(currentUserId).getNickname();String question = this.questionApi.getQuestionByUserId(userId);Map map = new HashMap();map.put("userId", currentUserId);map.put("huanXinId", currentHxId);map.put("nickname", nickName);map.put("strangerQuestion", question);map.put("reply", reply);String msg = JSON.toJSONString(map);// 2. 调用template发送消息Boolean flag = this.huanXinTemplate.sendMsg(HX_USER_PREFIX + userId, msg);if (!flag) {throw new BusinessException(ErrorResult.error());}
}

3.4 添加联系人

用户获取到陌生人问题的回答后,如果感兴趣,则可以加好友。具体要完成两项工作

  • 将好友信息保存到MongoDB的好友表中
  • 将好友关系注册到环信

具体的接口如下:

image-20230101210358935

/*** 添加好友** @param userId 申请人的用户id* @return*/
@PostMapping("/contacts")
public ResponseEntity contacts(Long userId) {this.messageService.contacts(userId);return ResponseEntity.ok(null);
}
public void contacts(Long userId) {// 1. 保存好友信息到环信this.huanXinTemplate.addContact(Constants.HX_USER_PREFIX + userId, Constants.HX_USER_PREFIX + UserHolder.getUserId());// 2. 保存到MongoDBthis.friendApi.save(UserHolder.getUserId(), userId);
}
@Override
public void save(Long userId, Long userId1) {saveFriend(userId, userId1);saveFriend(userId1, userId);
}
private void saveFriend(Long userId, Long userId1) {Query query = new Query(Criteria.where("userId").is(userId).and("friendId").is(userId1));boolean flag = this.mongoTemplate.exists(query, Friend.class);if (!flag) {Friend friend = new Friend();friend.setUserId(userId);friend.setFriendId(userId1);friend.setCreated(System.currentTimeMillis());this.mongoTemplate.save(friend);}
}

注意在添加用户关系的需要判断关系是否已经存在,如果已经存在则不需要重复添加。此外在数据库中保存好友关系的时候需要保存两份,是双向好友关系

3.5 查询联系人列表

用户可以查询所有好友的联系人列表,这个功能比较简单,只是从MongoDB中查询到好友的用户id,然后到数据库中查询用户详情表即可。具体接口如下:

image-20230101211431197

为了返回数据,这里定义一个VO类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ContactVo implements Serializable {private Long id;private String userId;private String avatar;private String nickname;private String gender;private Integer age;private String city;public static ContactVo init(UserInfo userInfo) {ContactVo vo = new ContactVo();if(userInfo != null) {BeanUtils.copyProperties(userInfo,vo);vo.setUserId("hx"+userInfo.getId().toString());}return vo;}
}
/*** 获取联系人列表** @param page     页号* @param pagesize 页大小* @param keyword  关键字* @return*/
@GetMapping("/contacts")
public ResponseEntity getContactList(@RequestParam(defaultValue = "1") Integer page,@RequestParam(defaultValue = "10") Integer pagesize,String keyword) {PageResult pageResult = this.messageService.getContactList(page, pagesize, keyword);return ResponseEntity.ok(pageResult);
}
public PageResult getContactList(Integer page, Integer pagesize, String keyword) {// 1. 获取当前用户idLong userId = UserHolder.getUserId();// 2. 根据用户id在friend表中查询出所有的好友idList<Friend> friendList = this.friendApi.getFriendList(userId);List<Long> ids = CollUtil.getFieldValues(friendList, "friendId", Long.class);// 3. 根据好友id,查询出所有好友的用户详情Map<Long, UserInfo> userInfoByIds = this.userInfoApi.getUserInfoByIds(ids, null);// 4. 封装VOList<ContactVo> voList = new ArrayList<>();for (Friend friend : friendList) {Long friendId = friend.getFriendId();UserInfo userInfo = userInfoByIds.get(friendId);if (userInfo != null) {voList.add(ContactVo.init(userInfo));}}// 5. 封装实现类return new PageResult(page, pagesize, 0, voList);
}

3.6 用户之间的实时通讯

当完成了上述的获取联系人列表后,用户点击某一个好友,就可以开始聊天了。此时的数据通讯是客户端和环信服务器之间的通讯,和探花交友服务器已经没有关系了。到此为止,即时通讯模块的所有功能均已实现。

image-20230101212153020

image-20230101212220576

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/40704.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

花房集团成功上市,构建互联互通的在线社交娱乐生态

近日&#xff0c;花房集团正式在香港联合交易所主板上市。对于花房集团而言&#xff0c;登陆港股市场是新的征程、新的起点。 可以看到&#xff0c;花房集团在稳固“直播社交”业务基本盘的同时&#xff0c;持续探索元宇宙领域&#xff0c;上市当日首次发布了最新的战略方向“娱…

软件/APP/产品投放渠道分类大全

随着行业马太效应的加深&#xff0c;APP流量的获取愈发艰难&#xff0c;酒香也怕巷子深&#xff0c;再好的产品&#xff0c;没有优质的推广渠道加持也很难实现有效传播。 因此准备用一个系列专题&#xff0c;汇总整理APP传播的渠道、投放策略和数据分析疑难杂症等实用经验&…

腾讯应用宝米大师直购模式支付流程以及服务端php回调校验

一. 米大师简介 1.米大师&#xff08;Midas&#xff09;为腾讯官方唯一虚拟支付平台。 2.YSDK已经内置了米大师&#xff08;Midas&#xff09;支付模块&#xff0c;游戏开发者接入YSDK后&#xff0c;可以通过相应的配置开启米大师的支付功能&#xff0c;并调用YSDK已经封装好的…

免费好用的划词搜题神器插件

年初疫情爆发至今&#xff0c;可能不少家长头疼坏了-因为小孩的教育突然成了家长们巨大的烦恼&#xff0c;面对漫天飞的试题和教辅&#xff0c;不知道多少家长疲于应付&#xff0c;今天分享一个可以在浏览器中进行划词搜题的插件&#xff0c;不光可以一定程度上缓解家长们的教育…

网页搜题插件使用

安装划词搜题 根据自己的浏览器类型查看安装指南 一、Chrome浏览器(谷歌浏览器) 1、下载划词搜题安装文件 Chrome版划词搜题扩展下载地址2、解压安装文件 3、Chrome浏览器地址栏打开 chrome://extensions/ ,然后打开开发者模式,点击加载已解压的扩展程序,选择上一步中解压…

纬衡多个用户荣获“第五届建筑创作奖”

2008年12月&#xff0c;“第五届建筑创作奖”正式产生。 “第五届建筑创作奖”评奖办公室共收到全国18个省、市、自治区67个建筑设计研究单位申报的建筑创作项目248个。经过严格遵照公开、公正和公平的评选和无记名的投票&#xff0c;最后确定北京市建筑设计研究院之“国家大剧…

主要股东近3年净买入排名

主要股东在此区间内的净买入排名&#xff1a; 到 2019-08-08。 主要股东是指&#xff1a;公司管理层、机构和重要的个人股东。 来源&#xff1a;主要股东近3年净买入排名 1 - 中国建筑(SH601668) - 净买入&#xff1a;172.33亿 - 占A股流通市值比&#xff1a;7.493% - 市盈率…

“1024”讲话两个月后,深交所正式发布区块链50指数,板块再迎利好

“业务领域涉及区块链产业上中下游的公司为选样空间&#xff0c;按近半年日均总市值从高到低排序&#xff0c;筛选排名前50名的股票构成样本股。” 本文来源&#xff1a;巴比特&#xff1b;作者&#xff1a;邱祥宇&#xff1b;该内容旨在传递更多市场信息&#xff0c;不构成任何…

【12月19日】传媒行业的股票排名

便宜指数为PE、PB、股息和ROE四因子等权相加的值&#xff0c;越大代表越低估。 来源&#xff1a;传媒行业的股票排名 中原传媒(SZ000719)&#xff0c; 便宜指数&#xff1a; 7.21&#xff0c; 扣非市盈率(PE)&#xff1a;9.9&#xff0c; 股息&#xff1a; 3.46%&#xff0c; …

首次!深交所发布区块链50指数,成分股总市值1.3万亿

作者&#xff1a;邱祥宇 12月24日&#xff0c;深圳证券交易所和深圳证券信息有限公司发布深证区块链50指数&#xff08;指数代码&#xff1a;399286&#xff09;&#xff0c;以反映深圳证券市场区块链产业相关公司的表现和丰富指数化投资工具。指数基点为1000点&#xff0c;24日…

正好股票开户核污染主概念简直通杀

周三两市成交6440亿&#xff0c;比昨日又少了200多亿&#xff0c;指数弱反弹收涨0.6%。盘前券商给予中国中免买入评级&#xff0c;竟然真被拉出大阳&#xff0c;白酒企稳&#xff0c;给了指数支撑。破位下行&#xff0c;一个研报就吹起来&#xff0c;明日预期不太达观。 短线方…

特斯拉要对车内数据服务收费了:年费100美元;百度14.43亿投资东软,又在长沙投入45辆自动驾驶出租车;途歌倒闭消失了......

整理&#xff1a; 数据猿 Toby 数据猿官网 | www.datayuan.cn 今日头条丨一点资讯丨腾讯丨搜狐丨网易丨凤凰丨阿里UC大鱼丨新浪微博丨新浪看点丨百度百家丨博客中国丨趣头条丨腾讯云云社区 【目录精要】贵州将投资200亿元加快推进5G建设&#xff1b;数梦工场完成6亿元B轮融资&a…

陀螺产业区块链第九季 | 区块链+海关关务生态链

2020年4月&#xff0c;国家发改委在例行新闻发布会上宣布区块链被正式列为新型基础设施中的信息基础设施&#xff0c;自此区块链正式搭上新基建的“风口”。 从2020年到2021年&#xff0c;我国的区块链政策环境、基础设施建设、行业标准规范、技术研发实力、产业生态构建、行业…

天载正规股票数字货币逆势领涨

到午盘&#xff0c;上证指数跌1.18%&#xff0c;报3376.30点&#xff0c;深证成指跌1.38%&#xff0c;报13547.90点&#xff0c;创业板指跌1.39%&#xff0c;报2765.32点。 指数早盘震动走低&#xff0c;团体跌超1%&#xff0c;二胎概念开盘逆势领涨&#xff0c;数字钱银、白酒…

从腾讯的频繁增持看“游戏之王”的繁华和危情

近期&#xff0c;游戏圈真是热闹非凡。 特别是腾讯这个老牌游戏厂商&#xff0c;市场热度似乎从未消减。 近日&#xff0c;腾讯的操作可谓是让群众吃饱了瓜&#xff0c;原由竟是腾讯律师口出狂言&#xff1a;“玩家不是我们的重心”&#xff0c;一时间“腾讯与DD373”事件冲上…

百舸争流,盘点我国主流区块链企业联盟链技术与应用

文/尹宁 出品/陀螺研究院 区块链&#xff08;blockchain&#xff09;是一种由多方共同维护&#xff0c;使用密码学保证传输和访问安全&#xff0c;能够实现数据一致存储、难以篡改、防止抵赖的记账技术。2020年10月15号&#xff0c;美国白宫发布《关键与新兴技术国家战略》&…

申宝策略-元宇宙板块拉升

上午各大指数震荡反弹&#xff0c;创业板指数领涨。盘面上&#xff0c;云游戏、传媒、元宇宙、知识产权保护、虚拟现实等板块涨幅前列&#xff0c;盐湖提锂、物流、港口航运、煤炭等板块跌幅居前。 板块上&#xff0c;游戏板块大幅走强&#xff0c;新国脉、凯撒文化、惠程科技、…

申宝公司-A股开门红落空

2022年A股开门红落空&#xff0c;今日三大指数高开后震荡下挫&#xff0c;其中深成指跌超1%&#xff0c;创业板指重挫逾2%&#xff0c;午前市场小幅出现反弹&#xff1b;午后A股进一步回暖后再度走弱&#xff0c;三大指数集体收跌。沪深连续3个交易日破万亿&#xff1b;北向资金…

烧脑语言Brainfuck及其加减乘除

Brainfuck&#xff0c;文如其名&#xff0c;为“烧脑”而生&#xff0c;简称BF。 BF由Urban Mller于1993年创建&#xff0c;是满足图灵完备性(Turing complete)的一种极小化的计算机语言。 小到什么程度&#xff1f; BF一共只有8种符号 <>-.,[] &#xff0c;编译器也只有…

前端面试题

前端面试题 目录 前端面试题React相关1.说说React生命周期中有哪些坑&#xff1f;为什么要溢出will相关生命周期2. 说说Real diff算法是怎么运作的&#xff0c;从tree层到component层到element层分别讲解&#xff1f;3.调和阶段setState干了什么4.css3的新特性都有哪些5.说说re…