20.环信即时通信

环信即时通信

​ 即时通信,简称IM(InstantMessaging),是指能够即时发送和接收互联网消息等的业务。

市场上有大量的即时通信产品,如:QQ,微信等

​ 环信即时通讯云为开发者提供基于移动互联网的即时通讯能力,如单聊、群聊、发语音、发图片、发位置、实时音频、实时视频等,让开发者摆脱繁重的移动IM通讯底层开发,24小时即可让App拥有稳定健壮的内置IM能力

官网:https://www.easemob.com/

一、工作流程

APP端

  1. 在APP端与服务端,都需要完成与环信的集成。
  2. 在APP端,通过服务端查询用户在环信的账号密码。
  3. APP端,登录环信,进行好友聊天

服务端

  1. 将用户信息同步注册到环信
  2. 将用户好友信息同步到环信

二、基础api

初始化

 EMProperties emProperties = EMProperties.builder()
                .setAppkey("yourAppkey")
                .setClientId("yourClientId")
                .setClientSecret("yourClientSecret")
                .build();
    EMService  service = new EMService(emProperties);

api

     //创建环信用户
   service.user().create("user01", "123456").block();

    //添加好友 
    service.contact().add("user01","user02").block();
    //删除好友
   service.contact().remove("user01","user02").block();
//服务端发送消息
         Set<String> set = CollUtil.newHashSet("123");
   service.message().send("user01","users",
                       set,
                       new EMTextMessage().text("java"),null).block();

三、项目抽取组件

1.先在tanhua-app-server的application.yml配置环信信息

tanhua:
  huanxin: #环信的即时通信(在线聊天)api配置信息
    appkey: yourAppkey
    clientId: yourClientId
    clientSecret: yourClientSecret

2.在tanhua-autoconfig创建与配置信息对应的配置类

package com.tanhua.autoconfig.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * 读取tanhua-app-server application.yml配置文件的属性类
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "tanhua.huanxin")
public class HuanXinProperties {

    private String  appkey;
    private String clientId;
    private String  clientSecret;
}

3. 编写HuanXinTemplate


package com.tanhua.autoconfig.template;

import cn.hutool.core.collection.CollUtil;
import com.easemob.im.server.EMProperties;
import com.easemob.im.server.EMService;
import com.easemob.im.server.model.EMTextMessage;
import com.tanhua.autoconfig.properties.HuanXinProperties;
import lombok.extern.slf4j.Slf4j;

import java.util.Set;

/**
* 即时通信(模板)
*/
@Slf4j
public class HuanXinTemplate {

 private EMService emService;


  //通过构造方法把emProperties配置信息注入
  public HuanXinTemplate(HuanXinProperties huanXinProperties) {
      EMProperties emPro  = EMProperties.builder()
              .setAppkey(huanXinProperties.getAppkey())
              .setClientId(huanXinProperties.getClientId())
              .setClientSecret(huanXinProperties.getClientSecret())
              .build();

      emService = new EMService(emPro);

  }


  //创建环信用户
  public Boolean createUser(String username,String password) {
      try {
          //创建环信用户
          emService.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 {
          //创建环信用户
          emService.contact().add(username1,username2)
                  .block();
          return true;
      }catch (Exception e) {
          log.error("添加联系人失败~");
      }
      return false;
  }



  //删除联系人
  public Boolean deleteContact(String username1,String username2) {
      try {
          //创建环信用户
          emService.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是管理员发送
          emService.message().send("admin","users",
                  set,message,null).block();
          return true;
      }catch (Exception e) {
          log.error("删除联系人失败~");
      }
      return false;
  }


}

4.在启动类中把HuanXinTemplate对象注入到spring容器中管理

package com.tanhua.autoconfig;

import com.tanhua.autoconfig.properties.AipFaceProperties;
import com.tanhua.autoconfig.properties.HuanXinProperties;
import com.tanhua.autoconfig.properties.OssProperties;
import com.tanhua.autoconfig.properties.SmsProperties;
import com.tanhua.autoconfig.template.AipFaceTemplate;
import com.tanhua.autoconfig.template.HuanXinTemplate;
import com.tanhua.autoconfig.template.OssTemplate;
import com.tanhua.autoconfig.template.SmsTemplate;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

/**
 * 我愿称它为配置类
 */

@EnableConfigurationProperties({
                       HuanXinProperties.class
                            })
public class TanhuaAutoConfiguration {

    /**创建HuanXinTemplate对象交给Spring容器管理
     *
     * @param huanXinProperties
     * @return
     */
    @Bean
    public HuanXinTemplate emTemplate(HuanXinProperties huanXinProperties){
       return new HuanXinTemplate(huanXinProperties);
    }
}

5.测试

package com.itheima.test;

import com.tanhua.autoconfig.template.HuanXinTemplate;
import com.tanhua.server.AppServerApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * 环信测试
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = AppServerApplication.class)
public class ImApiTest {


    @Autowired
    private HuanXinTemplate huanXinTemplate;

    @Test
    public void testFindByMobile() {

        huanXinTemplate.createUser("user", "123");
    }
}

四、用户体系集成

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

  • 探花用户注册时需要将用户信息注册到环信系统中
    • 对于老数据:编写单元测试方法批量的注册到环信
    • 对于新用户:改造代码(用户注册的时候,自动注册到环信)
  • APP从服务端获取当前用户的环信用户密码,自动登入环信系统
    • 编写一个接口,获取当前用户在环信的用户名密码
  • APP自动获取环信服务器发送的信息数据

1.注册环信用户

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

登录的LoginControllerController

  /**
     * 用户登录
     * 1.请求路径:/user/loginVerification
     * 2.请求参数:phone(String),verificationCode(String)用map集合封装
     * 3.返回结果:token,isNew(封装到map集合返回)
     */
    @PostMapping("/loginVerification")
    public ResponseEntity loginVerification(@RequestBody Map map){
            //1.接收参数
            String phone = (String) map.get("phone");
            String code = (String) map.get("verificationCode");

            //2.调用userServi
            //返回结果用map封装
            Map result = userService.loginVerification(phone, code);

            return ResponseEntity.ok(result);
}

LoginService

/**
     * 登录的业务层接口业务逻辑
     * @param phone
     * @param code
     * @return
     */
    public Map loginVerification(String phone, String code) {
        //1.从Redis缓存中获取验证码
        String redisCode = redisTemplate.opsForValue().get("CHECK_CODE_" + phone);
        //2.判断用户输入的验证码与下发的手机验证码是否一致
        //不一致则抛出一个异常
        if(StringUtils.isEmpty(code) || ! redisCode.equals(code)){
            //验证码无效
            throw new BusinessException(ErrorResult.loginError());
        }
        //3.删除Redis中缓存的验证码
        redisTemplate.delete("CHECK_CODE_" + phone);

        //4.dubbo远程调用UserApi,根据手机号码查询用户
        User user = userApi.selectByPhoneNumber(phone);

        boolean isNew =  false;

        //5.如果user为空,证明是新用户,把它存进数据库表
        if(user == null){
            user =new User();
            user.setMobile(phone);
//            user.setCreated(new Date());
//            user.setUpdated(new Date());
            user.setPassword(DigestUtils.md5Hex("123456"));
            Long userId = userApi.addNewUser(user);
            user.setId(userId);
            isNew =true;


            // 用户登录注册成功,信息保存到数据库的同时,还要用户在环信注册账号

            //1.构造环信用户名
            String hx = "hx" +user.getId();
            //2.环信密码
            String passWord = Constants.INIT_PASSWORD;

            //3.注册环信用户
            Boolean result = huanXinTemplate.createUser(hx, passWord);
            //4.注册成功,更新User表
            if(result){
                user.setHxUser(hx);
                user.setHxPassword(passWord);
                userApi.update(user);
            }


        }
        //6.生成token(存入id和手机号码)
        Map userToken = new HashMap();
        userToken.put("id", user.getId());
        userToken.put("phone",phone);

        String token = JwtUtils.getToken(userToken);

        //7.构造返回值
        Map resultMap = new HashMap();

        resultMap.put("token", token);
        resultMap.put("isNew", isNew);

        return resultMap;
    }

UserApiImpl

用户注册到环信之后要更新user表中相应的字段


    //根据用户id修改用户信息
    public void update(User user) {
        userMapper.updateById(user);
    }

2.查询环信用户信息

用户在注册探花交友项目的账号时,同时我们的服务端为用户注册了环信账号
现在我们要查询用户环信账号的信息,返回到客户端;客户端会自动登录环信

1.HuanXinController


    /**
     *
     * 请求路径:/huanxin/user
     * 请求方式:get
     * 响应数据:HuanXinUserVo
     */
    @GetMapping("/user")
    public ResponseEntity queryHuanXinInfo(){

        HuanXinUserVo huanXinUserVo = huanXinService.queryHuanXinInfo();
        return ResponseEntity.ok(huanXinUserVo);
    }

2.HuanXinService

package com.tanhua.server.service;

import com.tanhua.dubbo.api.UserApi;
import com.tanhua.model.vo.HuanXinUserVo;
import com.tanhua.server.interceptor.ThreadLocalUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;

@Service
public class HuanXinService {


    @DubboReference
    private UserApi userApi;


    public HuanXinUserVo queryHuanXinInfo() {

        HuanXinUserVo huanXinUserVo = new HuanXinUserVo();
        huanXinUserVo.setUsername("hx"+ThreadLocalUtils.getUserId());
        huanXinUserVo.setPassword("123456");


        return huanXinUserVo;

    }
}

vo对象


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

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

3. 环信用户ID查询用户信息

在好友聊天时,完全基于环信服务器实现。为了更好的页面效果,需要展示出用户的基本信息,这是需要通过环信用户id查询用户。

MessagesController

package com.tanhua.server.controller;

import com.tanhua.model.vo.UserInfoVo;
import com.tanhua.server.service.MessagesService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/messages")
public class MessagesController {

    @Autowired
    private MessagesService messagesService;


    /**
     * 环信用户ID查询用户信息:
     * 请求路径: /messages/userinfo
     * 请求方式:get
     * 请求参数:String,huanxinId;环信id
     * 响应数据:UserInfoVo
     */
    @GetMapping("/userinfo")
    public ResponseEntity queryInfoByHuanXin(String huanxinId){

        UserInfoVo userInfoVo = messagesService.queryInfoByHuanXin(huanxinId);

        return ResponseEntity.ok(userInfoVo);
    }
}

MessagesController

package com.tanhua.server.service;

import com.tanhua.dubbo.api.UserApi;
import com.tanhua.dubbo.api.UserInfoApi;
import com.tanhua.model.domain.User;
import com.tanhua.model.domain.UserInfo;
import com.tanhua.model.vo.UserInfoVo;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

@Service
public class MessagesService {

    @DubboReference
    private UserApi userApi;

    @DubboReference
    private UserInfoApi userInfoApi;

    //环信用户ID查询用户信息:
    public UserInfoVo queryInfoByHuanXin(String huanxinId) {

        //1.先根据环信id查询用户
        User user =userApi.queryUserByHuanXin(huanxinId);
        //2.从用户中得到用户id,查询用户详情
        UserInfo userInfo = userInfoApi.selectUserInfo(user.getId());
        //3.构造vo返回

        UserInfoVo userInfoVo = new UserInfoVo();
        BeanUtils.copyProperties(userInfo, userInfoVo);
        if(userInfo.getAge() != null) {
            userInfoVo.setAge(userInfo.getAge().toString());
        }

        return userInfoVo;
    }
}

UserApiImpl

 //1.根据环信id查询用户
    public User queryUserByHuanXin(String huanxinId) {
        QueryWrapper<User> qw = new QueryWrapper<>();
        qw.eq("hx_user",huanxinId);
        User user = userMapper.selectOne(qw);
        return user;
    }
posted @ 2022-10-31 10:06  给我手牵你走  阅读(424)  评论(0编辑  收藏  举报