hgw 3 years ago
parent
commit
7608a05587

+ 17 - 0
cooleshow-user/user-biz/pom.xml

@@ -15,6 +15,8 @@
     <url>http://maven.apache.org</url>
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <rongcloud.im.version>3.2.17</rongcloud.im.version>
+        <redisson.version>3.16.4</redisson.version>
     </properties>
     <dependencies>
         <dependency>
@@ -39,6 +41,21 @@
             <version>2.6</version>
             <scope>compile</scope>
         </dependency>
+
+        <!-- 融云-->
+        <dependency>
+            <groupId>cn.rongcloud.im</groupId>
+            <artifactId>server-sdk-java</artifactId>
+            <version>${rongcloud.im.version}</version>
+        </dependency>
+
+        <!-- redisson -->
+        <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson-spring-boot-starter</artifactId>
+            <version>${redisson.version}</version>
+        </dependency>
+
     </dependencies>
 
     <build>

+ 48 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/config/IMProperties.java

@@ -0,0 +1,48 @@
+package com.yonge.cooleshow.biz.dal.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+@Component
+@ConfigurationProperties(prefix = "cn.rongcloud.im")
+public class IMProperties {
+    private String appKey;
+    private String secret;
+    private String host;
+    private String rtcHost;
+
+    public String getAppKey() {
+        return appKey;
+    }
+
+    public void setAppKey(String appKey) {
+        this.appKey = appKey;
+    }
+
+    public String getSecret() {
+        return secret;
+    }
+
+    public void setSecret(String secret) {
+        this.secret = secret;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public String getRtcHost() {
+        return rtcHost;
+    }
+
+    public void setRtcHost(String rtcHost) {
+        this.rtcHost = rtcHost;
+    }
+}

+ 13 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/BaseMessage.java

@@ -0,0 +1,13 @@
+package com.yonge.cooleshow.biz.dal.entity;
+
+import com.alibaba.fastjson.JSONObject;
+
+public abstract class BaseMessage {
+
+    public abstract String getObjectName();
+
+    @Override
+    public String toString() {
+        return JSONObject.toJSONString(this);
+    }
+}

+ 43 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/IMApiResultInfo.java

@@ -0,0 +1,43 @@
+package com.yonge.cooleshow.biz.dal.entity;
+
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+
+public class IMApiResultInfo {
+    // 返回码,200 为正常。
+    Integer code;
+    // 错误信息。
+    String errorMessage;
+    //人员是否存在 true 存在  false 不存在
+    Boolean isInChrm;
+
+    public boolean isSuccess() {
+        return code == 200;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public void setCode(Integer code) {
+        this.code = code;
+    }
+
+    public String getErrorMessage() {
+        return errorMessage;
+    }
+
+    public void setErrorMessage(String errorMessage) {
+        this.errorMessage = errorMessage;
+    }
+
+    public Boolean getInChrm() {
+        return isInChrm;
+    }
+
+    public void setInChrm(Boolean inChrm) {
+        isInChrm = inChrm;
+    }
+}

+ 52 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/IMTokenInfo.java

@@ -0,0 +1,52 @@
+package com.yonge.cooleshow.biz.dal.entity;
+
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+public class IMTokenInfo {
+    // 返回码,200 为正常.如果您正在使用开发环境的 AppKey,您的应用只能注册 100 名用户,达到上限后,将返回错误码 2007.如果您需要更多的测试账户数量,您需要在应用配置中申请“增加测试人数”。
+    Integer code;
+    // 用户 Token,可以保存应用内,长度在 256 字节以内.用户 Token,可以保存应用内,长度在 256 字节以内。
+    String token;
+    // 用户 Id,与输入的用户 Id 相同.用户 Id,与输入的用户 Id 相同。
+    String userId;
+    // 错误信息。
+    String errorMessage;
+
+    public boolean isSuccess() {
+        return code == 200;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public void setCode(Integer code) {
+        this.code = code;
+    }
+
+    public String getToken() {
+        return token;
+    }
+
+    public void setToken(String token) {
+        this.token = token;
+    }
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public String getErrorMessage() {
+        return errorMessage;
+    }
+
+    public void setErrorMessage(String errorMessage) {
+        this.errorMessage = errorMessage;
+    }
+}

+ 67 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ImRoomMessage.java

@@ -0,0 +1,67 @@
+package com.yonge.cooleshow.biz.dal.entity;
+
+/**
+ * @author hgw
+ * Created by 2022-02-25
+ */
+public class ImRoomMessage extends BaseMessage {
+
+    //objectName 类型-将所有人强制踢出房间
+    public static final String FORCED_OFFLINE = "RC:ForcedOffline";
+
+    //objectName 类型-观看者退出房间
+    public static final String LOOKER_LOGIN_OUT = "RC:LookerLoginOut";
+
+    /**
+     * 消息类型
+     */
+    private String objectName;
+
+    /**
+     * 消息内容
+     */
+    private Object content;
+
+    /**
+     * 发送者id
+     */
+    private String fromUserId;
+
+    /**
+     * 发送到的房间uid
+     */
+    private String toChatroomId;
+
+    @Override
+    public String getObjectName() {
+        return objectName;
+    }
+
+    public void setObjectName(String objectName) {
+        this.objectName = objectName;
+    }
+
+    public Object getContent() {
+        return content;
+    }
+
+    public void setContent(Object content) {
+        this.content = content;
+    }
+
+    public String getFromUserId() {
+        return fromUserId;
+    }
+
+    public void setFromUserId(String fromUserId) {
+        this.fromUserId = fromUserId;
+    }
+
+    public String getToChatroomId() {
+        return toChatroomId;
+    }
+
+    public void setToChatroomId(String toChatroomId) {
+        this.toChatroomId = toChatroomId;
+    }
+}

+ 279 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/support/HttpHelper.java

@@ -0,0 +1,279 @@
+package com.yonge.cooleshow.biz.dal.support;
+
+import com.yonge.cooleshow.biz.dal.config.IMProperties;
+import io.rong.util.CodeUtil;
+import io.rong.util.GsonUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.*;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.Map;
+
+@Component
+public class HttpHelper {
+    private final static Logger log = LoggerFactory.getLogger(HttpHelper.class);
+
+    private static final String APPKEY = "RC-App-Key";
+    private static final String NONCE = "RC-Nonce";
+    private static final String TIMESTAMP = "RC-Timestamp";
+    private static final String SIGNATURE = "RC-Signature";
+
+    private SSLContext sslCtx = null;
+
+    @Autowired
+    IMProperties imProperties;
+
+    @PostConstruct
+    private void init() {
+        log.info("init HttpHelper");
+        try {
+            sslCtx = SSLContext.getInstance("TLS");
+            X509TrustManager tm = new X509TrustManager() {
+                public void checkClientTrusted(X509Certificate[] xcs, String string) {
+                }
+
+                public void checkServerTrusted(X509Certificate[] xcs, String string) {
+                }
+
+                public X509Certificate[] getAcceptedIssuers() {
+                    return null;
+                }
+            };
+            sslCtx.init(null, new TrustManager[]{tm}, null);
+        } catch (Exception e) {
+            log.error("SSLContext exception:{}", e.getMessage());
+        }
+
+        HttpsURLConnection.setDefaultHostnameVerifier((arg0, arg1) -> true);
+
+        HttpsURLConnection.setDefaultSSLSocketFactory(sslCtx.getSocketFactory());
+    }
+
+    // 设置body体
+    public void setBodyParameter(StringBuilder sb, HttpURLConnection conn)
+            throws IOException {
+        String str = sb.toString();
+        log.info("Call server api with url: {}, data: {}", conn.getURL().toString(), str);
+        DataOutputStream out = new DataOutputStream(conn.getOutputStream());
+        out.writeBytes(str);
+        out.flush();
+        out.close();
+    }
+
+    public HttpURLConnection createGetHttpConnection(String uri)
+            throws MalformedURLException, IOException {
+        URL url = new URL(uri);
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        conn.setConnectTimeout(30000);
+        conn.setRequestMethod("GET");
+        return conn;
+    }
+
+    public void setBodyParameter(String str, HttpURLConnection conn) throws IOException {
+        log.info("Call IM server api with url: {}, data: {}", conn.getURL().toString(), str);
+        DataOutputStream out = new DataOutputStream(conn.getOutputStream());
+        out.write(str.getBytes(StandardCharsets.UTF_8));
+        out.flush();
+        out.close();
+    }
+
+    public HttpURLConnection createWhiteBoardPostHttpConnection(String host, String uri, String contentType)
+            throws MalformedURLException, IOException, ProtocolException {
+
+        URL url = new URL(host + uri);
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        setConn(conn, contentType);
+        return conn;
+    }
+
+    public HttpURLConnection createCommonPostHttpConnection(String host, String appKey,
+                                                            String appSecret, String uri, String contentType)
+            throws IOException {
+        HttpURLConnection conn = getHttpURLConnection(host, appKey, appSecret, uri, contentType);
+        if (conn == null) return null;
+
+        conn.setConnectTimeout(30000);
+        conn.setReadTimeout(30000);
+        conn.setRequestMethod("POST");
+        return conn;
+    }
+
+    public HttpURLConnection createCommonGetHttpConnection(String host, String appKey,
+                                                           String appSecret, String uri, String contentType) throws IOException {
+        HttpURLConnection conn = getHttpURLConnection(host, appKey, appSecret, uri, contentType);
+        if (conn == null) return null;
+
+        conn.setConnectTimeout(10000);
+        conn.setReadTimeout(10000);
+        conn.setRequestMethod("GET");
+        return conn;
+    }
+
+    private HttpURLConnection getHttpURLConnection(String host, String appKey, String appSecret, String uri, String contentType) throws IOException {
+        String nonce = String.valueOf(Math.random() * 1000000);
+        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
+        StringBuilder toSign = new StringBuilder(appSecret).append(nonce).append(timestamp);
+        String sign = CodeUtil.hexSHA1(toSign.toString());
+        uri = host + uri;
+        URL url = new URL(uri);
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        if (conn == null) {
+            log.info("open url connectin fail, url={}", uri);
+            return null;
+        }
+
+        conn.setUseCaches(false);
+        conn.setDoInput(true);
+        conn.setDoOutput(true);
+        conn.setInstanceFollowRedirects(true);
+        conn.setRequestProperty(APPKEY, appKey);
+        conn.setRequestProperty(NONCE, nonce);
+        conn.setRequestProperty(TIMESTAMP, timestamp);
+        conn.setRequestProperty(SIGNATURE, sign);
+        conn.setRequestProperty("Content-Type", contentType);
+        return conn;
+    }
+
+    public HttpURLConnection createIMGetHttpConnection(String uri, String contentType) throws IOException {
+        return createCommonGetHttpConnection(imProperties.getHost(),
+                imProperties.getAppKey(), imProperties.getSecret(), uri,
+                contentType);
+    }
+
+    public HttpURLConnection createIMPostHttpConnection(String uri, String contentType) throws IOException {
+        return createCommonPostHttpConnection(imProperties.getHost(),
+                imProperties.getAppKey(), imProperties.getSecret(), uri,
+                contentType);
+    }
+
+    public byte[] readInputStream(InputStream inStream) throws Exception {
+        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+        byte[] buffer = new byte[1024];
+        int len = 0;
+        while ((len = inStream.read(buffer)) != -1) {
+            outStream.write(buffer, 0, len);
+        }
+        byte[] data = outStream.toByteArray();
+        outStream.close();
+        inStream.close();
+        return data;
+    }
+
+    public String returnResult(HttpURLConnection conn) throws Exception {
+        InputStream input;
+        String result;
+        try {
+            if (conn.getResponseCode() == 200 || conn.getResponseCode() == 201) {
+                input = conn.getInputStream();
+            } else {
+                input = conn.getErrorStream();
+            }
+            result = new String(readInputStream(input), StandardCharsets.UTF_8);
+        } catch (UnknownHostException e) {
+            result = getExceptionMessage("request:" + conn.getURL() + " ,UnknownHostException:" + e.getMessage());
+        } catch (SocketTimeoutException e) {
+            result = getExceptionMessage("request:" + conn.getURL() + " ,SocketTimeoutException:" + e.getMessage());
+        } catch (IOException e) {
+            result = getExceptionMessage("request:" + conn.getURL() + " ,IOException:" + e.getMessage());
+        }
+        log.info("IM server api response:{}", result);
+        return result;
+    }
+
+    private static String getExceptionMessage(String error) {
+        Map<String, Object> result = new HashMap<>();
+        result.put("code", 400);
+        result.put("msg", error);
+        return GsonUtil.toJson(result);
+    }
+
+    public String returnResult(HttpURLConnection conn, String body) throws Exception, IOException {
+        InputStream input = null;
+        if (conn.getResponseCode() == 200) {
+            input = conn.getInputStream();
+        } else {
+            input = conn.getErrorStream();
+        }
+        String result = new String(readInputStream(input), "UTF-8");
+        log.info("IM server api response:{}, {}, {}", conn.getURL(), body, result);
+        return result;
+    }
+
+    public HttpURLConnection createPostHttpConnection(String uri, String contentType) throws IOException {
+        URL url = new URL(uri);
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        if (conn == null) {
+            log.info("open url connection fail, url={}", uri);
+            return null;
+        }
+        setConn(conn, contentType);
+        return conn;
+    }
+
+    private void setConn(HttpURLConnection conn, String contentType) throws ProtocolException {
+        conn.setUseCaches(false);
+        conn.setDoInput(true);
+        conn.setDoOutput(true);
+        conn.setRequestMethod("POST");
+        conn.setInstanceFollowRedirects(true);
+        conn.setConnectTimeout(30000);
+        conn.setReadTimeout(30000);
+        conn.setRequestProperty("Content-Type", contentType);
+    }
+
+    public HttpURLConnection createIMRtcPostHttpConnection(String uri, String contentType, String roomId)
+            throws IOException {
+        return createCommonRtcPostHttpConnection(imProperties.getRtcHost(),
+                imProperties.getAppKey(), imProperties.getSecret(), uri,
+                contentType, roomId);
+    }
+
+    public HttpURLConnection createCommonRtcPostHttpConnection(String host, String appKey,
+                                                               String appSecret, String uri, String contentType, String roomId)
+            throws IOException {
+        String nonce = String.valueOf(Math.random() * 1000000);
+        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
+        StringBuilder toSign = new StringBuilder(appSecret).append(nonce).append(timestamp);
+        String sign = CodeUtil.hexSHA1(toSign.toString());
+        uri = host + uri;
+        URL url = new URL(uri);
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        if (conn == null) {
+            log.info("open url connectin fail, url={}", uri);
+            return null;
+        }
+
+        conn.setUseCaches(false);
+        conn.setDoInput(true);
+        conn.setDoOutput(true);
+        conn.setRequestMethod("POST");
+        conn.setInstanceFollowRedirects(true);
+        conn.setConnectTimeout(30000);
+        conn.setReadTimeout(30000);
+
+        conn.setRequestProperty("App-Key", appKey);
+        conn.setRequestProperty("Nonce", nonce);
+        conn.setRequestProperty("Timestamp", timestamp);
+        conn.setRequestProperty("Signature", sign);
+        if (StringUtils.isNotEmpty(roomId)) {
+            conn.setRequestProperty("Room-Id", roomId);
+        }
+        conn.setRequestProperty("Content-Type", contentType);
+        return conn;
+    }
+}

+ 469 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/support/IMHelper.java

@@ -0,0 +1,469 @@
+package com.yonge.cooleshow.biz.dal.support;
+
+import com.alibaba.fastjson.JSON;
+import com.yonge.cooleshow.biz.dal.entity.BaseMessage;
+import com.yonge.cooleshow.biz.dal.entity.IMApiResultInfo;
+import com.yonge.cooleshow.biz.dal.entity.IMTokenInfo;
+import io.rong.util.GsonUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+
+import java.net.HttpURLConnection;
+import java.net.URLEncoder;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+@Component
+public class IMHelper {
+
+    private final static Logger log = LoggerFactory.getLogger(IMHelper.class);
+
+    private static final String UTF8 = "UTF-8";
+
+    @Autowired
+    HttpHelper httpHelper;
+
+    /**
+     * 获取 Token 方法
+     *
+     * @param userId:用户        Id,最大长度 64 字节.是用户在 App 中的唯一标识码,必须保证在同一个 App 内不重复,重复的用户 Id 将被当作是同一用户。(必传)
+     * @param name:用户名称,最大长度   128 字节.用来在 Push 推送时显示用户的名称.用户名称,最大长度 128 字节.用来在 Push 推送时显示用户的名称。(必传)
+     * @param portraitUri:用户头像 URI,最大长度 1024 字节.用来在 Push 推送时显示用户的头像。(必传)
+     * @return TokenResult
+     **/
+    public IMTokenInfo getToken(String userId, String name, String portraitUri) throws Exception {
+        if (userId == null) {
+            throw new IllegalArgumentException("Paramer 'userId' is required");
+        }
+
+        if (name == null) {
+            throw new IllegalArgumentException("Paramer 'name' is required");
+        }
+
+        if (portraitUri == null) {
+            throw new IllegalArgumentException("Paramer 'portraitUri' is required");
+        }
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("&userId=").append(URLEncoder.encode(userId, UTF8));
+        sb.append("&name=").append(URLEncoder.encode(name, UTF8));
+        sb.append("&portraitUri=").append(URLEncoder.encode(portraitUri, UTF8));
+        String body = sb.toString();
+        if (body.indexOf("&") == 0) {
+            body = body.substring(1);
+        }
+
+        HttpURLConnection conn = httpHelper.createIMPostHttpConnection("/user/getToken.json", "application/x-www-form-urlencoded");
+        httpHelper.setBodyParameter(body, conn);
+
+        return JSON.parseObject(httpHelper.returnResult(conn, body), IMTokenInfo.class);
+    }
+
+    /**
+     * 创建群组方法(创建群组,并将用户加入该群组,用户将可以收到该群的消息,同一用户最多可加入 500 个群,每个群最大至 3000 人,App
+     * 内的群组数量没有限制.注:其实本方法是加入群组方法 /group/join 的别名。)
+     *
+     * @param userId:要加入群的用户 Id。(必传)
+     * @param groupId:创建群组   Id。(必传)
+     * @param groupName:群组   Id 对应的名称。(必传)
+     * @return CodeSuccessResult
+     **/
+    public IMApiResultInfo createGroup(String[] userId, String groupId, String groupName)
+            throws Exception {
+        if (userId == null) {
+            throw new IllegalArgumentException("Paramer 'userId' is required");
+        }
+
+        if (groupId == null) {
+            throw new IllegalArgumentException("Paramer 'groupId' is required");
+        }
+
+        if (groupName == null) {
+            throw new IllegalArgumentException("Paramer 'groupName' is required");
+        }
+
+        StringBuilder sb = new StringBuilder();
+
+        for (int i = 0; i < userId.length; i++) {
+            String child = userId[i];
+            sb.append("&userId=").append(URLEncoder.encode(child, UTF8));
+        }
+
+        sb.append("&groupId=").append(URLEncoder.encode(groupId, UTF8));
+        sb.append("&groupName=").append(URLEncoder.encode(groupName, UTF8));
+        String body = sb.toString();
+        if (body.indexOf("&") == 0) {
+            body = body.substring(1);
+        }
+
+        HttpURLConnection conn = httpHelper
+                .createIMPostHttpConnection("/group/create.json", "application/x-www-form-urlencoded");
+        httpHelper.setBodyParameter(body, conn);
+
+        return JSON.parseObject(httpHelper.returnResult(conn, body), IMApiResultInfo.class);
+    }
+
+
+    /**
+     * 将用户加入指定群组,用户将可以收到该群的消息,同一用户最多可加入 500 个群,每个群最大至 3000 人。
+     *
+     * @param userId:要加入群的用户  Id,可提交多个,最多不超过 1000 个。(必传)
+     * @param groupId:要加入的群   Id。(必传)
+     * @param groupName:要加入的群 Id 对应的名称。(必传)
+     * @return CodeSuccessResult
+     **/
+    @Async
+    public IMApiResultInfo joinGroup(String[] userId, String groupId, String groupName)
+            throws Exception {
+        if (userId == null) {
+            throw new IllegalArgumentException("Paramer 'userId' is required");
+        }
+
+        if (groupId == null) {
+            throw new IllegalArgumentException("Paramer 'groupId' is required");
+        }
+
+        if (groupName == null) {
+            throw new IllegalArgumentException("Paramer 'groupName' is required");
+        }
+
+        StringBuilder sb = new StringBuilder();
+
+        for (int i = 0; i < userId.length; i++) {
+            String child = userId[i];
+            sb.append("&userId=").append(URLEncoder.encode(child, UTF8));
+        }
+
+        sb.append("&groupId=").append(URLEncoder.encode(groupId, UTF8));
+        sb.append("&groupName=").append(URLEncoder.encode(groupName, UTF8));
+        String body = sb.toString();
+        if (body.indexOf("&") == 0) {
+            body = body.substring(1);
+        }
+
+        HttpURLConnection conn = httpHelper
+                .createIMPostHttpConnection("/group/join.json", "application/x-www-form-urlencoded");
+        httpHelper.setBodyParameter(body, conn);
+
+        return JSON.parseObject(httpHelper.returnResult(conn, body), IMApiResultInfo.class);
+    }
+
+
+    /**
+     * 退出群组方法(将用户从群中移除,不再接收该群组的消息.)
+     *
+     * @param userId:要退出群的用户 Id.(必传)
+     * @param groupId:要退出的群  Id.(必传)
+     * @return CodeSuccessResult
+     **/
+    public IMApiResultInfo quit(String[] userId, String groupId) throws Exception {
+        if (userId == null) {
+            throw new IllegalArgumentException("Paramer 'userId' is required");
+        }
+
+        if (groupId == null) {
+            throw new IllegalArgumentException("Paramer 'groupId' is required");
+        }
+
+        StringBuilder sb = new StringBuilder();
+
+        for (int i = 0; i < userId.length; i++) {
+            String child = userId[i];
+            sb.append("&userId=").append(URLEncoder.encode(child, UTF8));
+        }
+
+        sb.append("&groupId=").append(URLEncoder.encode(groupId, UTF8));
+        String body = sb.toString();
+        if (body.indexOf("&") == 0) {
+            body = body.substring(1);
+        }
+
+        HttpURLConnection conn = httpHelper
+                .createIMPostHttpConnection("/group/quit.json", "application/x-www-form-urlencoded");
+        httpHelper.setBodyParameter(body, conn);
+
+        return JSON.parseObject(httpHelper.returnResult(conn, body), IMApiResultInfo.class);
+    }
+
+
+    /**
+     * 解散群组方法。(将该群解散,所有用户都无法再接收该群的消息。)
+     *
+     * @param userId:操作解散群的用户 Id。(必传)
+     * @param groupId:要解散的群   Id。(必传)
+     * @return CodeSuccessResult
+     **/
+    public IMApiResultInfo dismiss(String userId, String groupId) throws Exception {
+        if (userId == null) {
+            throw new IllegalArgumentException("Paramer 'userId' is required");
+        }
+
+        if (groupId == null) {
+            throw new IllegalArgumentException("Paramer 'groupId' is required");
+        }
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("&userId=").append(URLEncoder.encode(userId, UTF8));
+        sb.append("&groupId=").append(URLEncoder.encode(groupId, UTF8));
+        String body = sb.toString();
+        if (body.indexOf("&") == 0) {
+            body = body.substring(1, body.length());
+        }
+
+        HttpURLConnection conn = httpHelper
+                .createIMPostHttpConnection("/group/dismiss.json", "application/x-www-form-urlencoded");
+        httpHelper.setBodyParameter(body, conn);
+
+        return JSON.parseObject(httpHelper.returnResult(conn, body), IMApiResultInfo.class);
+    }
+
+
+    /**
+     * 发送群组消息方法(以一个用户身份向群组发送消息,单条消息最大 128k.每秒钟最多发送 20 条消息,每次最多向 3 个群组发送,如:一次向 3 个群组发送消息,示为 3 条消息。)
+     *
+     * @param fromUserId:发送人用户                         Id 。(必传)
+     * @param toGroupId:接收群Id,提供多个本参数可以实现向多群发送消息,最多不超过 3 个群组。(必传)
+     * @return CodeSuccessResult
+     **/
+    public IMApiResultInfo publishMessage(String fromUserId, String toGroupId, BaseMessage message) throws Exception {
+        String[] toGroupIds = new String[1];
+        toGroupIds[0] = toGroupId;
+        return publishMessage(fromUserId, null, toGroupIds, message, "", "", 0,
+                0, 0, 0, 0);
+    }
+
+    public IMApiResultInfo publishMessage(String fromUserId, String toGroupId, BaseMessage message, Integer isIncludeSender) throws Exception {
+        String[] toGroupIds = new String[1];
+        toGroupIds[0] = toGroupId;
+        return publishMessage(fromUserId, null, toGroupIds, message, "", "", 0,
+                0, isIncludeSender, 0, 0);
+    }
+
+    //定向消息 toUserId 指向对应的人
+    public IMApiResultInfo publishMessage(String fromUserId, String toUserId, String toGroupId, BaseMessage message) throws Exception {
+        String[] toGroupIds = new String[1];
+        toGroupIds[0] = toGroupId;
+        return publishMessage(fromUserId, toUserId, toGroupIds, message, "", "", 0,
+                0, 0, 0, 0);
+    }
+
+    public IMApiResultInfo publishMessage(String fromUserId, String toUserId, String[] toGroupId,
+                                          BaseMessage message, String pushContent, String pushData, Integer isPersisted,
+                                          Integer isCounted, Integer isIncludeSender, Integer isStatus, Integer isMentioned)
+            throws Exception {
+        if (fromUserId == null) {
+            throw new IllegalArgumentException("Paramer 'fromUserId' is required");
+        }
+
+        if (toGroupId == null) {
+            throw new IllegalArgumentException("Paramer 'toGroupId' is required");
+        }
+
+        if (message == null) {
+            throw new IllegalArgumentException("Paramer 'message' is required");
+        }
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("&fromUserId=").append(URLEncoder.encode(fromUserId, UTF8));
+
+        //定向消息
+        if (toUserId != null) {
+            sb.append("&toUserId=").append(URLEncoder.encode(toUserId, UTF8));
+        }
+
+        for (int i = 0; i < toGroupId.length; i++) {
+            String child = toGroupId[i];
+            sb.append("&toGroupId=").append(URLEncoder.encode(child, UTF8));
+        }
+
+        String msgStr = message.toString();
+        log.info("publish msg: {}", msgStr);
+        sb.append("&objectName=").append(URLEncoder.encode(message.getObjectName(), UTF8));
+        sb.append("&content=").append(URLEncoder.encode(msgStr, UTF8));
+
+        if (pushContent != null) {
+            sb.append("&pushContent=").append(URLEncoder.encode(pushContent, UTF8));
+        }
+
+        if (pushData != null) {
+            sb.append("&pushData=").append(URLEncoder.encode(pushData, UTF8));
+        }
+
+        if (isPersisted != null) {
+            sb.append("&isPersisted=").append(URLEncoder.encode(isPersisted.toString(), UTF8));
+        }
+
+        if (isCounted != null) {
+            sb.append("&isCounted=").append(URLEncoder.encode(isCounted.toString(), UTF8));
+        }
+
+        if (isIncludeSender != null) {
+            sb.append("&isIncludeSender=")
+                    .append(URLEncoder.encode(isIncludeSender.toString(), UTF8));
+        }
+
+        if (isMentioned != null) {
+            sb.append("&isMentioned=").append(URLEncoder.encode(isMentioned.toString(), UTF8));
+        }
+
+        String body = sb.toString();
+        if (body.indexOf("&") == 0) {
+            body = body.substring(1, body.length());
+        }
+
+        String url;
+        if (isStatus != null && isStatus.intValue() == 1) {
+            url = "/statusmessage/group/publish.json";
+        } else {
+            url = "/message/group/publish.json";
+        }
+
+        HttpURLConnection conn = httpHelper
+                .createIMPostHttpConnection(url, "application/x-www-form-urlencoded");
+        httpHelper.setBodyParameter(body, conn);
+
+        return JSON.parseObject(httpHelper.returnResult(conn, body), IMApiResultInfo.class);
+    }
+
+    /**
+     * 创建聊天室
+     *
+     * @param chatRoomId:   要创建的聊天室 Id,长度不超过 64 字节
+     * @param chatRoomName: 聊天室的名称,每次可创建多个聊天室。
+     * @return IMApiResultInfo
+     * @throws Exception
+     */
+    public IMApiResultInfo createChatRoom(String chatRoomId, String chatRoomName) throws Exception {
+        if (chatRoomId == null) {
+            throw new RuntimeException("房间Uid不能为空");
+        }
+
+        if (chatRoomName == null) {
+            throw new RuntimeException("房间名称不能为空");
+        }
+
+        StringBuilder sb = new StringBuilder();
+        chatRoomId = "[" + chatRoomId + "]";
+        sb.append("&chatroom").append(URLEncoder.encode(chatRoomId, UTF8));
+        sb.append("=");
+        sb.append(URLEncoder.encode(chatRoomName, UTF8));
+        String body = sb.toString();
+        if (body.indexOf("&") == 0) {
+            body = body.substring(1);
+        }
+
+        HttpURLConnection conn = httpHelper
+                .createIMPostHttpConnection("/chatroom/create.json", "application/x-www-form-urlencoded");
+        httpHelper.setBodyParameter(body, conn);
+
+        return (IMApiResultInfo) GsonUtil.fromJson(httpHelper.returnResult(conn), IMApiResultInfo.class);
+    }
+
+
+    /**
+     * 销毁聊天室
+     *
+     * @param chatroomIds 聊天室 ID 列表(必传)
+     * @return
+     */
+    public IMApiResultInfo deleteChrm(List<String> chatroomIds)
+            throws Exception {
+        if (chatroomIds == null) {
+            throw new RuntimeException("房间Uid不能为空");
+        }
+        StringBuilder sb = new StringBuilder();
+        for (String child : chatroomIds) {
+            sb.append("&chatroomId=").append(URLEncoder.encode(child, UTF8));
+        }
+        String body = sb.toString();
+        if (body.indexOf("&") == 0) {
+            body = body.substring(1);
+        }
+        HttpURLConnection conn = httpHelper.createIMPostHttpConnection("/chatroom/destroy.json", "application/x-www-form-urlencoded");
+        httpHelper.setBodyParameter(body, conn);
+
+        return (IMApiResultInfo) GsonUtil.fromJson(httpHelper.returnResult(conn), IMApiResultInfo.class);
+
+    }
+
+    /**
+     * 发送房间消息
+     *
+     * @param fromUserId   发送人id
+     * @param toChatroomId 房间uid
+     * @param message      发送的消息
+     */
+    public IMApiResultInfo publishRoomMessage(String fromUserId, String toChatroomId, BaseMessage message) throws Exception {
+        String[] toChatroomIds = new String[1];
+        toChatroomIds[0] = toChatroomId;
+        return publishRoomMessage(fromUserId, toChatroomIds, message);
+    }
+
+    public IMApiResultInfo publishRoomMessage(String fromUserId, String[] toChatroomIds, BaseMessage message) throws Exception {
+        if (StringUtils.isBlank(fromUserId)) {
+            throw new RuntimeException("发送人不能为空");
+        }
+        if (Objects.isNull(toChatroomIds)) {
+            throw new RuntimeException("房间Uid不能为空");
+        }
+        if (Objects.isNull(message)) {
+            throw new RuntimeException("消息不能为空");
+        }
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("&fromUserId=").append(URLEncoder.encode(fromUserId, UTF8));
+
+        for (String child : toChatroomIds) {
+            sb.append("&toChatroomId=").append(URLEncoder.encode(child, UTF8));
+        }
+
+        String msgStr = GsonUtil.toJson(message);
+        log.info("publish msg: {}", msgStr);
+        sb.append("&objectName=").append(URLEncoder.encode(message.getObjectName(), UTF8));
+        sb.append("&content=").append(URLEncoder.encode(msgStr, UTF8));
+
+        String body = sb.toString();
+        if (body.indexOf("&") == 0) {
+            body = body.substring(1);
+        }
+
+        HttpURLConnection conn = httpHelper
+                .createIMPostHttpConnection("/message/chatroom/publish.json", "application/x-www-form-urlencoded");
+        httpHelper.setBodyParameter(body, conn);
+
+        return (IMApiResultInfo) GsonUtil.fromJson(httpHelper.returnResult(conn), IMApiResultInfo.class);
+    }
+
+    /**
+     * 查询用户是否在聊天室
+     *
+     * @param chatroomId 要查询的聊天室 ID(必传)
+     * @param userId     要查询的用户 ID(必传)
+     */
+    public IMApiResultInfo isInChartRoom(String chatroomId, String userId) throws Exception {
+        if (chatroomId == null) {
+            throw new RuntimeException("房间Uid不能为空");
+        }
+        if (userId == null) {
+            throw new RuntimeException("用户不能为空");
+        }
+        String body = "&chatroomId=" + URLEncoder.encode(chatroomId, UTF8) +
+                "&userId=" + URLEncoder.encode(userId, UTF8);
+        if (body.indexOf("&") == 0) {
+            body = body.substring(1);
+        }
+
+        HttpURLConnection conn = httpHelper.createIMPostHttpConnection("/chatroom/user/exist.json", "application/x-www-form-urlencoded");
+        httpHelper.setBodyParameter(body, conn);
+
+        return (IMApiResultInfo) GsonUtil.fromJson(httpHelper.returnResult(conn), IMApiResultInfo.class);
+    }
+
+}