zouxuan 5 år sedan
förälder
incheckning
a8381b28bc
100 ändrade filer med 5768 tillägg och 13 borttagningar
  1. 0 13
      mec-im/src/main/java/com/ym/App.java
  2. 24 0
      mec-im/src/main/java/com/ym/SealClassApplication.java
  3. 100 0
      mec-im/src/main/java/com/ym/SealClassConfiguration.java
  4. 60 0
      mec-im/src/main/java/com/ym/common/ApiException.java
  5. 52 0
      mec-im/src/main/java/com/ym/common/BaseResponse.java
  6. 15 0
      mec-im/src/main/java/com/ym/common/DisplayEnum.java
  7. 58 0
      mec-im/src/main/java/com/ym/common/ErrorEnum.java
  8. 14 0
      mec-im/src/main/java/com/ym/common/JwtUser.java
  9. 47 0
      mec-im/src/main/java/com/ym/common/UserAgentTypeEnum.java
  10. 17 0
      mec-im/src/main/java/com/ym/config/IMProperties.java
  11. 15 0
      mec-im/src/main/java/com/ym/config/ResourceServerConfig.java
  12. 18 0
      mec-im/src/main/java/com/ym/config/RoomProperties.java
  13. 15 0
      mec-im/src/main/java/com/ym/config/WhiteBoardProperties.java
  14. 35 0
      mec-im/src/main/java/com/ym/controller/BlackListController.java
  15. 35 0
      mec-im/src/main/java/com/ym/controller/BlockController.java
  16. 89 0
      mec-im/src/main/java/com/ym/controller/ChatroomController.java
  17. 32 0
      mec-im/src/main/java/com/ym/controller/ConversationController.java
  18. 36 0
      mec-im/src/main/java/com/ym/controller/DemotionController.java
  19. 32 0
      mec-im/src/main/java/com/ym/controller/DistributeController.java
  20. 82 0
      mec-im/src/main/java/com/ym/controller/GroupController.java
  21. 33 0
      mec-im/src/main/java/com/ym/controller/HistoryController.java
  22. 39 0
      mec-im/src/main/java/com/ym/controller/KeepAliveController.java
  23. 36 0
      mec-im/src/main/java/com/ym/controller/MuteAllMembersController.java
  24. 36 0
      mec-im/src/main/java/com/ym/controller/MuteChatroomsController.java
  25. 37 0
      mec-im/src/main/java/com/ym/controller/MuteGroupsController.java
  26. 36 0
      mec-im/src/main/java/com/ym/controller/MuteMembersController.java
  27. 36 0
      mec-im/src/main/java/com/ym/controller/MuteWhiteController.java
  28. 38 0
      mec-im/src/main/java/com/ym/controller/PrivateController.java
  29. 33 0
      mec-im/src/main/java/com/ym/controller/PushController.java
  30. 226 0
      mec-im/src/main/java/com/ym/controller/RoomController.java
  31. 36 0
      mec-im/src/main/java/com/ym/controller/SensitiveController.java
  32. 36 0
      mec-im/src/main/java/com/ym/controller/SystemController.java
  33. 38 0
      mec-im/src/main/java/com/ym/controller/TagController.java
  34. 40 0
      mec-im/src/main/java/com/ym/controller/UserController.java
  35. 53 0
      mec-im/src/main/java/com/ym/controller/WhitelistController.java
  36. 34 0
      mec-im/src/main/java/com/ym/dao/RoomDao.java
  37. 66 0
      mec-im/src/main/java/com/ym/dao/RoomMemberDao.java
  38. 16 0
      mec-im/src/main/java/com/ym/dao/SpeechDao.java
  39. 24 0
      mec-im/src/main/java/com/ym/dao/UserDao.java
  40. 36 0
      mec-im/src/main/java/com/ym/dao/WhiteboardDao.java
  41. 102 0
      mec-im/src/main/java/com/ym/filter/GlobalExceptionHandlerAdvice.java
  42. 237 0
      mec-im/src/main/java/com/ym/http/HttpHelper.java
  43. 16 0
      mec-im/src/main/java/com/ym/im/BaseMessage.java
  44. 333 0
      mec-im/src/main/java/com/ym/im/IMHelper.java
  45. 19 0
      mec-im/src/main/java/com/ym/im/message/ApplyForSpeechMessage.java
  46. 19 0
      mec-im/src/main/java/com/ym/im/message/AssistantTransferMessage.java
  47. 27 0
      mec-im/src/main/java/com/ym/im/message/ControlDeviceNotifyMessage.java
  48. 27 0
      mec-im/src/main/java/com/ym/im/message/DeviceStateChangedMessage.java
  49. 22 0
      mec-im/src/main/java/com/ym/im/message/DisplayMessage.java
  50. 40 0
      mec-im/src/main/java/com/ym/im/message/MemberChangedMessage.java
  51. 38 0
      mec-im/src/main/java/com/ym/im/message/RoleChangedMessage.java
  52. 27 0
      mec-im/src/main/java/com/ym/im/message/SpeechResultMessage.java
  53. 19 0
      mec-im/src/main/java/com/ym/im/message/TicketExpiredMessage.java
  54. 28 0
      mec-im/src/main/java/com/ym/im/message/TurnPageMessage.java
  55. 27 0
      mec-im/src/main/java/com/ym/im/message/UpgradeRoleMessage.java
  56. 26 0
      mec-im/src/main/java/com/ym/im/message/WhiteboardMessage.java
  57. 130 0
      mec-im/src/main/java/com/ym/job/ScheduleManager.java
  58. 18 0
      mec-im/src/main/java/com/ym/job/ScheduledDelayTask.java
  59. 11 0
      mec-im/src/main/java/com/ym/pojo/ActionEnum.java
  60. 20 0
      mec-im/src/main/java/com/ym/pojo/ControlDeviceTaskInfo.java
  61. 9 0
      mec-im/src/main/java/com/ym/pojo/DeviceTypeEnum.java
  62. 18 0
      mec-im/src/main/java/com/ym/pojo/IMApiResultInfo.java
  63. 22 0
      mec-im/src/main/java/com/ym/pojo/IMTokenInfo.java
  64. 13 0
      mec-im/src/main/java/com/ym/pojo/ReqChangeRole.java
  65. 20 0
      mec-im/src/main/java/com/ym/pojo/ReqChangeUserRoleData.java
  66. 15 0
      mec-im/src/main/java/com/ym/pojo/ReqDeviceControlData.java
  67. 14 0
      mec-im/src/main/java/com/ym/pojo/ReqDisplayData.java
  68. 11 0
      mec-im/src/main/java/com/ym/pojo/ReqMemberOnlineStatus.java
  69. 12 0
      mec-im/src/main/java/com/ym/pojo/ReqSpeechData.java
  70. 14 0
      mec-im/src/main/java/com/ym/pojo/ReqUpgradeRoleData.java
  71. 15 0
      mec-im/src/main/java/com/ym/pojo/ReqUserData.java
  72. 13 0
      mec-im/src/main/java/com/ym/pojo/ReqWhiteboardData.java
  73. 37 0
      mec-im/src/main/java/com/ym/pojo/RoleEnum.java
  74. 40 0
      mec-im/src/main/java/com/ym/pojo/Room.java
  75. 51 0
      mec-im/src/main/java/com/ym/pojo/RoomMember.java
  76. 63 0
      mec-im/src/main/java/com/ym/pojo/RoomResult.java
  77. 24 0
      mec-im/src/main/java/com/ym/pojo/ScheduledTaskInfo.java
  78. 18 0
      mec-im/src/main/java/com/ym/pojo/UpgradeRoleTaskInfo.java
  79. 21 0
      mec-im/src/main/java/com/ym/pojo/UserInfo.java
  80. 17 0
      mec-im/src/main/java/com/ym/pojo/WhiteBoardApiResultInfo.java
  81. 30 0
      mec-im/src/main/java/com/ym/pojo/Whiteboard.java
  82. 78 0
      mec-im/src/main/java/com/ym/service/ChatroomService.java
  83. 55 0
      mec-im/src/main/java/com/ym/service/GroupService.java
  84. 190 0
      mec-im/src/main/java/com/ym/service/Impl/ChatroomServiceImpl.java
  85. 136 0
      mec-im/src/main/java/com/ym/service/Impl/GroupServiceImpl.java
  86. 125 0
      mec-im/src/main/java/com/ym/service/Impl/MessageServiceImpl.java
  87. 1099 0
      mec-im/src/main/java/com/ym/service/Impl/RoomServiceImpl.java
  88. 165 0
      mec-im/src/main/java/com/ym/service/Impl/UserServiceImpl.java
  89. 51 0
      mec-im/src/main/java/com/ym/service/MessageService.java
  90. 67 0
      mec-im/src/main/java/com/ym/service/RoomService.java
  91. 64 0
      mec-im/src/main/java/com/ym/service/UserService.java
  92. 12 0
      mec-im/src/main/java/com/ym/utils/CheckUtils.java
  93. 34 0
      mec-im/src/main/java/com/ym/utils/CodeUtil.java
  94. 15 0
      mec-im/src/main/java/com/ym/utils/DateTimeUtils.java
  95. 45 0
      mec-im/src/main/java/com/ym/utils/IdentifierUtils.java
  96. 79 0
      mec-im/src/main/java/com/ym/utils/SecurityUtils.java
  97. 56 0
      mec-im/src/main/java/com/ym/whiteboard/WhiteBoardHelper.java
  98. 98 0
      mec-im/src/main/resources/application.yml
  99. 16 0
      mec-im/src/main/resources/bootstrap.properties
  100. 15 0
      mec-im/src/main/resources/logback-spring.xml

+ 0 - 13
mec-im/src/main/java/com/ym/App.java

@@ -1,13 +0,0 @@
-package com.ym;
-
-/**
- * Hello world!
- *
- */
-public class App 
-{
-    public static void main( String[] args )
-    {
-        System.out.println( "Hello World!" );
-    }
-}

+ 24 - 0
mec-im/src/main/java/com/ym/SealClassApplication.java

@@ -0,0 +1,24 @@
+package com.ym;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+@EnableAspectJAutoProxy
+@Slf4j
+@SpringBootApplication
+@EnableScheduling
+@EnableDiscoveryClient
+@Configuration
+public class SealClassApplication {
+
+	public static void main(String[] args) {
+		SpringApplication.run(SealClassApplication.class, args);
+		log.info("SealClassApplication started");
+	}
+
+}

+ 100 - 0
mec-im/src/main/java/com/ym/SealClassConfiguration.java

@@ -0,0 +1,100 @@
+package com.ym;
+
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.alibaba.fastjson.support.config.FastJsonConfig;
+import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
+import com.ym.filter.GlobalExceptionHandlerAdvice;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@Configuration
+public class SealClassConfiguration {
+
+    @Bean
+    public <K, V> RedisTemplate<K, V> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
+        RedisTemplate<K, V> redisTemplate = new RedisTemplate<>();
+        redisTemplate.setConnectionFactory(redisConnectionFactory);
+        return redisTemplate;
+    }
+
+    @Bean
+    public GlobalExceptionHandlerAdvice globalExceptionAdvice() {
+        return new GlobalExceptionHandlerAdvice();
+    }
+
+    /*@Bean
+    public JwtTokenHelper jwtTokenHelper() {
+        return jwtProperties.getTtlInMilliSec() == null
+                ? new JwtTokenHelper(jwtProperties.getSecret())
+                : new JwtTokenHelper(jwtProperties.getSecret(), jwtProperties.getTtlInMilliSec());
+    }*/
+
+    /*@Bean
+    public FilterRegistrationBean jwtTokenFilter() {
+        final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
+        registrationBean.setFilter(new JwtFilter());
+        registrationBean.addUrlPatterns("/*");
+        return registrationBean;
+    }*/
+
+    @Bean
+    public WebMvcConfigurer getWebMvcConfigurerAdapter() {
+        return new WebMvcConfigurer() {
+            @Override
+            public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
+                Iterator<HttpMessageConverter<?>> iterator = converters.iterator();
+                while (iterator.hasNext()) {
+                    HttpMessageConverter<?> c = iterator.next();
+                    if (c instanceof MappingJackson2HttpMessageConverter) {
+                        iterator.remove();
+                    }
+                }
+                FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
+                List<MediaType> supportedMediaTypes = new ArrayList<>();
+                supportedMediaTypes.add(MediaType.APPLICATION_JSON);
+                supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
+                supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
+                supportedMediaTypes.add(MediaType.MULTIPART_FORM_DATA);
+                converter.setSupportedMediaTypes(supportedMediaTypes);
+                FastJsonConfig fastJsonConfig = new FastJsonConfig();
+                fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue, SerializerFeature.PrettyFormat);
+                converter.setFastJsonConfig(fastJsonConfig);
+                converters.add(converter);
+            }
+        };
+    }
+
+    @Bean
+    @ConditionalOnProperty(prefix = "cn.rongcloud.web", value = "enableCors", havingValue = "true")
+    public FilterRegistrationBean corsFilter() {
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        CorsConfiguration config = new CorsConfiguration();
+        config.setAllowCredentials(true);
+        config.addAllowedOrigin("*");
+        config.addAllowedHeader("*");
+        config.addAllowedMethod("*");
+        source.registerCorsConfiguration("/**", config);
+        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
+        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
+        return bean;
+    }
+}

+ 60 - 0
mec-im/src/main/java/com/ym/common/ApiException.java

@@ -0,0 +1,60 @@
+package com.ym.common;
+
+/**
+ * Created by weiqinxiao on 2019/2/26.
+ */
+public class ApiException extends RuntimeException {
+    /**
+     *
+     */
+    private static final long serialVersionUID = 1L;
+    private ErrorEnum error;
+    private String errDetail;
+    private Object extraData;
+
+
+    /**
+     * 标准异常,使用预定义的的errCode和errMsg
+     *
+     * @param error
+     */
+    public ApiException(ErrorEnum error) {
+        this(error, null);
+    }
+
+    /**
+     * 标准异常,并携带自定义数据
+     *
+     * @param error
+     * @param errDetail
+     */
+    public ApiException(ErrorEnum error, String errDetail) {
+        this(error, errDetail, null);
+    }
+
+    /**
+     * 自定义异常,使用预定义的的errCode和自定义message,且携带自定义数据
+     *
+     * @param error
+     * @param errDetail
+     * @param extraData
+     */
+    public ApiException(ErrorEnum error, String errDetail, Object extraData) {
+        super(null == errDetail ? error.getErrMsg() : errDetail);
+        this.error = error;
+        this.errDetail = errDetail;
+        this.extraData = extraData;
+    }
+
+    public ErrorEnum getError() {
+        return error;
+    }
+
+    public String getErrDetail() {
+        return errDetail;
+    }
+
+    public Object getExtraData() {
+        return extraData;
+    }
+}

+ 52 - 0
mec-im/src/main/java/com/ym/common/BaseResponse.java

@@ -0,0 +1,52 @@
+package com.ym.common;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import static com.ym.common.ErrorEnum.ERR_SUCCESS;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+public class BaseResponse<T> {
+    private @Getter int errCode;
+    private @Setter @Getter String errMsg;
+    private @Setter @Getter String errDetail;
+    private @Getter BaseResponseResult data;
+
+    public BaseResponse() {
+        this(ERR_SUCCESS);
+    }
+
+    public BaseResponse(T data) {
+        this(ERR_SUCCESS);
+        setData(data);
+    }
+
+    public void setData(T data) {
+        this.data = new BaseResponseResult<T>(data);
+    }
+
+    public BaseResponse(ErrorEnum errorEnum) {
+        setErr(errorEnum, "");
+    }
+
+    public BaseResponse(ErrorEnum err, String errDetail, T data) {
+        setErr(err, errDetail);
+        setData(data);
+    }
+
+    public void setErr(ErrorEnum error, String errDetail) {
+        this.errCode = error.getErrCode();
+        this.errMsg = error.getErrMsg();
+        this.errDetail = errDetail;
+    }
+
+    class BaseResponseResult<R> {
+        private @Setter @Getter R result;
+
+        BaseResponseResult(R result) {
+            this.result = result;
+        }
+    }
+}

+ 15 - 0
mec-im/src/main/java/com/ym/common/DisplayEnum.java

@@ -0,0 +1,15 @@
+package com.ym.common;
+
+// Display 定义格式如下:
+// display://type=1?userId=xxx?uri=xxxxx
+// 0,1,3 时,userId 有效,对应此人的 id,uri 无效
+// 2 时,展示白板,必须携带白板 uri
+// 4 时,清空当前 room 的 display
+
+public enum DisplayEnum {
+    Assistant,//0
+    Teacher,//1
+    WhiteBoard,//2
+    Screen,//3
+    None, //4
+}

+ 58 - 0
mec-im/src/main/java/com/ym/common/ErrorEnum.java

@@ -0,0 +1,58 @@
+package com.ym.common;
+
+import lombok.Getter;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+public enum ErrorEnum {
+    ERR_SUCCESS(0x0000, "OK"),
+    ERR_OTHER(0x00FF, "Error"),
+    ERR_REQUEST_PARA_ERR(1, "Missing or invalid parameter"),
+    ERR_INVALID_AUTH(2, "Invalid or expired authorization"),
+    ERR_ACCESS_DENIED(3, "Access denied"),
+    ERR_BAD_REQUEST(4, "Bad request"),
+
+    //IM error
+    ERR_IM_TOKEN_ERROR(10, "IM token error"),
+    ERR_CREATE_ROOM_ERROR(11, "Create room error"),
+    ERR_JOIN_ROOM_ERROR(12, "Join room error"),
+    ERR_MESSAGE_ERROR(13, "IM Message send error"),
+
+
+    //room error
+    ERR_ROOM_NOT_EXIST(20, "Room not exist"),
+    ERR_USER_NOT_EXIST_IN_ROOM(21, "User not exist in room"),
+    ERR_EXIT_ROOM_ERROR(22, "Exit room error"),
+    ERR_TEACHER_NOT_EXIST_IN_ROOM(23, "Teacher not exist in room"),
+    ERR_ASSISTANT_NOT_EXIST_IN_ROOM(24, "Assistant not exist in room"),
+    ERR_CREATE_WHITE_BOARD(25, "Create whiteboard error"),
+    ERR_WHITE_BOARD_NOT_EXIST(26, "Whiteboard not exist"),
+    ERR_DELETE_WHITE_BOARD(27, "Delete whiteboard error"),
+    ERR_USER_EXIST_IN_ROOM(28, "User exist in room"),
+    ERR_CHANGE_SELF_ROLE(29, "Can not change self role"),
+    ERR_APPLY_TICKET_INVALID(30, "Apply ticket invalid"),
+    ERR_OVER_MAX_COUNT(31, "Over max count"),
+    ERR_TEACHER_EXIST_IN_ROOM(32, "Teacher exist in room"),
+    ERR_DOWNGRADE_ROLE(33, "Can't downgrade role"),
+    ERR_CHANGE_ROLE(34, "Only change student to teacher"),
+
+    ;
+
+    private @Getter int errCode;
+    private @Getter String errMsg;
+    private ErrorEnum(int errCode, String errMsg) {
+        this.errCode = errCode;
+        this.errMsg = errMsg;
+    }
+
+    public static ErrorEnum getEnumByValue(long errCode) {
+        for(ErrorEnum item : ErrorEnum.values()) {
+            if(item.getErrCode() == errCode) {
+                return item;
+            }
+        }
+
+        throw new IllegalArgumentException();
+    }
+}

+ 14 - 0
mec-im/src/main/java/com/ym/common/JwtUser.java

@@ -0,0 +1,14 @@
+package com.ym.common;
+
+import lombok.Data;
+
+/**
+ * Created by weiqinxiao on 2019/2/27.
+ */
+@Data
+public class JwtUser {
+    private String userId;
+    private String roomId;
+    private String userName;
+    private int deviceType;
+}

+ 47 - 0
mec-im/src/main/java/com/ym/common/UserAgentTypeEnum.java

@@ -0,0 +1,47 @@
+package com.ym.common;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+
+public enum UserAgentTypeEnum {
+	AGENT_ANDROID("Android", 1),
+	AGENT_IOS("IOS", 2),
+	AGENT_OTHER("Other", 3);
+
+	private @Getter
+    @Setter(AccessLevel.PRIVATE) String agent;
+	private @Getter
+    @Setter(AccessLevel.PRIVATE) int type;
+
+	private UserAgentTypeEnum(String agent, int type) {
+		this.agent = agent;
+		this.type = type;
+	}
+
+	public static UserAgentTypeEnum getEnumByValue(int type) {
+		for (UserAgentTypeEnum item : UserAgentTypeEnum.values()) {
+			if (item.getType() == type) {
+				return item;
+			}
+		}
+
+		throw new IllegalArgumentException();
+	}
+
+	public static UserAgentTypeEnum getEnumByUserAgent(String userAgent) {
+		UserAgentTypeEnum type = AGENT_OTHER;
+		
+		if (null != userAgent) {
+			userAgent = userAgent.toLowerCase();
+			if(userAgent.contains("iphone")||userAgent.contains("ipod")||userAgent.contains("ipad")){
+		        type = AGENT_IOS;
+		    } else if(userAgent.contains("android") ) { 
+		        type = AGENT_ANDROID;
+		    } 
+		}
+
+		return type;
+	}
+
+}

+ 17 - 0
mec-im/src/main/java/com/ym/config/IMProperties.java

@@ -0,0 +1,17 @@
+package com.ym.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "cn.rongcloud.im")
+public class IMProperties {
+    private String appKey;
+    private String secret;
+    private String host;
+}

+ 15 - 0
mec-im/src/main/java/com/ym/config/ResourceServerConfig.java

@@ -0,0 +1,15 @@
+package com.ym.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
+
+@Configuration
+@EnableResourceServer
+public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
+    @Override
+    public void configure(HttpSecurity http) throws Exception {
+        http.authorizeRequests().antMatchers("/v2/api-docs").permitAll().anyRequest().authenticated().and().csrf().disable();
+    }
+}

+ 18 - 0
mec-im/src/main/java/com/ym/config/RoomProperties.java

@@ -0,0 +1,18 @@
+package com.ym.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "cn.rongcloud.room")
+public class RoomProperties {
+    private int maxCount;
+    private long taskTtl;
+    private long roomTtl;
+    private long userIMOfflineKickTtl;
+}

+ 15 - 0
mec-im/src/main/java/com/ym/config/WhiteBoardProperties.java

@@ -0,0 +1,15 @@
+package com.ym.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "cn.rongcloud.whiteboard")
+public class WhiteBoardProperties {
+    private String host;
+}

+ 35 - 0
mec-im/src/main/java/com/ym/controller/BlackListController.java

@@ -0,0 +1,35 @@
+package com.ym.controller;
+
+import com.ym.service.UserService;
+import io.rong.models.user.UserModel;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/blackList")
+public class BlackListController {
+
+    @Autowired
+    UserService userService;
+
+    @RequestMapping(value = "/add", method = RequestMethod.POST)
+    public Object add(@RequestBody UserModel userModel) throws Exception {
+        return userService.blackListAdd(userModel);
+    }
+
+    @RequestMapping(value = "/remove", method = RequestMethod.POST)
+    public Object remove(@RequestBody UserModel userModel) throws Exception {
+        return userService.blackListRemove(userModel);
+    }
+
+    @RequestMapping(value = "/getList", method = RequestMethod.POST)
+    public Object getList(@RequestBody UserModel userModel) throws Exception {
+        return userService.blackListGetList(userModel);
+    }
+}

+ 35 - 0
mec-im/src/main/java/com/ym/controller/BlockController.java

@@ -0,0 +1,35 @@
+package com.ym.controller;
+
+import com.ym.service.UserService;
+import io.rong.models.user.UserModel;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/block")
+public class BlockController {
+
+    @Autowired
+    UserService userService;
+
+    @RequestMapping(value = "/add", method = RequestMethod.POST)
+    public Object add(@RequestBody UserModel userModel) throws Exception {
+        return userService.blockAdd(userModel);
+    }
+
+    @RequestMapping(value = "/remove", method = RequestMethod.POST)
+    public Object remove(@RequestBody String id) throws Exception {
+        return userService.blockRemove(id);
+    }
+
+    @RequestMapping(value = "/getList", method = RequestMethod.POST)
+    public Object getList() throws Exception {
+        return userService.blockGetList();
+    }
+}

+ 89 - 0
mec-im/src/main/java/com/ym/controller/ChatroomController.java

@@ -0,0 +1,89 @@
+package com.ym.controller;
+
+import com.ym.service.ChatroomService;
+import com.ym.service.MessageService;
+import io.rong.models.Result;
+import io.rong.models.chatroom.ChatroomMember;
+import io.rong.models.chatroom.ChatroomModel;
+import io.rong.models.message.ChatroomMessage;
+import io.rong.models.response.CheckChatRoomUserResult;
+import io.rong.models.response.ListGagChatroomUserResult;
+import io.rong.models.response.ResponseResult;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/chatroom")
+public class ChatroomController {
+
+    @Autowired
+    MessageService messageService;
+    @Autowired
+    ChatroomService chatroomCreate;
+
+    @RequestMapping(value = "/send", method = RequestMethod.POST)
+    public Result send(@RequestBody ChatroomMessage chatroomMessage) throws Exception {
+        return messageService.chatroomSend(chatroomMessage);
+    }
+
+    @RequestMapping(value = "/broadcast", method = RequestMethod.POST)
+    public Result broadcast(@RequestBody ChatroomMessage chatroomMessage) throws Exception {
+        return messageService.chatroomBroadcast(chatroomMessage);
+    }
+
+    @RequestMapping(value = "/create", method = RequestMethod.POST)
+    public Result create(@RequestBody ChatroomModel[] chatroomModels) throws Exception {
+        return chatroomCreate.chatroomCreate(chatroomModels);
+    }
+
+    @RequestMapping(value = "/destory", method = RequestMethod.POST)
+    public Result destory(@RequestBody ChatroomModel chatroomModel) throws Exception {
+        return chatroomCreate.chatroomDestory(chatroomModel);
+    }
+
+    @RequestMapping(value = "/get", method = RequestMethod.POST)
+    public Result get(@RequestBody ChatroomModel chatroomModel) throws Exception {
+        return chatroomCreate.chatroomGet(chatroomModel);
+    }
+
+    @RequestMapping(value = "/isExist", method = RequestMethod.POST)
+    public CheckChatRoomUserResult isExist(@RequestBody ChatroomMember chatroomMember) throws Exception {
+        return chatroomCreate.chatroomIsExit(chatroomMember);
+    }
+
+    @RequestMapping(value = "/block/add", method = RequestMethod.POST)
+    public Result blockAdd(@RequestBody ChatroomModel chatroomModel) throws Exception {
+        return chatroomCreate.chatroomBlockAdd(chatroomModel);
+    }
+
+    @RequestMapping(value = "/block/remove", method = RequestMethod.POST)
+    public Result blockRemove(@RequestBody ChatroomModel chatroomModel) throws Exception {
+        return chatroomCreate.chatroomBlockRemove(chatroomModel);
+    }
+
+    @RequestMapping(value = "/block/getList", method = RequestMethod.POST)
+    public Result blockGetList(@RequestBody String chatroomId) throws Exception {
+        return chatroomCreate.chatroomBlockGetList(chatroomId);
+    }
+
+    @RequestMapping(value = "/muteMembers/add", method = RequestMethod.POST)
+    public ResponseResult muteMembersAdd(@RequestBody ChatroomModel chatroomModel) throws Exception {
+        return chatroomCreate.chatroomMuteMembersAdd(chatroomModel);
+    }
+
+    @RequestMapping(value = "/muteMembers/remove", method = RequestMethod.POST)
+    public ResponseResult muteMembersRemove(@RequestBody ChatroomModel chatroomModel) throws Exception {
+        return chatroomCreate.chatroomMuteMembersRemove(chatroomModel);
+    }
+
+    @RequestMapping(value = "/muteMembers/getList", method = RequestMethod.POST)
+    public ListGagChatroomUserResult muteMembersGetList(@RequestBody ChatroomModel chatroomModel) throws Exception {
+        return chatroomCreate.chatroomMuteMembersGetList(chatroomModel);
+    }
+}

+ 32 - 0
mec-im/src/main/java/com/ym/controller/ConversationController.java

@@ -0,0 +1,32 @@
+package com.ym.controller;
+
+import com.ym.service.GroupService;
+import io.rong.models.Result;
+import io.rong.models.conversation.ConversationModel;
+import io.rong.models.group.GroupModel;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/conversation")
+public class ConversationController {
+
+    @Autowired
+    GroupService groupService;
+
+    @RequestMapping(value = "/mute", method = RequestMethod.POST)
+    public Result mute(@RequestBody ConversationModel conversationModel) throws Exception {
+        return groupService.conversationMute(conversationModel);
+    }
+
+    @RequestMapping(value = "/unmute", method = RequestMethod.POST)
+    public Result unmute(@RequestBody ConversationModel conversationModel) throws Exception {
+        return groupService.conversationUnmute(conversationModel);
+    }
+}

+ 36 - 0
mec-im/src/main/java/com/ym/controller/DemotionController.java

@@ -0,0 +1,36 @@
+package com.ym.controller;
+
+import com.ym.service.ChatroomService;
+import io.rong.models.response.ChatroomDemotionMsgResult;
+import io.rong.models.response.ResponseResult;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/demotion")
+public class DemotionController {
+
+    @Autowired
+    ChatroomService chatroomService;
+
+    @RequestMapping(value = "/add", method = RequestMethod.POST)
+    public ResponseResult add(@RequestBody String[] objectNames) throws Exception {
+        return chatroomService.demotionAdd(objectNames);
+    }
+
+    @RequestMapping(value = "/remove", method = RequestMethod.POST)
+    public ResponseResult remove(@RequestBody String[] objectNames) throws Exception {
+        return chatroomService.demotionRemove(objectNames);
+    }
+
+    @RequestMapping(value = "/getList", method = RequestMethod.POST)
+    public ChatroomDemotionMsgResult getList() throws Exception {
+        return chatroomService.demotionGetList();
+    }
+}

+ 32 - 0
mec-im/src/main/java/com/ym/controller/DistributeController.java

@@ -0,0 +1,32 @@
+package com.ym.controller;
+
+import com.ym.service.ChatroomService;
+import io.rong.models.chatroom.ChatroomModel;
+import io.rong.models.response.ChatroomDemotionMsgResult;
+import io.rong.models.response.ResponseResult;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/distribute")
+public class DistributeController {
+
+    @Autowired
+    ChatroomService chatroomService;
+
+    @RequestMapping(value = "/stop", method = RequestMethod.POST)
+    public ResponseResult stop(@RequestBody ChatroomModel chatroomModel) throws Exception {
+        return chatroomService.distributeStop(chatroomModel);
+    }
+
+    @RequestMapping(value = "/resume", method = RequestMethod.POST)
+    public ResponseResult resume(@RequestBody ChatroomModel chatroomModel) throws Exception {
+        return chatroomService.distributeResume(chatroomModel);
+    }
+}

+ 82 - 0
mec-im/src/main/java/com/ym/controller/GroupController.java

@@ -0,0 +1,82 @@
+package com.ym.controller;
+
+import com.ym.service.GroupService;
+import com.ym.service.MessageService;
+import io.rong.methods.group.Group;
+import io.rong.models.Result;
+import io.rong.models.group.GroupModel;
+import io.rong.models.group.UserGroup;
+import io.rong.models.message.*;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/group")
+public class GroupController {
+
+    @Autowired
+    MessageService messageService;
+    @Autowired
+    GroupService groupService;
+
+    @RequestMapping(value = "/send", method = RequestMethod.POST)
+    public Result send(@RequestBody GroupMessage groupMessage) throws Exception {
+        return messageService.groupSend(groupMessage);
+    }
+
+    @RequestMapping(value = "/sendDirection", method = RequestMethod.POST)
+    public Result sendDirection(@RequestBody GroupMessage groupMessage) throws Exception {
+        return messageService.groupSendDirection(groupMessage);
+    }
+
+    @RequestMapping(value = "/sendMention", method = RequestMethod.POST)
+    public Result sendMention(@RequestBody MentionMessage mentionMessage) throws Exception {
+        return messageService.groupSendMention(mentionMessage);
+    }
+
+    @RequestMapping(value = "/recall", method = RequestMethod.POST)
+    public Result recall(@RequestBody RecallMessage recallMessage) throws Exception {
+        return messageService.groupRecall(recallMessage);
+    }
+
+    @RequestMapping(value = "/sync", method = RequestMethod.POST)
+    public Result sync(@RequestBody UserGroup userGroup) throws Exception {
+        return groupService.groupSync(userGroup);
+    }
+
+    @RequestMapping(value = "/create", method = RequestMethod.POST)
+    public Result create(@RequestBody GroupModel groupModel) throws Exception {
+        return groupService.groupCreate(groupModel);
+    }
+
+    @RequestMapping(value = "/get", method = RequestMethod.POST)
+    public Result get(@RequestBody GroupModel groupModel) throws Exception {
+        return groupService.groupGet(groupModel);
+    }
+
+    @RequestMapping(value = "/update", method = RequestMethod.POST)
+    public Result update(@RequestBody GroupModel groupModel) throws Exception {
+        return groupService.groupUpdate(groupModel);
+    }
+
+    @RequestMapping(value = "/join", method = RequestMethod.POST)
+    public Result join(@RequestBody GroupModel groupModel) throws Exception {
+        return groupService.groupJoin(groupModel);
+    }
+
+    @RequestMapping(value = "/quit", method = RequestMethod.POST)
+    public Result quit(@RequestBody GroupModel groupModel) throws Exception {
+        return groupService.groupQuit(groupModel);
+    }
+
+    @RequestMapping(value = "/dismiss", method = RequestMethod.POST)
+    public Result dismiss(@RequestBody GroupModel groupModel) throws Exception {
+        return groupService.groupDismiss(groupModel);
+    }
+}

+ 33 - 0
mec-im/src/main/java/com/ym/controller/HistoryController.java

@@ -0,0 +1,33 @@
+package com.ym.controller;
+
+import com.ym.service.MessageService;
+import io.rong.models.Result;
+import io.rong.models.message.GroupMessage;
+import io.rong.models.message.MentionMessage;
+import io.rong.models.message.RecallMessage;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/history")
+public class HistoryController {
+
+    @Autowired
+    MessageService messageService;
+
+    @RequestMapping(value = "/get", method = RequestMethod.POST)
+    public Result get(@RequestBody String date) throws Exception {
+        return messageService.historyGet(date);
+    }
+
+    @RequestMapping(value = "/remove", method = RequestMethod.POST)
+    public Result remove(@RequestBody String date) throws Exception {
+        return messageService.historyRemove(date);
+    }
+}

+ 39 - 0
mec-im/src/main/java/com/ym/controller/KeepAliveController.java

@@ -0,0 +1,39 @@
+package com.ym.controller;
+
+import com.ym.service.ChatroomService;
+import io.rong.models.Result;
+import io.rong.models.chatroom.ChatroomModel;
+import io.rong.models.group.GroupModel;
+import io.rong.models.response.ChatroomKeepaliveResult;
+import io.rong.models.response.ResponseResult;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/keepAlive")
+public class KeepAliveController {
+
+    @Autowired
+    ChatroomService chatroomService;
+
+    @RequestMapping(value = "/add", method = RequestMethod.POST)
+    public ResponseResult add(@RequestBody ChatroomModel chatroomModel) throws Exception {
+        return chatroomService.keepAliveAdd(chatroomModel);
+    }
+
+    @RequestMapping(value = "/remove", method = RequestMethod.POST)
+    public ResponseResult remove(@RequestBody ChatroomModel chatroomModel) throws Exception {
+        return chatroomService.keepAliveRemove(chatroomModel);
+    }
+
+    @RequestMapping(value = "/getList", method = RequestMethod.POST)
+    public ChatroomKeepaliveResult getList() throws Exception {
+        return chatroomService.keepAliveGetList();
+    }
+}

+ 36 - 0
mec-im/src/main/java/com/ym/controller/MuteAllMembersController.java

@@ -0,0 +1,36 @@
+package com.ym.controller;
+
+import com.ym.service.GroupService;
+import io.rong.models.Result;
+import io.rong.models.group.GroupModel;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/muteAllMembers")
+public class MuteAllMembersController {
+
+    @Autowired
+    GroupService groupService;
+
+    @RequestMapping(value = "/add", method = RequestMethod.POST)
+    public Result add(@RequestBody String[] groupIds) throws Exception {
+        return groupService.muteAllMembersAdd(groupIds);
+    }
+
+    @RequestMapping(value = "/remove", method = RequestMethod.POST)
+    public Result remove(@RequestBody String[] groupIds) throws Exception {
+        return groupService.muteAllMembersRemove(groupIds);
+    }
+
+    @RequestMapping(value = "/getList", method = RequestMethod.POST)
+    public Result getList() throws Exception {
+        return groupService.muteAllMembersGetList();
+    }
+}

+ 36 - 0
mec-im/src/main/java/com/ym/controller/MuteChatroomsController.java

@@ -0,0 +1,36 @@
+package com.ym.controller;
+
+import com.ym.service.UserService;
+import io.rong.models.Result;
+import io.rong.models.chatroom.ChatroomModel;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/muteChatrooms")
+public class MuteChatroomsController {
+
+    @Autowired
+    UserService userService;
+
+    @RequestMapping(value = "/add", method = RequestMethod.POST)
+    public Result add(@RequestBody ChatroomModel chatroomModel) throws Exception {
+        return userService.muteChatroomsAdd(chatroomModel);
+    }
+
+    @RequestMapping(value = "/remove", method = RequestMethod.POST)
+    public Result remove(@RequestBody ChatroomModel chatroomModel) throws Exception {
+        return userService.muteChatroomsRemove(chatroomModel);
+    }
+
+    @RequestMapping(value = "/getList", method = RequestMethod.POST)
+    public Result getList() throws Exception {
+        return userService.muteChatroomsGetList();
+    }
+}

+ 37 - 0
mec-im/src/main/java/com/ym/controller/MuteGroupsController.java

@@ -0,0 +1,37 @@
+package com.ym.controller;
+
+import com.ym.service.UserService;
+import io.rong.models.Result;
+import io.rong.models.group.GroupModel;
+import io.rong.models.user.UserModel;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/muteGroups")
+public class MuteGroupsController {
+
+    @Autowired
+    UserService userService;
+
+    @RequestMapping(value = "/add", method = RequestMethod.POST)
+    public Result add(@RequestBody GroupModel groupModel) throws Exception {
+        return userService.muteGroupsAdd(groupModel);
+    }
+
+    @RequestMapping(value = "/remove", method = RequestMethod.POST)
+    public Result remove(@RequestBody GroupModel groupModel) throws Exception {
+        return userService.muteGroupsRemove(groupModel);
+    }
+
+    @RequestMapping(value = "/getList", method = RequestMethod.POST)
+    public Result getList() throws Exception {
+        return userService.muteGroupsGetList();
+    }
+}

+ 36 - 0
mec-im/src/main/java/com/ym/controller/MuteMembersController.java

@@ -0,0 +1,36 @@
+package com.ym.controller;
+
+import com.ym.service.GroupService;
+import io.rong.models.Result;
+import io.rong.models.group.GroupModel;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/muteMembers")
+public class MuteMembersController {
+
+    @Autowired
+    GroupService groupService;
+
+    @RequestMapping(value = "/add", method = RequestMethod.POST)
+    public Result add(@RequestBody GroupModel groupModel) throws Exception {
+        return groupService.muteMembersAdd(groupModel);
+    }
+
+    @RequestMapping(value = "/remove", method = RequestMethod.POST)
+    public Result remove(@RequestBody GroupModel groupModel) throws Exception {
+        return groupService.muteMembersRemove(groupModel);
+    }
+
+    @RequestMapping(value = "/getList", method = RequestMethod.POST)
+    public Result getList(String groupId) throws Exception {
+        return groupService.muteMembersGetList(groupId);
+    }
+}

+ 36 - 0
mec-im/src/main/java/com/ym/controller/MuteWhiteController.java

@@ -0,0 +1,36 @@
+package com.ym.controller;
+
+import com.ym.service.GroupService;
+import io.rong.models.Result;
+import io.rong.models.group.GroupModel;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/muteWhite")
+public class MuteWhiteController {
+
+    @Autowired
+    GroupService groupService;
+
+    @RequestMapping(value = "/add", method = RequestMethod.POST)
+    public Result add(@RequestBody GroupModel groupModel) throws Exception {
+        return groupService.muteWhiteAdd(groupModel);
+    }
+
+    @RequestMapping(value = "/remove", method = RequestMethod.POST)
+    public Result remove(@RequestBody GroupModel groupModel) throws Exception {
+        return groupService.muteWhiteRemove(groupModel);
+    }
+
+    @RequestMapping(value = "/getList", method = RequestMethod.POST)
+    public Result getList(@RequestBody String groupId) throws Exception {
+        return groupService.muteWhiteGetList(groupId);
+    }
+}

+ 38 - 0
mec-im/src/main/java/com/ym/controller/PrivateController.java

@@ -0,0 +1,38 @@
+package com.ym.controller;
+
+import com.ym.service.MessageService;
+import io.rong.models.Result;
+import io.rong.models.message.PrivateMessage;
+import io.rong.models.message.RecallMessage;
+import io.rong.models.message.TemplateMessage;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/private")
+public class PrivateController {
+
+    @Autowired
+    MessageService messageService;
+
+    @RequestMapping(value = "/send", method = RequestMethod.POST)
+    public Result send(@RequestBody PrivateMessage privateMessage) throws Exception {
+        return messageService.privateSend(privateMessage);
+    }
+
+    @RequestMapping(value = "/recall", method = RequestMethod.POST)
+    public Result recall(@RequestBody RecallMessage recallMessage) throws Exception {
+        return messageService.privateRecall(recallMessage);
+    }
+
+    @RequestMapping(value = "/sendTemplate", method = RequestMethod.POST)
+    public Result sendTemplate(@RequestBody TemplateMessage templateMessage) throws Exception {
+        return messageService.privateSendTemplate(templateMessage);
+    }
+}

+ 33 - 0
mec-im/src/main/java/com/ym/controller/PushController.java

@@ -0,0 +1,33 @@
+package com.ym.controller;
+
+import com.ym.service.MessageService;
+import io.rong.models.Result;
+import io.rong.models.message.BroadcastMessage;
+import io.rong.models.push.BroadcastModel;
+import io.rong.models.push.PushModel;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/push")
+public class PushController {
+
+    @Autowired
+    MessageService messageService;
+
+    @RequestMapping(value = "/push", method = RequestMethod.POST)
+    public Result push(@RequestBody PushModel pushModel) throws Exception {
+        return messageService.pushPush(pushModel);
+    }
+
+    @RequestMapping(value = "/message", method = RequestMethod.POST)
+    public Result message(@RequestBody BroadcastModel broadcastModel) throws Exception {
+        return messageService.pushMessage(broadcastModel);
+    }
+}

+ 226 - 0
mec-im/src/main/java/com/ym/controller/RoomController.java

@@ -0,0 +1,226 @@
+package com.ym.controller;
+
+import com.ym.common.ApiException;
+import com.ym.common.BaseResponse;
+import com.ym.common.ErrorEnum;
+import com.ym.common.JwtUser;
+import com.ym.pojo.*;
+import com.ym.service.RoomService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/room")
+public class RoomController {
+    @Autowired
+    RoomService roomService;
+
+    @RequestMapping(value = "/join", method = RequestMethod.POST)
+    public BaseResponse<RoomResult> joinRoom(@RequestBody ReqUserData data,
+                                             @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        RoomResult roomResult = roomService.joinRoom(data.getUserName(), data.getRoomId(), data.isAudience(), data.isDisableCamera(), jwtUser);
+        return new BaseResponse<>(roomResult);
+    }
+
+    @RequestMapping(value = "/leave", method = RequestMethod.POST)
+    public BaseResponse<Boolean> leaveRoom(@RequestBody ReqUserData data,
+                                           @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        boolean result = roomService.leaveRoom(jwtUser, data.getRoomId());
+        return new BaseResponse<>(result);
+    }
+
+    @RequestMapping(value = "/downgrade", method = RequestMethod.POST)
+    public BaseResponse<Boolean> downRole(@RequestBody ReqChangeUserRoleData data,
+                                          @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        boolean result = roomService.downgrade(data.getRoomId(), jwtUser, data.getUsers());
+        return new BaseResponse<>(result);
+    }
+
+    @RequestMapping(value = "/kick", method = RequestMethod.POST)
+    public BaseResponse<Boolean> kickMember(@RequestBody ReqUserData data,
+                                            @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        boolean result = roomService.kickMember(data.getRoomId(), data.getUserId(), jwtUser);
+        return new BaseResponse<>(result);
+    }
+
+    //only teacher
+    @RequestMapping(value = "/display", method = RequestMethod.POST)
+    public BaseResponse<Boolean> display(@RequestBody ReqDisplayData data,
+                                         @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        boolean result = roomService.display(data.getRoomId(), data.getType(), data.getUserId(), data.getUri(), jwtUser);
+        return new BaseResponse<>(result);
+    }
+
+    @RequestMapping(value = "/whiteboard/create", method = RequestMethod.POST)
+    public BaseResponse<String> createWhiteBoard(@RequestBody ReqWhiteboardData data,
+                                                 @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        String result = roomService.createWhiteBoard(data.getRoomId(), jwtUser);
+        return new BaseResponse<>(result);
+    }
+
+
+    @RequestMapping(value = "/whiteboard/delete", method = RequestMethod.POST)
+    public BaseResponse<Boolean> destroyWhiteBoard(@RequestBody ReqWhiteboardData data,
+                                                   @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        boolean result = roomService.deleteWhiteboard(data.getRoomId(), jwtUser, data.getWhiteboardId());
+        return new BaseResponse<>(result);
+    }
+
+    @RequestMapping(value = "/whiteboard/list", method = RequestMethod.GET)
+    public BaseResponse<List<RoomResult.WhiteboardResult>> getWhiteBoard(@RequestParam String roomId,
+                                                                         @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        List<RoomResult.WhiteboardResult> whiteboards = roomService.getWhiteboard(roomId, jwtUser);
+        return new BaseResponse<>(whiteboards);
+    }
+
+    @RequestMapping(value = "/device/approve", method = RequestMethod.POST)
+    public BaseResponse<Boolean> approveControlDevice(@RequestBody ReqDeviceControlData data,
+                                                      @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        boolean result;
+        result = roomService.approveControlDevice(data.getRoomId(), data.getTicket(), jwtUser);
+        return new BaseResponse<>(result);
+    }
+
+    @RequestMapping(value = "/device/reject", method = RequestMethod.POST)
+    public BaseResponse<Boolean> rejectControlDevice(@RequestBody ReqDeviceControlData data,
+                                                      @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        boolean result;
+        result = roomService.rejectControlDevice(data.getRoomId(), data.getTicket(), jwtUser);
+        return new BaseResponse<>(result);
+    }
+
+    @RequestMapping(value = "/device/control", method = RequestMethod.POST)
+    public BaseResponse<Boolean> controlDevice(@RequestBody ReqDeviceControlData data,
+                                               @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        boolean result;
+        if (data.getCameraOn() != null) {
+            result = roomService.controlDevice(data.getRoomId(), data.getUserId(), DeviceTypeEnum.Camera, data.getCameraOn(), jwtUser);
+        } else if (data.getMicrophoneOn() != null) {
+            result = roomService.controlDevice(data.getRoomId(), data.getUserId(), DeviceTypeEnum.Microphone, data.getMicrophoneOn(), jwtUser);
+        } else {
+            throw new ApiException(ErrorEnum.ERR_REQUEST_PARA_ERR);
+        }
+        return new BaseResponse<>(result);
+    }
+
+    @RequestMapping(value = "/device/sync", method = RequestMethod.POST)
+    public BaseResponse<Boolean> syncDeviceState(@RequestBody ReqDeviceControlData data,
+                                                 @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        boolean result;
+        if (data.getCameraOn() != null) {
+            result = roomService.syncDeviceState(data.getRoomId(), DeviceTypeEnum.Camera, data.getCameraOn(), jwtUser);
+        } else if (data.getMicrophoneOn() != null) {
+            result = roomService.syncDeviceState(data.getRoomId(), DeviceTypeEnum.Microphone, data.getMicrophoneOn(), jwtUser);
+        } else {
+            throw new ApiException(ErrorEnum.ERR_REQUEST_PARA_ERR);
+        }
+        return new BaseResponse<>(result);
+    }
+
+    @RequestMapping(value = "/whiteboard/turn-page", method = RequestMethod.POST)
+    public BaseResponse<Boolean> turnPage(@RequestBody ReqWhiteboardData data,
+                                          @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        boolean result = roomService.turnWhiteBoardPage(data.getRoomId(), data.getWhiteboardId(), data.getPage(), jwtUser);
+        return new BaseResponse<>(result);
+    }
+
+    @RequestMapping(value = "/members", method = RequestMethod.GET)
+    public BaseResponse<List<RoomResult.MemberResult>> getMembers(@RequestParam String roomId,
+                                                                  @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        List<RoomResult.MemberResult> whiteboards = roomService.getMembers(roomId, jwtUser);
+        return new BaseResponse<>(whiteboards);
+    }
+
+    @RequestMapping(value = "/speech/apply", method = RequestMethod.POST)
+    public BaseResponse<Boolean> apply(@RequestBody ReqSpeechData data,
+                                       @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        Boolean result = roomService.applySpeech(data.getRoomId(), jwtUser);
+        return new BaseResponse<>(result);
+    }
+
+    @RequestMapping(value = "/speech/approve", method = RequestMethod.POST)
+    public BaseResponse<Boolean> approval(@RequestBody ReqSpeechData data,
+                                          @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        Boolean result = roomService.approveSpeech(data.getRoomId(), data.getTicket(), jwtUser);
+        return new BaseResponse<>(result);
+    }
+
+    @RequestMapping(value = "/speech/reject", method = RequestMethod.POST)
+    public BaseResponse<Boolean> reject(@RequestBody ReqSpeechData data,
+                                        @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        Boolean result = roomService.rejectSpeech(data.getRoomId(), data.getTicket(), jwtUser);
+        return new BaseResponse<>(result);
+    }
+
+    @RequestMapping(value = "/transfer", method = RequestMethod.POST)
+    public BaseResponse<Boolean> transfer(@RequestBody ReqUpgradeRoleData data,
+                                          @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        Boolean result = roomService.transfer(data.getRoomId(), data.getUserId(), jwtUser);
+        return new BaseResponse<>(result);
+    }
+
+    @RequestMapping(value = "/upgrade/invite", method = RequestMethod.POST)
+    public BaseResponse<Boolean> inviteUpgradeRole(@RequestBody ReqUpgradeRoleData data,
+                                                   @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        Boolean result = roomService.inviteUpgradeRole(data.getRoomId(), data.getUserId(), data.getRole(), jwtUser);
+        return new BaseResponse<>(result);
+    }
+
+    @RequestMapping(value = "/upgrade/approve", method = RequestMethod.POST)
+    public BaseResponse<Boolean> approveUpgradeRole(@RequestBody ReqUpgradeRoleData data,
+                                                    @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        Boolean result = roomService.approveUpgradeRole(data.getRoomId(), data.getTicket(), jwtUser);
+        return new BaseResponse<>(result);
+    }
+
+    @RequestMapping(value = "/upgrade/reject", method = RequestMethod.POST)
+    public BaseResponse<Boolean> rejectUpgradeRole(@RequestBody ReqUpgradeRoleData data,
+                                                   @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        Boolean result = roomService.rejectUpgradeRole(data.getRoomId(), data.getTicket(), jwtUser);
+        return new BaseResponse<>(result);
+    }
+
+    @RequestMapping(value = "/change-role", method = RequestMethod.POST)
+    public BaseResponse<Boolean> changeRole(@RequestBody ReqChangeRole data,
+                                            @RequestAttribute JwtUser jwtUser)
+            throws ApiException, Exception {
+        Boolean result = roomService.changeRole(data.getRoomId(), data.getUserId(), data.getRole(), jwtUser);
+        return new BaseResponse<>(result);
+    }
+
+    @RequestMapping(value = "/members/online-status", method = RequestMethod.POST)
+    public BaseResponse<Boolean> memberOnlineStatus(@RequestBody List<ReqMemberOnlineStatus> statusList,
+                                                    @RequestParam(value = "timestamp", required = false) String timestamp,
+                                                    @RequestParam(value = "nonce", required = false) String nonce,
+                                                    @RequestParam(value = "signature", required = false) String signature)
+            throws ApiException, Exception {
+        Boolean result = roomService.memberOnlineStatus(statusList, nonce, timestamp, signature);
+        return new BaseResponse<>(result);
+    }
+}

+ 36 - 0
mec-im/src/main/java/com/ym/controller/SensitiveController.java

@@ -0,0 +1,36 @@
+package com.ym.controller;
+
+import com.ym.service.ChatroomService;
+import io.rong.models.response.ListWordfilterResult;
+import io.rong.models.response.ResponseResult;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/sensitive")
+public class SensitiveController {
+
+    @Autowired
+    ChatroomService chatroomService;
+
+    @RequestMapping(value = "/add", method = RequestMethod.POST)
+    public ResponseResult add(@RequestBody String word) throws Exception {
+        return chatroomService.sensitiveAdd(word);
+    }
+
+    @RequestMapping(value = "/remove", method = RequestMethod.POST)
+    public ResponseResult remove(@RequestBody String word) throws Exception {
+        return chatroomService.sensitiveRemove(word);
+    }
+
+    @RequestMapping(value = "/getList", method = RequestMethod.POST)
+    public ListWordfilterResult getList() throws Exception {
+        return chatroomService.sensitiveGetList();
+    }
+}

+ 36 - 0
mec-im/src/main/java/com/ym/controller/SystemController.java

@@ -0,0 +1,36 @@
+package com.ym.controller;
+
+import com.ym.service.MessageService;
+import io.rong.models.Result;
+import io.rong.models.message.*;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/system")
+public class SystemController {
+
+    @Autowired
+    MessageService messageService;
+
+    @RequestMapping(value = "/send", method = RequestMethod.POST)
+    public Result send(@RequestBody SystemMessage systemMessage) throws Exception {
+        return messageService.systemSend(systemMessage);
+    }
+
+    @RequestMapping(value = "/broadcast", method = RequestMethod.POST)
+    public Result broadcast(@RequestBody BroadcastMessage broadcastMessage) throws Exception {
+        return messageService.systemBroadcast(broadcastMessage);
+    }
+
+    @RequestMapping(value = "/sendTemplate", method = RequestMethod.POST)
+    public Result sendTemplate(@RequestBody TemplateMessage templateMessage) throws Exception {
+        return messageService.systemSendTemplate(templateMessage);
+    }
+}

+ 38 - 0
mec-im/src/main/java/com/ym/controller/TagController.java

@@ -0,0 +1,38 @@
+package com.ym.controller;
+
+import com.ym.service.UserService;
+import io.rong.models.Result;
+import io.rong.models.user.BatchTagModel;
+import io.rong.models.user.GetTagModel;
+import io.rong.models.user.TagModel;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/tag")
+public class TagController {
+
+    @Autowired
+    UserService userService;
+
+    @RequestMapping(value = "/set", method = RequestMethod.POST)
+    public Result add(@RequestBody TagModel tagModel) throws Exception {
+        return userService.tagSet(tagModel);
+    }
+
+    @RequestMapping(value = "/batchSet", method = RequestMethod.POST)
+    public Result remove(@RequestBody BatchTagModel batchTagModel) throws Exception {
+        return userService.tagBatchSet(batchTagModel);
+    }
+
+    @RequestMapping(value = "/get", method = RequestMethod.POST)
+    public Result getList(GetTagModel getTagModel) throws Exception {
+        return userService.tagGet(getTagModel);
+    }
+}

+ 40 - 0
mec-im/src/main/java/com/ym/controller/UserController.java

@@ -0,0 +1,40 @@
+package com.ym.controller;
+
+import com.ym.common.ApiException;
+import com.ym.common.BaseResponse;
+import com.ym.common.JwtUser;
+import com.ym.pojo.ReqUserData;
+import com.ym.service.UserService;
+import io.rong.models.user.UserModel;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/user")
+public class UserController{
+
+    @Autowired
+    UserService userService;
+
+    @RequestMapping(value = "/refresh-token", method = RequestMethod.POST)
+    public BaseResponse<String> refreshToken(@RequestBody ReqUserData reqUserData)
+            throws ApiException, Exception {
+        String token = userService.refreshToken(reqUserData.getUserId(), reqUserData.getUserId());
+        BaseResponse<String> response = new BaseResponse<>();
+        response.setData(token);
+        return response;
+    }
+
+    @RequestMapping(value = "/register", method = RequestMethod.POST)
+    public Object register(@RequestBody UserModel userModel) throws Exception {
+        return userService.register(userModel);
+    }
+
+    @RequestMapping(value = "/update", method = RequestMethod.POST)
+    public Object update(@RequestBody UserModel userModel) throws Exception {
+        return userService.update(userModel);
+    }
+}

+ 53 - 0
mec-im/src/main/java/com/ym/controller/WhitelistController.java

@@ -0,0 +1,53 @@
+package com.ym.controller;
+
+import com.ym.service.ChatroomService;
+import io.rong.models.Result;
+import io.rong.models.chatroom.ChatroomModel;
+import io.rong.models.response.ChatroomWhitelistMsgResult;
+import io.rong.models.response.ResponseResult;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@RestController
+@RequestMapping("/whitelist")
+public class WhitelistController {
+
+    @Autowired
+    ChatroomService chatroomService;
+
+    @RequestMapping(value = "/user/add", method = RequestMethod.POST)
+    public ResponseResult userAdd(@RequestBody ChatroomModel chatroomModel) throws Exception {
+        return chatroomService.whitelistUserAdd(chatroomModel);
+    }
+
+    @RequestMapping(value = "/user/remove", method = RequestMethod.POST)
+    public ResponseResult userRemove(@RequestBody ChatroomModel chatroomModel) throws Exception {
+        return chatroomService.whitelistUserRemove(chatroomModel);
+    }
+
+    @RequestMapping(value = "/user/getList", method = RequestMethod.POST)
+    public Result userGetList(@RequestBody ChatroomModel chatroomModel) throws Exception {
+        return chatroomService.whitelistUserGetList(chatroomModel);
+    }
+
+    @RequestMapping(value = "/message/add", method = RequestMethod.POST)
+    public ResponseResult messageAdd(@RequestBody String[] objectNames) throws Exception {
+        return chatroomService.whitelistMessageAdd(objectNames);
+    }
+
+    @RequestMapping(value = "/message/remove", method = RequestMethod.POST)
+    public ResponseResult messageRemove(@RequestBody String[] objectNames) throws Exception {
+        return chatroomService.whitelistMessageRemove(objectNames);
+    }
+
+    @RequestMapping(value = "/message/getList", method = RequestMethod.POST)
+    public ChatroomWhitelistMsgResult messageGetList() throws Exception {
+        return chatroomService.whitelistMessageGetList();
+    }
+}

+ 34 - 0
mec-im/src/main/java/com/ym/dao/RoomDao.java

@@ -0,0 +1,34 @@
+package com.ym.dao;
+
+import com.ym.pojo.Room;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@Repository
+public interface RoomDao extends JpaRepository<Room, Long> {
+    public List<Room> findByRid(String rid);
+
+    @Transactional
+    @Modifying
+    public int deleteByRid(String rid);
+
+    public boolean existsByRid(String rid);
+
+    @Transactional
+    @Modifying
+    @Query(value = "update rongyun_room set display=?2 where rid=?1", nativeQuery = true)
+    public int updateDisplayByRid(String rid, String display);
+
+    @Transactional
+    @Modifying
+    @Query(value = "update rongyun_room set whiteboard_name_index=?2 where rid=?1", nativeQuery = true)
+    public int updateWhiteboardNameIndexByRid(String rid, int whiteboardNameIndex);
+}

+ 66 - 0
mec-im/src/main/java/com/ym/dao/RoomMemberDao.java

@@ -0,0 +1,66 @@
+package com.ym.dao;
+
+import com.ym.pojo.RoomMember;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@Repository
+public interface RoomMemberDao extends JpaRepository<RoomMember, Long> {
+    public List<RoomMember> findByRid(String rid);
+
+    public List<RoomMember> findByRidAndUid(String rid, String uid);
+
+    public List<RoomMember> findByRidAndRole(String rid, int role);
+
+    public List<RoomMember> findByUid(String uid);
+
+    public int countByRidAndRole(String rid, int role);
+
+    @Modifying
+    @Transactional
+    public int deleteByRid(String roomId);
+
+    @Query(value = "select count(*) from rongyun_room_member where rid=?1", nativeQuery = true)
+    public int countByRid(String roomId);
+
+    @Query(value = "select count(*) from rongyun_room_member where rid=?1 and role!=?2", nativeQuery = true)
+    public int countByRidAndExcludeRole(String roomId, int excludeRole);
+
+    @Transactional
+    @Modifying
+    @Query(value = "delete from rongyun_room_member where rid=?1 and uid=?2", nativeQuery = true)
+    public int deleteUserByRidAndUid(String rid, String uid);
+
+    @Transactional
+    @Modifying
+    @Query(value = "update rongyun_room_member set role=?3 where rid=?1 and uid=?2", nativeQuery = true)
+    public int updateRoleByRidAndUid(String rid, String uid, int role);
+
+    @Transactional
+    @Modifying
+    @Query(value = "update rongyun_room_member set role=?3 where rid=?1 and role=?2", nativeQuery = true)
+    public int updateRoleByRidAndRole(String rid, int role);
+
+    @Transactional
+    @Modifying
+    @Query(value = "update rongyun_room_member set camera=?3 where rid=?1 and uid=?2", nativeQuery = true)
+    public int updateCameraByRidAndUid(String rid, String uid, boolean camera);
+
+    @Transactional
+    @Modifying
+    @Query(value = "update rongyun_room_member set mic=?3 where rid=?1 and uid=?2", nativeQuery = true)
+    public int updateMicByRidAndUid(String rid, String uid, boolean mic);
+
+    public boolean existsByRidAndUid(String rid, String uid);
+
+    public boolean existsByRidAndRole(String rid, int role);
+
+}

+ 16 - 0
mec-im/src/main/java/com/ym/dao/SpeechDao.java

@@ -0,0 +1,16 @@
+package com.ym.dao;
+
+import com.ym.pojo.ScheduledTaskInfo;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * Created by weiqinxiao on 2019/3/13.
+ */
+@Repository
+public interface SpeechDao extends CrudRepository<ScheduledTaskInfo, String> {
+
+    List<ScheduledTaskInfo> findByTicket(String ticket);
+}

+ 24 - 0
mec-im/src/main/java/com/ym/dao/UserDao.java

@@ -0,0 +1,24 @@
+package com.ym.dao;
+
+import com.ym.pojo.UserInfo;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@Repository
+public interface UserDao extends JpaRepository<UserInfo, Long> {
+    //will query with "select * from TABLE where user_id = 'userId'"
+    public List<UserInfo> findByUid(String uid);
+
+    public List<UserInfo> findByName(String name);
+
+    @Transactional
+    @Modifying
+    public int deleteByUid(String uid);
+}

+ 36 - 0
mec-im/src/main/java/com/ym/dao/WhiteboardDao.java

@@ -0,0 +1,36 @@
+package com.ym.dao;
+
+import com.ym.pojo.Whiteboard;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@Repository
+public interface WhiteboardDao extends JpaRepository<Whiteboard, Long> {
+    public List<Whiteboard> findByRid(String rid);
+    public List<Whiteboard> findByRidAndCreator(String rid, String creator);
+    public List<Whiteboard> findByRidAndWbid(String rid, String wbid);
+
+    public int deleteByRid(String rid);
+
+    @Transactional
+    @Modifying
+    public int deleteByWbid(String wbid);
+
+    @Transactional
+    @Modifying
+    @Query(value = "delete from rongyun_whiteboard where rid=?1 and creator=?2", nativeQuery = true)
+    public int deleteByRidAndCreator(String rid, String creator);
+
+    @Transactional
+    @Modifying
+    @Query(value = "update rongyun_whiteboard set cur_pg=?3 where wbid=?2 and rid=?1", nativeQuery = true)
+    public int updatePageByRidAndWbid(String rid, String wbid, int page);
+}

+ 102 - 0
mec-im/src/main/java/com/ym/filter/GlobalExceptionHandlerAdvice.java

@@ -0,0 +1,102 @@
+package com.ym.filter;
+
+/**
+ * Created by weiqinxiao on 2019/2/26.
+ */
+
+import com.ym.common.ApiException;
+import com.ym.common.BaseResponse;
+import com.ym.common.ErrorEnum;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.ConversionNotSupportedException;
+import org.springframework.beans.TypeMismatchException;
+import org.springframework.dao.DataAccessException;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.http.converter.HttpMessageNotWritableException;
+import org.springframework.validation.BindException;
+import org.springframework.web.HttpMediaTypeNotAcceptableException;
+import org.springframework.web.HttpMediaTypeNotSupportedException;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.MissingPathVariableException;
+import org.springframework.web.bind.MissingServletRequestParameterException;
+import org.springframework.web.bind.ServletRequestBindingException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
+import org.springframework.web.multipart.support.MissingServletRequestPartException;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+@Slf4j
+@RestControllerAdvice
+public class GlobalExceptionHandlerAdvice {
+    @ResponseStatus(value = HttpStatus.OK)
+    @ExceptionHandler({MissingServletRequestParameterException.class,
+            IllegalArgumentException.class,
+            ServletRequestBindingException.class})
+    public BaseResponse<Object> handleRequestParameterException(Exception ex) {
+        logException(ex);
+        return getResponseData(ErrorEnum.ERR_REQUEST_PARA_ERR, ex.getMessage());
+    }
+
+    @ResponseStatus(value = HttpStatus.OK)
+    @ExceptionHandler({DataAccessException.class})
+    public BaseResponse<Object> handleDatabaseException(Exception ex) {
+        logException(ex);
+        return getResponseData(ErrorEnum.ERR_OTHER, ((DataAccessException) ex).getMostSpecificCause().getMessage());
+    }
+
+    // 400
+    @ResponseStatus(value = HttpStatus.OK)
+    @ExceptionHandler({TypeMismatchException.class,
+            HttpMessageNotReadableException.class,
+            MethodArgumentNotValidException.class,
+            MissingServletRequestPartException.class,
+            BindException.class})
+    public BaseResponse<Object> handleBadRequestException(Exception ex) {
+        logException(ex);
+        return getResponseData(ErrorEnum.ERR_BAD_REQUEST, ex.getMessage());
+    }
+
+    @ResponseStatus(value = HttpStatus.OK)
+    @ExceptionHandler({HttpRequestMethodNotSupportedException.class,
+            HttpMediaTypeNotSupportedException.class,
+            HttpMediaTypeNotAcceptableException.class,
+            MissingPathVariableException.class,
+            ConversionNotSupportedException.class,
+            HttpMessageNotWritableException.class,
+            IOException.class,
+            RuntimeException.class,
+            AsyncRequestTimeoutException.class})
+    public BaseResponse<Object> handleOtherException(Exception ex) {
+        logException(ex);
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter);
+        printWriter.print(ex);
+        return getResponseData(ErrorEnum.ERR_OTHER,  stringWriter.toString());
+    }
+
+    @ResponseStatus(value = HttpStatus.OK)
+    @ExceptionHandler(ApiException.class)
+    public BaseResponse<Object> handleMiMicroAPIException(ApiException ex) {
+        logException(ex);
+        return getResponseData(ex);
+    }
+
+    private void logException(Exception ex) {
+        log.error("caught exception:", ex);
+    }
+
+    private BaseResponse<Object> getResponseData(ApiException ex) {
+        return new BaseResponse<>(ex.getError(), ex.getErrDetail(), ex.getExtraData());
+    }
+
+    private BaseResponse<Object> getResponseData(ErrorEnum err, String errMsg) {
+        return new BaseResponse<>(err, errMsg, "");
+    }
+}

+ 237 - 0
mec-im/src/main/java/com/ym/http/HttpHelper.java

@@ -0,0 +1,237 @@
+package com.ym.http;
+
+import com.ym.config.IMProperties;
+import com.ym.utils.CodeUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.net.ssl.*;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.ProtocolException;
+import java.net.URL;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+@Slf4j
+@Component
+public class HttpHelper {
+    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)
+                        throws CertificateException {
+                }
+
+                public void checkServerTrusted(X509Certificate[] xcs, String string)
+                        throws CertificateException {
+                }
+
+                public X509Certificate[] getAcceptedIssuers() {
+                    return null;
+                }
+            };
+            sslCtx.init(null, new TrustManager[]{tm}, null);
+        } catch (Exception e) {
+            log.error("SSLContext exception:{}", e);
+        }
+
+        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
+
+            @Override
+            public boolean verify(String arg0, SSLSession arg1) {
+                return 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("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();
+        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);
+
+        return conn;
+    }
+
+    public HttpURLConnection createCommonPostHttpConnection(String host, String appKey,
+        String appSecret, String uri, String contentType)
+        throws MalformedURLException, IOException, ProtocolException {
+        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(APPKEY, appKey);
+        conn.setRequestProperty(NONCE, nonce);
+        conn.setRequestProperty(TIMESTAMP, timestamp);
+        conn.setRequestProperty(SIGNATURE, sign);
+        conn.setRequestProperty("Content-Type", contentType);
+        return conn;
+    }
+
+    public HttpURLConnection createCommonGetHttpConnection(String host, String appKey,
+        String appSecret, String uri, String contentType)
+        throws MalformedURLException, IOException, ProtocolException {
+        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("GET");
+        conn.setInstanceFollowRedirects(true);
+        conn.setConnectTimeout(10000);
+        conn.setReadTimeout(10000);
+
+        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 MalformedURLException, IOException, ProtocolException {
+        return createCommonGetHttpConnection(imProperties.getHost(),
+            imProperties.getAppKey(), imProperties.getSecret(), uri,
+            contentType);
+    }
+
+    public HttpURLConnection createIMPostHttpConnection(String uri, String contentType)
+        throws MalformedURLException, IOException, ProtocolException {
+        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, 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;
+        }
+
+        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);
+        return conn;
+    }
+}

+ 16 - 0
mec-im/src/main/java/com/ym/im/BaseMessage.java

@@ -0,0 +1,16 @@
+package com.ym.im;
+
+import com.alibaba.fastjson.JSONObject;
+
+/**
+ * Created by weiqinxiao on 2019/3/1.
+ */
+public abstract class BaseMessage {
+
+    public abstract String getObjectName();
+
+    @Override
+    public String toString() {
+        return JSONObject.toJSONString(this);
+    }
+}

+ 333 - 0
mec-im/src/main/java/com/ym/im/IMHelper.java

@@ -0,0 +1,333 @@
+package com.ym.im;
+
+import com.alibaba.fastjson.JSON;
+import com.ym.http.HttpHelper;
+import com.ym.pojo.IMApiResultInfo;
+import com.ym.pojo.IMTokenInfo;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.net.HttpURLConnection;
+import java.net.URLEncoder;
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+@Slf4j
+@Component
+public class IMHelper {
+    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.toString(), UTF8));
+        sb.append("&name=").append(URLEncoder.encode(name.toString(), UTF8));
+        sb.append("&portraitUri=").append(URLEncoder.encode(portraitUri.toString(), UTF8));
+        String body = sb.toString();
+        if (body.indexOf("&") == 0) {
+            body = body.substring(1, body.length());
+        }
+
+        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.toString(), UTF8));
+        sb.append("&groupName=").append(URLEncoder.encode(groupName.toString(), UTF8));
+        String body = sb.toString();
+        if (body.indexOf("&") == 0) {
+            body = body.substring(1, body.length());
+        }
+
+        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
+     **/
+    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.toString(), UTF8));
+        sb.append("&groupName=").append(URLEncoder.encode(groupName.toString(), UTF8));
+        String body = sb.toString();
+        if (body.indexOf("&") == 0) {
+            body = body.substring(1, body.length());
+        }
+
+        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.toString(), UTF8));
+        String body = sb.toString();
+        if (body.indexOf("&") == 0) {
+            body = body.substring(1, body.length());
+        }
+
+        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.toString(), UTF8));
+        sb.append("&groupId=").append(URLEncoder.encode(groupId.toString(), 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 个群组。(必传)
+     * @param pushContent:定义显示的 Push 内容,如果 objectName 为融云内置消息类型时,则发送后用户一定会收到 Push 信息. 如果为自定义消息,则
+     * pushContent 为自定义消息显示的 Push 内容,如果不传则用户不会收到 Push 通知。(可选)
+     * @param pushData:针对 iOS 平台为 Push 通知时附加到 payload 中,Android 客户端收到推送消息时对应字段名为 pushData。(可选)
+     * @param isPersisted:当前版本有新的自定义消息,而老版本没有该自定义消息时,老版本客户端收到消息后是否进行存储,0 表示为不存储、 1 表示为存储,默认为 1
+     * 存储消息。(可选)
+     * @param isCounted:当前版本有新的自定义消息,而老版本没有该自定义消息时,老版本客户端收到消息后是否进行未读消息计数,0 表示为不计数、 1 表示为计数,默认为 1
+     * 计数,未读消息数增加 1。(可选)
+     * @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.toString(), UTF8));
+
+        //定向消息
+        if (toUserId != null) {
+            sb.append("&toUserId=").append(URLEncoder.encode(toUserId.toString(), 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.toString(), UTF8));
+        }
+
+        if (pushData != null) {
+            sb.append("&pushData=").append(URLEncoder.encode(pushData.toString(), 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);
+    }
+}

+ 19 - 0
mec-im/src/main/java/com/ym/im/message/ApplyForSpeechMessage.java

@@ -0,0 +1,19 @@
+package com.ym.im.message;
+
+import com.ym.im.BaseMessage;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Created by weiqinxiao on 2019/3/13.
+ */
+public class ApplyForSpeechMessage extends BaseMessage {
+    private @Getter @Setter String reqUserId;
+    private @Getter @Setter String reqUserName;
+    private @Getter @Setter String ticket;
+
+    @Override
+    public String getObjectName() {
+        return "SC:RSMsg";
+    }
+}

+ 19 - 0
mec-im/src/main/java/com/ym/im/message/AssistantTransferMessage.java

@@ -0,0 +1,19 @@
+package com.ym.im.message;
+
+import com.ym.im.BaseMessage;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Created by weiqinxiao on 2019/3/6.
+ */
+public class AssistantTransferMessage extends BaseMessage {
+
+    private @Getter @Setter String opUserId;
+    private @Getter @Setter String toUserId;
+
+    @Override
+    public String getObjectName() {
+        return "SC:ATMsg";
+    }
+}

+ 27 - 0
mec-im/src/main/java/com/ym/im/message/ControlDeviceNotifyMessage.java

@@ -0,0 +1,27 @@
+package com.ym.im.message;
+
+import com.ym.im.BaseMessage;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Created by weiqinxiao on 2019/3/19.
+ */
+public class ControlDeviceNotifyMessage extends BaseMessage {
+
+    private  @Getter @Setter int action;
+    private  @Getter @Setter String ticket;
+    private @Getter @Setter int type;
+
+    private @Getter @Setter String opUserId;
+    private @Getter @Setter String opUserName;
+
+    public ControlDeviceNotifyMessage(int action) {
+        this.action = action;
+    }
+
+    @Override
+    public String getObjectName() {
+        return "SC:CDNMsg";
+    }
+}

+ 27 - 0
mec-im/src/main/java/com/ym/im/message/DeviceStateChangedMessage.java

@@ -0,0 +1,27 @@
+package com.ym.im.message;
+
+import com.ym.im.BaseMessage;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Created by weiqinxiao on 2019/3/7.
+ */
+public class DeviceStateChangedMessage extends BaseMessage {
+    private @Setter @Getter boolean enable;
+
+    private @Setter @Getter int type;
+
+    private @Setter @Getter String userId;
+    private @Setter @Getter String userName;
+
+    public DeviceStateChangedMessage(int type, boolean enable) {
+        this.type = type;
+        this.enable = enable;
+    }
+
+    @Override
+    public String getObjectName() {
+        return "SC:DRMsg";
+    }
+}

+ 22 - 0
mec-im/src/main/java/com/ym/im/message/DisplayMessage.java

@@ -0,0 +1,22 @@
+package com.ym.im.message;
+
+import com.ym.im.BaseMessage;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Created by weiqinxiao on 2019/3/7.
+ */
+public class DisplayMessage extends BaseMessage {
+
+    private @Setter @Getter String display;
+
+    public DisplayMessage(String display) {
+        this.display = display;
+    }
+
+    @Override
+    public String getObjectName() {
+        return "SC:DisplayMsg";
+    }
+}

+ 40 - 0
mec-im/src/main/java/com/ym/im/message/MemberChangedMessage.java

@@ -0,0 +1,40 @@
+package com.ym.im.message;
+
+import com.ym.im.BaseMessage;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/**
+ * Created by weiqinxiao on 2019/3/6.
+ */
+public class MemberChangedMessage extends BaseMessage {
+    public final static int Action_Join = 1;
+    public final static int Action_Leave = 2;
+    public final static int Action_Kick = 3;
+
+    @Getter @Setter private int action;
+
+    @Getter @Setter private String userId;
+    @Getter @Setter private String userName;
+    @Getter @Setter private boolean camera;
+    @Getter @Setter private boolean microphone;
+
+    private @Getter @Setter int role;
+
+    private @Getter @Setter Date timestamp;
+
+    public MemberChangedMessage(int action, String userId, int role) {
+        this.action = action;
+        this.userId = userId;
+        this.role = role;
+        this.camera = true;
+        this.microphone = true;
+    }
+
+    @Override
+    public String getObjectName() {
+        return "SC:RMCMsg";
+    }
+}

+ 38 - 0
mec-im/src/main/java/com/ym/im/message/RoleChangedMessage.java

@@ -0,0 +1,38 @@
+package com.ym.im.message;
+
+import com.ym.im.BaseMessage;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+/**
+ * Created by weiqinxiao on 2019/3/6.
+ */
+public class RoleChangedMessage extends BaseMessage {
+
+    private @Getter @Setter String opUserId;
+
+    private @Getter @Setter List<ChangedUser> users;
+
+
+    public RoleChangedMessage(String opUserId) {
+        this.opUserId = opUserId;
+    }
+
+    @Override
+    public String getObjectName() {
+        return "SC:RCMsg";
+    }
+
+    public static class ChangedUser {
+        @Getter @Setter String userId;
+        @Getter @Setter String userName;
+        @Getter @Setter int role;
+
+        public ChangedUser(String userId, int role) {
+            this.userId = userId;
+            this.role = role;
+        }
+    }
+}

+ 27 - 0
mec-im/src/main/java/com/ym/im/message/SpeechResultMessage.java

@@ -0,0 +1,27 @@
+package com.ym.im.message;
+
+import com.ym.im.BaseMessage;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Created by weiqinxiao on 2019/3/13.
+ */
+public class SpeechResultMessage extends BaseMessage {
+    public final static int Action_Approve = 1, Action_Reject = 2;
+    private @Getter @Setter String opUserId;
+    private @Getter @Setter String opUserName;
+    private @Getter @Setter String reqUserId;
+    private @Getter @Setter String reqUserName;
+    private @Getter @Setter int role;
+    private @Getter @Setter int action;
+
+    public SpeechResultMessage(int action) {
+        this.action = action;
+    }
+
+    @Override
+    public String getObjectName() {
+        return "SC:SRMsg";
+    }
+}

+ 19 - 0
mec-im/src/main/java/com/ym/im/message/TicketExpiredMessage.java

@@ -0,0 +1,19 @@
+package com.ym.im.message;
+
+import com.ym.im.BaseMessage;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Created by weiqinxiao on 2019/3/15.
+ */
+public class TicketExpiredMessage extends BaseMessage {
+    private @Getter @Setter String ticket;
+    private @Getter @Setter String fromUserId;//请求发言者
+    private @Getter @Setter String toUserId;//处理请求者
+
+    @Override
+    public String getObjectName() {
+        return "SC:TEMsg";
+    }
+}

+ 28 - 0
mec-im/src/main/java/com/ym/im/message/TurnPageMessage.java

@@ -0,0 +1,28 @@
+package com.ym.im.message;
+
+import com.ym.im.BaseMessage;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Created by weiqinxiao on 2019/3/13.
+ */
+public class TurnPageMessage extends BaseMessage {
+
+    private @Setter @Getter String whiteboardId;
+
+    private @Setter @Getter String userId;
+
+    private @Setter @Getter int curPg;
+
+    public TurnPageMessage(String whiteboardId, String userId, int curPg) {
+        this.whiteboardId = whiteboardId;
+        this.userId = userId;
+        this.curPg = curPg;
+    }
+
+    @Override
+    public String getObjectName() {
+        return "SC:WBTPMsg";
+    }
+}

+ 27 - 0
mec-im/src/main/java/com/ym/im/message/UpgradeRoleMessage.java

@@ -0,0 +1,27 @@
+package com.ym.im.message;
+
+import com.ym.im.BaseMessage;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Created by weiqinxiao on 2019/3/18.
+ */
+public class UpgradeRoleMessage extends BaseMessage {
+
+    private @Getter @Setter String ticket;
+    private @Getter @Setter String opUserId;
+    private @Getter @Setter String opUserName;
+
+    private @Getter @Setter int action;
+    private @Getter @Setter int role;
+
+    public UpgradeRoleMessage(int action) {
+        this.action = action;
+    }
+
+    @Override
+    public String getObjectName() {
+        return "SC:IURMsg";
+    }
+}

+ 26 - 0
mec-im/src/main/java/com/ym/im/message/WhiteboardMessage.java

@@ -0,0 +1,26 @@
+package com.ym.im.message;
+
+import com.ym.im.BaseMessage;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Created by weiqinxiao on 2019/3/8.
+ */
+public class WhiteboardMessage extends BaseMessage {
+    public final static int Create = 1, Delete = 2;
+
+    private @Setter @Getter int action;
+
+    private @Setter @Getter String whiteboardId;
+    private @Setter @Getter String whiteboardName;
+
+    public WhiteboardMessage(int action) {
+        this.action = action;
+    }
+
+    @Override
+    public String getObjectName() {
+        return "SC:WBMsg";
+    }
+}

+ 130 - 0
mec-im/src/main/java/com/ym/job/ScheduleManager.java

@@ -0,0 +1,130 @@
+package com.ym.job;
+
+import com.ym.common.ApiException;
+import com.ym.common.ErrorEnum;
+import com.ym.config.RoomProperties;
+import com.ym.config.WhiteBoardProperties;
+import com.ym.dao.RoomDao;
+import com.ym.dao.RoomMemberDao;
+import com.ym.im.IMHelper;
+import com.ym.im.message.TicketExpiredMessage;
+import com.ym.pojo.ScheduledTaskInfo;
+import com.ym.service.RoomService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.SchedulingConfigurer;
+import org.springframework.scheduling.config.ScheduledTask;
+import org.springframework.scheduling.config.ScheduledTaskRegistrar;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Created by weiqinxiao on 2019/3/15.
+ */
+@Slf4j
+@Service
+public class ScheduleManager implements SchedulingConfigurer {
+    private ScheduledTaskRegistrar taskRegistrar;
+
+    @Autowired
+    RoomProperties roomProperties;
+
+    @Autowired
+    WhiteBoardProperties whiteBoardProperties;
+
+    @Autowired
+    IMHelper imHelper;
+
+    @Autowired
+    RoomMemberDao roomMemberDao;
+
+    @Autowired
+    RoomDao roomDao;
+
+    @Autowired
+    RoomService roomService;
+
+    private ConcurrentHashMap<String, ScheduledTask> schedulingTasks = new ConcurrentHashMap<>();
+    private ConcurrentHashMap<String, ScheduledTask> roomCacheTasks = new ConcurrentHashMap<>();
+    private ConcurrentHashMap<String, Date> userIMOfflineMap = new ConcurrentHashMap<>();
+    private ScheduledDelayTask userIMOfflineKickTask = new ScheduledDelayTask(new Runnable() {
+        @Override
+        public void run() {
+            for (Map.Entry<String, Date> entry : userIMOfflineMap.entrySet()) {
+                long currentTimeMillis = System.currentTimeMillis();
+                log.info("userIMOfflineKickTask entry={}, currentTimeMillis={}", entry.getValue().getTime(), currentTimeMillis);
+                if (currentTimeMillis - entry.getValue().getTime() > roomProperties.getUserIMOfflineKickTtl()) {
+                    userIMOfflineMap.remove(entry.getKey());
+                    roomService.userIMOfflineKick(entry.getKey());
+                }
+            }
+        }
+    }, 60000, 60000, null);
+
+    @Override
+    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
+        scheduledTaskRegistrar.scheduleFixedDelayTask(userIMOfflineKickTask);
+        this.taskRegistrar = scheduledTaskRegistrar;
+        log.info("config schedule: taskTtl = {}, roomTtl={}, roomMaxCount={}, ", roomProperties.getTaskTtl(), roomProperties.getRoomTtl(), roomProperties.getMaxCount());
+        log.info("config whiteboard: host={}", whiteBoardProperties.getHost());
+    }
+
+    public void userIMOffline(String userId) {
+        userIMOfflineMap.put(userId, new Date());
+    }
+
+    public void userIMOnline(String userId) {
+        userIMOfflineMap.remove(userId);
+    }
+
+    public void addExpiredTask(RoomService roomService, String roomId) {
+        log.info("addExpiredTask destroyRoom: {}", roomId);
+        ScheduledTask task = taskRegistrar.scheduleFixedDelayTask(new ScheduledDelayTask(new Runnable() {
+            @Override
+            public void run() {
+                ScheduledTask task = roomCacheTasks.remove(roomId);
+                task.cancel();
+                roomService.destroyRoom(roomId);
+
+            }
+        }, roomProperties.getRoomTtl() * 10, roomProperties.getRoomTtl(), null));
+        roomCacheTasks.put(roomId, task);
+    }
+
+    public void addTask(ScheduledTaskInfo task) {
+        log.info("add speech task: {}", task);
+        schedulingTasks.put(task.getTicket(), taskRegistrar.scheduleFixedDelayTask(new ScheduledDelayTask(new Runnable() {
+            @Override
+            public void run() {
+                log.info("speech task expired, execute task: {}", task);
+                TicketExpiredMessage msg = new TicketExpiredMessage();
+                msg.setFromUserId(task.getApplyUserId());
+                msg.setToUserId(task.getTargetUserId());
+                msg.setTicket(task.getTicket());
+                try {
+                    imHelper.publishMessage(task.getTargetUserId(), task.getRoomId(), msg);
+                } catch (Exception e) {
+                    log.error("msg send error: {}", e.getMessage());
+                }
+                ScheduledTask scheduledTask = schedulingTasks.remove(task.getTicket());
+                scheduledTask.cancel();
+            }
+        }, roomProperties.getTaskTtl() * 60, roomProperties.getTaskTtl(), task)));
+    }
+
+    public ScheduledTaskInfo executeTask(String key) {
+        ScheduledTask scheduledTask = schedulingTasks.remove(key);
+        if (scheduledTask == null) {
+            log.error("task not exist: key={}", key);
+            throw new ApiException(ErrorEnum.ERR_APPLY_TICKET_INVALID);
+        }
+        ScheduledDelayTask task = (ScheduledDelayTask)scheduledTask.getTask();
+        ScheduledTaskInfo taskInfo = task.getScheduledTaskInfo();
+        scheduledTask.cancel();
+        log.info("execute speech task: {}", taskInfo);
+        return taskInfo;
+    }
+}

+ 18 - 0
mec-im/src/main/java/com/ym/job/ScheduledDelayTask.java

@@ -0,0 +1,18 @@
+package com.ym.job;
+
+import com.ym.pojo.ScheduledTaskInfo;
+import lombok.Getter;
+import org.springframework.scheduling.config.FixedDelayTask;
+
+/**
+ * Created by weiqinxiao on 2019/3/15.
+ */
+public class ScheduledDelayTask extends FixedDelayTask {
+    private @Getter
+    ScheduledTaskInfo scheduledTaskInfo;
+
+    public ScheduledDelayTask(Runnable runnable, long interval, long initialDelay, ScheduledTaskInfo scheduledTaskInfo) {
+        super(runnable, interval, initialDelay);
+        this.scheduledTaskInfo = scheduledTaskInfo;
+    }
+}

+ 11 - 0
mec-im/src/main/java/com/ym/pojo/ActionEnum.java

@@ -0,0 +1,11 @@
+package com.ym.pojo;
+
+/**
+ * Created by weiqinxiao on 2019/3/19.
+ */
+public enum ActionEnum {
+    None,
+    Invite,
+    Reject,
+    Approve,
+}

+ 20 - 0
mec-im/src/main/java/com/ym/pojo/ControlDeviceTaskInfo.java

@@ -0,0 +1,20 @@
+package com.ym.pojo;
+
+import lombok.Data;
+
+/**
+ * Created by weiqinxiao on 2019/3/13.
+ */
+@Data
+public class ControlDeviceTaskInfo extends ScheduledTaskInfo {
+    private DeviceTypeEnum typeEnum;
+    private boolean onOff;
+
+    @Override
+    public String toString() {
+        return "ControlDeviceTaskInfo{" +
+                "typeEnum=" + typeEnum +
+                ", onOff=" + onOff +
+                '}' + super.toString();
+    }
+}

+ 9 - 0
mec-im/src/main/java/com/ym/pojo/DeviceTypeEnum.java

@@ -0,0 +1,9 @@
+package com.ym.pojo;
+
+/**
+ * Created by weiqinxiao on 2019/3/19.
+ */
+public enum DeviceTypeEnum {
+    Microphone,
+    Camera,
+}

+ 18 - 0
mec-im/src/main/java/com/ym/pojo/IMApiResultInfo.java

@@ -0,0 +1,18 @@
+package com.ym.pojo;
+
+import lombok.Data;
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+@Data
+public class IMApiResultInfo {
+    // 返回码,200 为正常。
+    Integer code;
+    // 错误信息。
+    String errorMessage;
+
+    public boolean isSuccess() {
+        return code == 200;
+    }
+}

+ 22 - 0
mec-im/src/main/java/com/ym/pojo/IMTokenInfo.java

@@ -0,0 +1,22 @@
+package com.ym.pojo;
+
+import lombok.Data;
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+@Data
+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;
+    }
+}

+ 13 - 0
mec-im/src/main/java/com/ym/pojo/ReqChangeRole.java

@@ -0,0 +1,13 @@
+package com.ym.pojo;
+
+import lombok.Data;
+
+/**
+ * Created by weiqinxiao on 2019/3/19.
+ */
+@Data
+public class ReqChangeRole {
+    String userId;
+    int role;
+    String roomId;
+}

+ 20 - 0
mec-im/src/main/java/com/ym/pojo/ReqChangeUserRoleData.java

@@ -0,0 +1,20 @@
+package com.ym.pojo;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * Created by weiqinxiao on 2019/3/4.
+ */
+@Data
+public class ReqChangeUserRoleData {
+    private String roomId;
+    private List<ChangedUser> users;
+
+    @Data
+    public static class ChangedUser {
+        String userId;
+        int role;
+    }
+}

+ 15 - 0
mec-im/src/main/java/com/ym/pojo/ReqDeviceControlData.java

@@ -0,0 +1,15 @@
+package com.ym.pojo;
+
+import lombok.Data;
+
+/**
+ * Created by weiqinxiao on 2019/3/7.
+ */
+@Data
+public class ReqDeviceControlData {
+    private Boolean cameraOn;
+    private Boolean microphoneOn;
+    private String roomId;
+    private String userId;
+    private String ticket;
+}

+ 14 - 0
mec-im/src/main/java/com/ym/pojo/ReqDisplayData.java

@@ -0,0 +1,14 @@
+package com.ym.pojo;
+
+import lombok.Data;
+
+/**
+ * Created by weiqinxiao on 2019/3/1.
+ */
+@Data
+public class ReqDisplayData {
+    private String roomId;
+    private int type;
+    private String userId;//即将展示的对应人的 userId
+    private String uri;
+}

+ 11 - 0
mec-im/src/main/java/com/ym/pojo/ReqMemberOnlineStatus.java

@@ -0,0 +1,11 @@
+package com.ym.pojo;
+
+import lombok.Data;
+
+@Data
+public class ReqMemberOnlineStatus {
+    private String userId;
+    private String status;//状态:0:online 在线、1:offline 离线、2:logout 登出。
+    private String os;//操作系统,iOS 、 Android 或 Websocket,用户上线时同步。
+    private String time;
+}

+ 12 - 0
mec-im/src/main/java/com/ym/pojo/ReqSpeechData.java

@@ -0,0 +1,12 @@
+package com.ym.pojo;
+
+import lombok.Data;
+
+/**
+ * Created by weiqinxiao on 2019/3/13.
+ */
+@Data
+public class ReqSpeechData {
+    String roomId;
+    String ticket;
+}

+ 14 - 0
mec-im/src/main/java/com/ym/pojo/ReqUpgradeRoleData.java

@@ -0,0 +1,14 @@
+package com.ym.pojo;
+
+import lombok.Data;
+
+/**
+ * Created by weiqinxiao on 2019/3/13.
+ */
+@Data
+public class ReqUpgradeRoleData {
+    private String roomId;
+    private String userId;
+    private String ticket;
+    private int role;
+}

+ 15 - 0
mec-im/src/main/java/com/ym/pojo/ReqUserData.java

@@ -0,0 +1,15 @@
+package com.ym.pojo;
+
+import lombok.Data;
+
+/**
+ * Created by weiqinxiao on 2019/3/1.
+ */
+@Data
+public class ReqUserData {
+    private String userName;
+    private String roomId;
+    private String userId;
+    private boolean audience;
+	private boolean disableCamera;
+}

+ 13 - 0
mec-im/src/main/java/com/ym/pojo/ReqWhiteboardData.java

@@ -0,0 +1,13 @@
+package com.ym.pojo;
+
+import lombok.Data;
+
+/**
+ * Created by weiqinxiao on 2019/3/7.
+ */
+@Data
+public class ReqWhiteboardData {
+    private String roomId;
+    private String whiteboardId;
+    private int page;
+}

+ 37 - 0
mec-im/src/main/java/com/ym/pojo/RoleEnum.java

@@ -0,0 +1,37 @@
+package com.ym.pojo;
+
+import com.ym.common.ApiException;
+import com.ym.common.ErrorEnum;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+public enum RoleEnum {
+    RoleAssistant("RoleAssistant", 1),
+    RoleTeacher("RoleTeacher", 2),
+    RoleStudent("RoleStudent", 3),
+    RoleAudience("RoleAudience", 4);
+
+    private @Getter
+    @Setter(AccessLevel.PRIVATE) String msg;
+    private @Getter
+    @Setter(AccessLevel.PRIVATE) int value;
+
+    RoleEnum(String msg, int value) {
+        this.msg = msg;
+        this.value = value;
+    }
+
+    public static RoleEnum getEnumByValue(int v) {
+        for(RoleEnum item : RoleEnum.values()) {
+            if(item.getValue() == v) {
+                return item;
+            }
+        }
+
+        throw new ApiException(ErrorEnum.ERR_REQUEST_PARA_ERR, v + " not valid role");
+    }
+}

+ 40 - 0
mec-im/src/main/java/com/ym/pojo/Room.java

@@ -0,0 +1,40 @@
+package com.ym.pojo;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+@Entity
+@Table(name = "rongyun_room")
+public class Room implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private long id;
+
+    private @Getter @Setter String rid;
+    private @Getter @Setter String name;
+    private @Getter @Setter String portrait;
+    private @Getter @Setter Date createDt;
+    private @Getter @Setter String display;
+    private @Getter @Setter int whiteboardNameIndex;
+
+    @Override
+    public String toString() {
+        return "Room{" +
+                "rid='" + rid + '\'' +
+                ", name='" + name + '\'' +
+                ", portrait='" + portrait + '\'' +
+                ", createDt=" + createDt +
+                ", display='" + display + '\'' +
+                ", whiteboardNameIndex='" + whiteboardNameIndex + '\'' +
+                '}';
+    }
+}

+ 51 - 0
mec-im/src/main/java/com/ym/pojo/RoomMember.java

@@ -0,0 +1,51 @@
+package com.ym.pojo;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.persistence.*;
+import java.util.Date;
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+@Entity
+@Table(name = "rongyun_room_member")
+public class RoomMember {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private long id;
+
+    private @Getter @Setter String uid;
+    private @Getter @Setter String rid;
+    private @Getter @Setter int role;
+    private @Getter @Setter Date joinDt;
+    private @Getter @Setter String name;
+    private @Getter @Setter boolean camera = true;
+    private @Getter @Setter boolean mic = true;
+
+    public RoomMember() {
+    }
+
+    public RoomMember(String uid, String rid) {
+        this.uid = uid;
+        this.rid = rid;
+    }
+
+    @Override
+    public String toString() {
+        return "RoomMember{" +
+                "uid='" + uid + '\'' +
+                ", rid='" + rid + '\'' +
+                ", role=" + role +
+                ", joinDt=" + joinDt +
+                ", name='" + name + '\'' +
+                ", camera=" + camera +
+                ", mic=" + mic +
+                '}';
+    }
+//
+//    @ManyToOne(fetch = FetchType.LAZY)
+//    @JoinColumn(name = "room_id", referencedColumnName = "rid")
+//    private @Getter @Setter Room room;
+}

+ 63 - 0
mec-im/src/main/java/com/ym/pojo/RoomResult.java

@@ -0,0 +1,63 @@
+package com.ym.pojo;
+
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+public class RoomResult {
+    private @Getter @Setter String roomId;
+    private @Setter Date startTime;
+    private @Getter @Setter String imToken;
+    private @Getter @Setter String authorization;
+    private @Getter List<MemberResult> members = new ArrayList<>();
+    private @Getter @Setter String display;
+    private @Getter @Setter List<WhiteboardResult> whiteboards = new ArrayList<>();
+    private @Getter @Setter MemberResult userInfo;
+
+    @Data
+    public static class MemberResult {
+        String userId;
+        String userName;
+        int role;
+        Date joinTime;
+        boolean camera;
+        boolean microphone;
+    }
+
+    @Data
+    public static class WhiteboardResult {
+        String whiteboardId;
+        String name;
+        int curPg;
+    }
+
+    public void setMembers(List<RoomMember> roomMemberList) {
+        for (RoomMember member : roomMemberList) {
+            MemberResult result = new MemberResult();
+            result.setUserId(member.getUid());
+            result.setJoinTime(member.getJoinDt());
+            result.setRole(member.getRole());
+            result.setMicrophone(member.isMic());
+            result.setCamera(member.isCamera());
+            result.setUserName(member.getName());
+            members.add(result);
+        }
+    }
+
+    public void setWhiteboards(List<Whiteboard> whiteboardList) {
+        for (Whiteboard wb : whiteboardList) {
+            WhiteboardResult r = new WhiteboardResult();
+            r.setName(wb.getName());
+            r.setWhiteboardId(wb.getWbid());
+            r.setCurPg(wb.getCurPg());
+            whiteboards.add(r);
+        }
+    }
+}

+ 24 - 0
mec-im/src/main/java/com/ym/pojo/ScheduledTaskInfo.java

@@ -0,0 +1,24 @@
+package com.ym.pojo;
+
+import lombok.Data;
+
+/**
+ * Created by weiqinxiao on 2019/3/13.
+ */
+@Data
+public class ScheduledTaskInfo {
+    private String ticket;
+    private String roomId;
+    private String applyUserId;
+    private String targetUserId;
+
+    @Override
+    public String toString() {
+        return "{" +
+                "ticket='" + ticket + '\'' +
+                ", roomId='" + roomId + '\'' +
+                ", applyUserId='" + applyUserId + '\'' +
+                ", targetUserId='" + targetUserId + '\'' +
+                '}';
+    }
+}

+ 18 - 0
mec-im/src/main/java/com/ym/pojo/UpgradeRoleTaskInfo.java

@@ -0,0 +1,18 @@
+package com.ym.pojo;
+
+import lombok.Data;
+
+/**
+ * Created by weiqinxiao on 2019/3/18.
+ */
+@Data
+public class UpgradeRoleTaskInfo extends ScheduledTaskInfo {
+    private RoleEnum role;
+
+    @Override
+    public String toString() {
+        return "UpgradeRoleTaskInfo{" +
+                "role=" + role +
+                '}' + super.toString();
+    }
+}

+ 21 - 0
mec-im/src/main/java/com/ym/pojo/UserInfo.java

@@ -0,0 +1,21 @@
+package com.ym.pojo;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.persistence.*;
+import java.util.Date;
+
+@Entity
+@Table(name = "rongyun_user")
+public class UserInfo {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    private @Getter @Setter String uid;
+    private @Getter @Setter String name;
+    private @Getter @Setter String portrait;
+    private @Getter @Setter Date createDt;
+    private @Getter @Setter Date updateDt;
+}

+ 17 - 0
mec-im/src/main/java/com/ym/pojo/WhiteBoardApiResultInfo.java

@@ -0,0 +1,17 @@
+package com.ym.pojo;
+
+import lombok.Data;
+
+/**
+ * Created by weiqinxiao on 2019/3/7.
+ */
+@Data
+public class WhiteBoardApiResultInfo {
+    private int code;
+    private String msg;
+    private String data;
+
+    public boolean isSuccess() {
+        return code == 200;
+    }
+}

+ 30 - 0
mec-im/src/main/java/com/ym/pojo/Whiteboard.java

@@ -0,0 +1,30 @@
+package com.ym.pojo;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+@Entity
+@Table(name = "rongyun_whiteboard")
+public class Whiteboard implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private @Getter long id;
+
+    private @Getter @Setter String rid;
+    private @Getter @Setter String wbid;
+    private @Getter @Setter String wbRoom;
+    private @Getter @Setter String name;
+    private @Getter @Setter String creator;
+    private @Getter @Setter Date createDt;
+    private @Getter @Setter int pgCount;
+    private @Getter @Setter int curPg;
+}

+ 78 - 0
mec-im/src/main/java/com/ym/service/ChatroomService.java

@@ -0,0 +1,78 @@
+package com.ym.service;
+
+import io.rong.models.Result;
+import io.rong.models.chatroom.ChatroomMember;
+import io.rong.models.chatroom.ChatroomModel;
+import io.rong.models.response.*;
+
+public interface ChatroomService {
+    //聊天室
+    Result chatroomCreate(ChatroomModel[] chatroomModels) throws Exception;
+
+    Result chatroomDestory(ChatroomModel chatroomModel) throws Exception;
+
+    Result chatroomGet(ChatroomModel chatroomModel) throws Exception;
+
+    CheckChatRoomUserResult chatroomIsExit(ChatroomMember chatroomMember) throws Exception;
+
+
+    //聊天室模块  封禁
+    Result chatroomBlockAdd(ChatroomModel chatroomModel) throws Exception;
+
+    Result chatroomBlockRemove(ChatroomModel chatroomModel) throws Exception;
+
+    Result chatroomBlockGetList(String chatroomId) throws Exception;
+
+
+    //聊天室模块,成员禁言
+    ResponseResult chatroomMuteMembersAdd(ChatroomModel chatroomModel) throws Exception;
+
+    ResponseResult chatroomMuteMembersRemove(ChatroomModel chatroomModel) throws Exception;
+
+    ListGagChatroomUserResult chatroomMuteMembersGetList(ChatroomModel chatroomModel) throws Exception;
+
+
+    //消息降级
+    ChatroomDemotionMsgResult demotionGetList() throws Exception;
+
+    ResponseResult demotionRemove(String[] objectNames) throws Exception;
+
+    ResponseResult demotionAdd(String[] objectNames) throws Exception;
+
+
+    //消息分发
+    ResponseResult distributeStop(ChatroomModel chatroomModel) throws Exception;
+
+    ResponseResult distributeResume(ChatroomModel chatroomModel) throws Exception;
+
+
+    //保活
+    ResponseResult keepAliveAdd(ChatroomModel chatroomModel) throws Exception;
+
+    ResponseResult keepAliveRemove(ChatroomModel chatroomModel) throws Exception;
+
+    ChatroomKeepaliveResult keepAliveGetList() throws Exception;
+
+
+    //白名单
+    ResponseResult whitelistUserAdd(ChatroomModel chatroomModel) throws Exception;
+
+    ResponseResult whitelistUserRemove(ChatroomModel chatroomModel) throws Exception;
+
+    Result whitelistUserGetList(ChatroomModel chatroomModel) throws Exception;
+
+    ResponseResult whitelistMessageAdd(String[] objectNames) throws Exception;
+
+    ResponseResult whitelistMessageRemove(String[] objectNames) throws Exception;
+
+    ChatroomWhitelistMsgResult whitelistMessageGetList() throws Exception;
+
+
+    //敏感词
+    ResponseResult sensitiveAdd(String word) throws Exception;
+
+    ResponseResult sensitiveRemove(String word) throws Exception;
+
+    ListWordfilterResult sensitiveGetList() throws Exception;
+
+}

+ 55 - 0
mec-im/src/main/java/com/ym/service/GroupService.java

@@ -0,0 +1,55 @@
+package com.ym.service;
+
+import io.rong.models.Result;
+import io.rong.models.conversation.ConversationModel;
+import io.rong.models.group.GroupModel;
+import io.rong.models.group.UserGroup;
+
+public interface GroupService {
+
+    //群组模块   群组
+    Result groupSync(UserGroup userGroup) throws Exception;
+
+    Result groupCreate(GroupModel groupModel) throws Exception;
+
+    Result groupGet(GroupModel groupModel) throws Exception;
+
+    Result groupUpdate(GroupModel groupModel) throws Exception;
+
+    Result groupJoin(GroupModel groupModel) throws Exception;
+
+    Result groupQuit(GroupModel groupModel) throws Exception;
+
+    Result groupDismiss(GroupModel groupModel) throws Exception;
+
+
+    //成员禁言
+    Result muteMembersAdd(GroupModel groupModel) throws Exception;
+
+    Result muteMembersRemove(GroupModel groupModel) throws Exception;
+
+    Result muteMembersGetList(String groupId) throws Exception;
+
+
+    //整体禁言
+    Result muteAllMembersAdd(String[] groupIds) throws Exception;
+
+    Result muteAllMembersRemove(String[] groupIds) throws Exception;
+
+    Result muteAllMembersGetList() throws Exception;
+
+
+    //群禁言用户白名单服务
+    Result muteWhiteAdd(GroupModel groupModel) throws Exception;
+
+    Result muteWhiteRemove(GroupModel groupModel) throws Exception;
+
+    Result muteWhiteGetList(String groupId) throws Exception;
+
+
+
+    //会话模块   会话
+    Result conversationMute(ConversationModel conversationModel) throws Exception;
+
+    Result conversationUnmute(ConversationModel conversationModel) throws Exception;
+}

+ 190 - 0
mec-im/src/main/java/com/ym/service/Impl/ChatroomServiceImpl.java

@@ -0,0 +1,190 @@
+package com.ym.service.Impl;
+
+import com.ym.service.ChatroomService;
+import io.rong.RongCloud;
+import io.rong.methods.chatroom.Chatroom;
+import io.rong.methods.chatroom.block.Block;
+import io.rong.methods.chatroom.demotion.Demotion;
+import io.rong.methods.chatroom.distribute.Distribute;
+import io.rong.methods.chatroom.keepalive.Keepalive;
+import io.rong.methods.chatroom.mute.MuteMembers;
+import io.rong.methods.chatroom.whitelist.Whitelist;
+import io.rong.methods.sensitive.Wordfilter;
+import io.rong.models.Result;
+import io.rong.models.chatroom.ChatroomMember;
+import io.rong.models.chatroom.ChatroomModel;
+import io.rong.models.response.*;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+public class ChatroomServiceImpl implements ChatroomService {
+    @Value("${cn.rongcloud.im.appkey}")
+    private String appKey;
+    @Value("${cn.rongcloud.im.secret}")
+    private String appSecret;
+
+    private Chatroom getChatroom(){
+        RongCloud rongCloud = RongCloud.getInstance(appKey, appSecret);
+        return new Chatroom(appKey,appSecret,rongCloud);
+    }
+    private Block getBlock(){
+        return new Block(appKey,appSecret);
+    }
+    private MuteMembers getMuteMembers(){
+        RongCloud rongCloud = RongCloud.getInstance(appKey, appSecret);
+        return new MuteMembers(appKey,appSecret,rongCloud);
+    }
+    private Whitelist getWhitelist(){
+        return new Whitelist(appKey,appSecret);
+    }
+    private Keepalive getKeepalive(){
+        return new Keepalive(appKey,appSecret);
+    }
+    private Distribute getDistribute(){
+        return new Distribute(appKey,appSecret);
+    }
+    private Demotion getDemotion(){
+        return new Demotion(appKey,appSecret);
+    }
+    private Wordfilter getWordfilter(){
+        return new Wordfilter(appKey,appSecret);
+    }
+
+    @Override
+    public Result chatroomCreate(ChatroomModel[] chatroomModels) throws Exception {
+        return getChatroom().create(chatroomModels);
+    }
+
+    @Override
+    public Result chatroomDestory(ChatroomModel chatroomModel) throws Exception {
+        return getChatroom().destroy(chatroomModel);
+    }
+
+    @Override
+    public Result chatroomGet(ChatroomModel chatroomModel) throws Exception {
+        return getChatroom().get(chatroomModel);
+    }
+
+    @Override
+    public CheckChatRoomUserResult chatroomIsExit(ChatroomMember chatroomMember) throws Exception {
+        return getChatroom().isExist(chatroomMember);
+    }
+
+    @Override
+    public Result chatroomBlockAdd(ChatroomModel chatroomModel) throws Exception {
+        return getBlock().add(chatroomModel);
+    }
+
+    @Override
+    public Result chatroomBlockRemove(ChatroomModel chatroomModel) throws Exception {
+        return getBlock().remove(chatroomModel);
+    }
+
+    @Override
+    public Result chatroomBlockGetList(String chatroomId) throws Exception {
+        return getBlock().getList(chatroomId);
+    }
+
+    @Override
+    public ResponseResult chatroomMuteMembersAdd(ChatroomModel chatroomModel) throws Exception {
+        return getMuteMembers().add(chatroomModel);
+    }
+
+    @Override
+    public ResponseResult chatroomMuteMembersRemove(ChatroomModel chatroomModel) throws Exception {
+        return getMuteMembers().remove(chatroomModel);
+    }
+
+    @Override
+    public ListGagChatroomUserResult chatroomMuteMembersGetList(ChatroomModel chatroomModel) throws Exception {
+        return getMuteMembers().getList(chatroomModel);
+    }
+
+    @Override
+    public ChatroomDemotionMsgResult demotionGetList() throws Exception {
+        return getDemotion().getList();
+    }
+
+    @Override
+    public ResponseResult demotionRemove(String[] objectNames) throws Exception {
+        return getDemotion().remove(objectNames);
+    }
+
+    @Override
+    public ResponseResult demotionAdd(String[] objectNames) throws Exception {
+        return getDemotion().add(objectNames);
+    }
+
+    @Override
+    public ResponseResult distributeStop(ChatroomModel chatroomModel) throws Exception {
+        return getDistribute().stop(chatroomModel);
+    }
+
+    @Override
+    public ResponseResult distributeResume(ChatroomModel chatroomModel) throws Exception {
+        return getDistribute().resume(chatroomModel);
+    }
+
+    @Override
+    public ResponseResult keepAliveAdd(ChatroomModel chatroomModel) throws Exception {
+        return getKeepalive().add(chatroomModel);
+    }
+
+    @Override
+    public ResponseResult keepAliveRemove(ChatroomModel chatroomModel) throws Exception {
+        return getKeepalive().remove(chatroomModel);
+    }
+
+    @Override
+    public ChatroomKeepaliveResult keepAliveGetList() throws Exception {
+        return getKeepalive().getList();
+    }
+
+    @Override
+    public ResponseResult whitelistUserAdd(ChatroomModel chatroomModel) throws Exception {
+        return getWhitelist().user.add(chatroomModel);
+    }
+
+    @Override
+    public ResponseResult whitelistUserRemove(ChatroomModel chatroomModel) throws Exception {
+        return getWhitelist().user.remove(chatroomModel);
+    }
+
+    @Override
+    public Result whitelistUserGetList(ChatroomModel chatroomModel) throws Exception {
+        return getWhitelist().user.getList(chatroomModel);
+    }
+
+    @Override
+    public ResponseResult whitelistMessageAdd(String[] objectNames) throws Exception {
+        return getWhitelist().message.add(objectNames);
+    }
+
+    @Override
+    public ResponseResult whitelistMessageRemove(String[] objectNames) throws Exception {
+        return getWhitelist().message.remove(objectNames);
+    }
+
+    @Override
+    public ChatroomWhitelistMsgResult whitelistMessageGetList() throws Exception {
+        return getWhitelist().message.getList();
+    }
+
+    @Override
+    public ResponseResult sensitiveAdd(String word) throws Exception {
+        return getWordfilter().add(word);
+    }
+
+    @Override
+    public ResponseResult sensitiveRemove(String word) throws Exception {
+        return getWordfilter().remove(word);
+    }
+
+    @Override
+    public ListWordfilterResult sensitiveGetList() throws Exception {
+        return getWordfilter().getList();
+    }
+}

+ 136 - 0
mec-im/src/main/java/com/ym/service/Impl/GroupServiceImpl.java

@@ -0,0 +1,136 @@
+package com.ym.service.Impl;
+
+import com.ym.service.GroupService;
+import io.rong.RongCloud;
+import io.rong.methods.conversation.Conversation;
+import io.rong.methods.group.Group;
+import io.rong.methods.group.mute.MuteAllMembers;
+import io.rong.methods.group.mute.MuteMembers;
+import io.rong.methods.group.mute.whitelist.User;
+import io.rong.models.Result;
+import io.rong.models.conversation.ConversationModel;
+import io.rong.models.group.GroupModel;
+import io.rong.models.group.UserGroup;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+public class GroupServiceImpl implements GroupService {
+
+    @Value("${cn.rongcloud.im.appkey}")
+    private String appKey;
+    @Value("${cn.rongcloud.im.secret}")
+    private String appSecret;
+
+    private Group getGroup(){
+        RongCloud rongCloud = RongCloud.getInstance(appKey, appSecret);
+        return new Group(appKey,appSecret,rongCloud);
+    }
+    private MuteMembers getMuteMembers(){
+        RongCloud rongCloud = RongCloud.getInstance(appKey, appSecret);
+        return new MuteMembers(appKey,appSecret,rongCloud);
+    }
+    private MuteAllMembers getMuteAllMembers(){
+        RongCloud rongCloud = RongCloud.getInstance(appKey, appSecret);
+        return new MuteAllMembers(appKey,appSecret,rongCloud);
+    }
+    private User getUser(){
+        RongCloud rongCloud = RongCloud.getInstance(appKey, appSecret);
+        return new User(appKey,appSecret,rongCloud);
+    }
+    private Conversation getConversation(){
+        return new Conversation(appKey,appSecret);
+    }
+
+    @Override
+    public Result groupSync(UserGroup userGroup) throws Exception {
+        return getGroup().sync(userGroup);
+    }
+
+    @Override
+    public Result groupCreate(GroupModel groupModel) throws Exception {
+        return getGroup().create(groupModel);
+    }
+
+    @Override
+    public Result groupGet(GroupModel groupModel) throws Exception {
+        return getGroup().get(groupModel);
+    }
+
+    @Override
+    public Result groupUpdate(GroupModel groupModel) throws Exception {
+        return getGroup().update(groupModel);
+    }
+
+    @Override
+    public Result groupJoin(GroupModel groupModel) throws Exception {
+        return getGroup().join(groupModel);
+    }
+
+    @Override
+    public Result groupQuit(GroupModel groupModel) throws Exception {
+        return getGroup().quit(groupModel);
+    }
+
+    @Override
+    public Result groupDismiss(GroupModel groupModel) throws Exception {
+        return getGroup().dismiss(groupModel);
+    }
+
+    @Override
+    public Result muteMembersAdd(GroupModel groupModel) throws Exception {
+        return getMuteMembers().add(groupModel);
+    }
+
+    @Override
+    public Result muteMembersRemove(GroupModel groupModel) throws Exception {
+        return getMuteMembers().remove(groupModel);
+    }
+
+    @Override
+    public Result muteMembersGetList(String groupId) throws Exception {
+        return getMuteMembers().getList(groupId);
+    }
+
+    @Override
+    public Result muteAllMembersAdd(String[] groupIds) throws Exception {
+        return getMuteAllMembers().add(groupIds);
+    }
+
+    @Override
+    public Result muteAllMembersRemove(String[] groupIds) throws Exception {
+        return getMuteAllMembers().remove(groupIds);
+    }
+
+    @Override
+    public Result muteAllMembersGetList() throws Exception {
+        return getMuteAllMembers().getList();
+    }
+
+    @Override
+    public Result muteWhiteAdd(GroupModel groupModel) throws Exception {
+        return getUser().add(groupModel);
+    }
+
+    @Override
+    public Result muteWhiteRemove(GroupModel groupModel) throws Exception {
+        return getUser().remove(groupModel);
+    }
+
+    @Override
+    public Result muteWhiteGetList(String groupId) throws Exception {
+        return getUser().getList(groupId);
+    }
+
+    @Override
+    public Result conversationMute(ConversationModel conversationModel) throws Exception {
+        return getConversation().mute(conversationModel);
+    }
+
+    @Override
+    public Result conversationUnmute(ConversationModel conversationModel) throws Exception {
+        return getConversation().unMute(conversationModel);
+    }
+}

+ 125 - 0
mec-im/src/main/java/com/ym/service/Impl/MessageServiceImpl.java

@@ -0,0 +1,125 @@
+package com.ym.service.Impl;
+
+import com.ym.service.MessageService;
+import io.rong.methods.message._private.Private;
+import io.rong.methods.message.chatroom.Chatroom;
+import io.rong.methods.message.group.Group;
+import io.rong.methods.message.history.History;
+import io.rong.methods.message.system.MsgSystem;
+import io.rong.methods.push.Push;
+import io.rong.models.Result;
+import io.rong.models.message.*;
+import io.rong.models.push.BroadcastModel;
+import io.rong.models.push.PushModel;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+public class MessageServiceImpl implements MessageService {
+
+    @Value("${cn.rongcloud.im.appkey}")
+    private String appKey;
+    @Value("${cn.rongcloud.im.secret}")
+    private String appSecret;
+
+    private Private getPrivate(){
+        return new Private(appKey,appSecret);
+    }
+    private Group getGroup(){
+        return new Group(appKey,appSecret);
+    }
+    private Chatroom getChatroom(){
+        return new Chatroom(appKey,appSecret);
+    }
+    private MsgSystem getMsgSystem(){
+        return new MsgSystem(appKey,appSecret);
+    }
+    private History getHistory(){
+        return new History(appKey,appSecret);
+    }
+    private Push getPush(){
+        return new Push(appKey,appSecret);
+    }
+
+    @Override
+    public Result privateSend(PrivateMessage privateMessage) throws Exception {
+        return getPrivate().send(privateMessage);
+    }
+
+    @Override
+    public Result privateRecall(RecallMessage recallMessage) throws Exception {
+        return getPrivate().recall(recallMessage);
+    }
+
+    @Override
+    public Result privateSendTemplate(TemplateMessage templateMessage) throws Exception {
+        return getPrivate().sendTemplate(templateMessage);
+    }
+
+    @Override
+    public Result groupSend(GroupMessage groupMessage) throws Exception {
+        return getGroup().send(groupMessage);
+    }
+
+    @Override
+    public Result groupSendDirection(GroupMessage groupMessage) throws Exception {
+        return getGroup().sendDirection(groupMessage);
+    }
+
+    @Override
+    public Result groupSendMention(MentionMessage mentionMessage) throws Exception {
+        return getGroup().sendMention(mentionMessage);
+    }
+
+    @Override
+    public Result groupRecall(RecallMessage recallMessage) throws Exception {
+        return getGroup().recall(recallMessage);
+    }
+
+    @Override
+    public Result chatroomSend(ChatroomMessage chatroomMessage) throws Exception {
+        return getChatroom().send(chatroomMessage);
+    }
+
+    @Override
+    public Result chatroomBroadcast(ChatroomMessage chatroomMessage) throws Exception {
+        return getChatroom().broadcast(chatroomMessage);
+    }
+
+    @Override
+    public Result systemSend(SystemMessage systemMessage) throws Exception {
+        return getMsgSystem().send(systemMessage);
+    }
+
+    @Override
+    public Result systemBroadcast(BroadcastMessage broadcastMessage) throws Exception {
+        return getMsgSystem().broadcast(broadcastMessage);
+    }
+
+    @Override
+    public Result systemSendTemplate(TemplateMessage templateMessage) throws Exception {
+        return getMsgSystem().sendTemplate(templateMessage);
+    }
+
+    @Override
+    public Result historyGet(String date) throws Exception {
+        return getHistory().get(date);
+    }
+
+    @Override
+    public Result historyRemove(String date) throws Exception {
+        return getHistory().remove(date);
+    }
+
+    @Override
+    public Result pushPush(PushModel pushModel) throws Exception {
+        return getPush().push(pushModel);
+    }
+
+    @Override
+    public Result pushMessage(BroadcastModel broadcastModel) throws Exception {
+        return getPush().message(broadcastModel);
+    }
+}

+ 1099 - 0
mec-im/src/main/java/com/ym/service/Impl/RoomServiceImpl.java

@@ -0,0 +1,1099 @@
+package com.ym.service.Impl;
+
+import com.ym.common.ApiException;
+import com.ym.common.DisplayEnum;
+import com.ym.common.ErrorEnum;
+import com.ym.common.JwtUser;
+import com.ym.config.IMProperties;
+import com.ym.config.RoomProperties;
+import com.ym.dao.RoomDao;
+import com.ym.dao.RoomMemberDao;
+import com.ym.dao.UserDao;
+import com.ym.dao.WhiteboardDao;
+import com.ym.im.IMHelper;
+import com.ym.im.message.*;
+import com.ym.job.ScheduleManager;
+import com.ym.pojo.*;
+import com.ym.service.RoomService;
+import com.ym.utils.CheckUtils;
+import com.ym.utils.CodeUtil;
+import com.ym.utils.DateTimeUtils;
+import com.ym.utils.IdentifierUtils;
+import com.ym.whiteboard.WhiteBoardHelper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+@Slf4j
+@Service
+public class RoomServiceImpl implements RoomService {
+    @Autowired
+    private IMHelper imHelper;
+
+    @Autowired
+    private RoomProperties roomProperties;
+
+    @Autowired
+    private RoomDao roomDao;
+
+    @Autowired
+    private RoomMemberDao roomMemberDao;
+
+//    @Autowired
+//    private JwtTokenHelper tokenHelper;
+
+    @Autowired
+    private WhiteBoardHelper whiteBoardHelper;
+
+    @Autowired
+    private WhiteboardDao whiteboardDao;
+
+    @Autowired
+    private ScheduleManager scheduleManager;
+
+    @Autowired
+    private UserDao userDao;
+
+    @Autowired
+    private IMProperties imProperties;
+
+    @Transactional
+    @Override
+    public RoomResult joinRoom(String userName, String roomId, boolean isAudience, boolean isDisableCamera, JwtUser jwtUser) throws ApiException, Exception {
+        CheckUtils.checkArgument(userName != null, "userName must't be null");
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+
+        log.info("joinRoom: jwtUser={}, roomId={}, userName={}, isAudience={}, isDisableCamera={}", jwtUser, roomId, userName, isAudience, isDisableCamera);
+        String userId;
+        if (jwtUser != null) {
+            userId = jwtUser.getUserId();
+            if (!jwtUser.getUserName().equals(userName) || !jwtUser.getRoomId().equals(roomId)) {
+                userId = IdentifierUtils.uuid();
+                log.info("generate new user: roomId={} , userId={}, userName={}", roomId, userId, userName);
+            } else {
+                log.info("join the old room: roomId={} , userId={}, userName={}", roomId, userId, userName);
+            }
+        } else {
+            userId = IdentifierUtils.uuid();
+        }
+
+        String display = "";
+        Date curTime = DateTimeUtils.currentUTC();
+        List<Room> roomList = roomDao.findByRid(roomId);
+        if (roomList.isEmpty()) {
+            saveRoom(roomId, roomId, curTime, display);
+            IMApiResultInfo resultInfo = imHelper.createGroup(new String[]{userId}, roomId, roomId);
+            if (!resultInfo.isSuccess()) {
+                log.error("joinRoom IM error: roomId={}, {}", roomId, resultInfo.getErrorMessage());
+                throw new ApiException(ErrorEnum.ERR_CREATE_ROOM_ERROR, resultInfo.getErrorMessage());
+            } else {
+                scheduleManager.addExpiredTask(this, roomId);
+            }
+        } else {
+            display = roomList.get(0).getDisplay();
+        }
+
+        RoleEnum roleEnum;
+        RoomResult roomResult = new RoomResult();
+        RoomResult.MemberResult userResult = new RoomResult.MemberResult();
+        List<RoomMember> memberList = roomMemberDao.findByRidAndUid(roomId, userId);
+        if (memberList.isEmpty()) {
+            int count = roomMemberDao.countByRidAndExcludeRole(roomId, RoleEnum.RoleAudience.getValue());
+            if (!isAudience && count == roomProperties.getMaxCount()) {
+                log.info("join error: roomId = {}, userName = {}, isAudience = {}", roomId, userName, isAudience);
+                throw new ApiException(ErrorEnum.ERR_OVER_MAX_COUNT);
+            }
+            if (!isAudience) {
+                List<RoomMember> assistantList = roomMemberDao.findByRidAndRole(roomId, RoleEnum.RoleAssistant.getValue());
+                if (!assistantList.isEmpty()) {
+                    if (count == 1) {
+                        roleEnum = RoleEnum.RoleTeacher;
+                    } else {
+                        roleEnum = RoleEnum.RoleStudent;
+                    }
+                } else {
+                    roleEnum = RoleEnum.RoleAssistant;
+                }
+            } else {
+                roleEnum = RoleEnum.RoleAudience;
+            }
+            saveRoomMember(userId, userName, roomId, roleEnum.getValue(), !isDisableCamera, curTime);
+            IMApiResultInfo resultInfo = imHelper.joinGroup(new String[]{userId}, roomId, roomId);
+            if (!resultInfo.isSuccess()) {
+                throw new ApiException(ErrorEnum.ERR_CREATE_ROOM_ERROR, resultInfo.getErrorMessage());
+            }
+            userResult.setMicrophone(true);
+            userResult.setCamera(!isDisableCamera);
+            userResult.setJoinTime(curTime);
+            log.info("user join the room: roomId={} , userId={}, roleEnum={}, memCount: {}", roomId, userId, roleEnum, count);
+        } else {
+            roleEnum = RoleEnum.getEnumByValue(memberList.get(0).getRole());
+            roomMemberDao.updateCameraByRidAndUid(roomId, userId, !isDisableCamera);
+            userResult.setCamera(!isDisableCamera);
+            userResult.setJoinTime(memberList.get(0).getJoinDt());
+
+            log.info("user exist in the room: roomId={} , userId={}, use the last role={}", roomId, userId, roleEnum);
+        }
+
+        MemberChangedMessage msg = new MemberChangedMessage(MemberChangedMessage.Action_Join, userId, roleEnum.getValue());
+        msg.setTimestamp(curTime);
+        msg.setUserName(userName);
+        msg.setCamera(!isDisableCamera);
+        imHelper.publishMessage(userId, roomId, msg);
+        if (roleEnum == RoleEnum.RoleTeacher) {
+            display = "display://type=1?userId=" + userId + "?uri=";
+            updateDisplay(roomId, userId, display, 0);
+            log.info("joinRoom, display changed: roomId={}, {}, userId={}", roomId, display, userId);
+        } else if (roleEnum == RoleEnum.RoleAssistant && display.isEmpty()) {
+            display = "display://type=0?userId=" + userId + "?uri=";
+            updateDisplay(roomId, userId, display, 0);
+            log.info("joinRoom, display changed: roomId={}, {}, userId={}", roomId, display, userId);
+        }
+
+        List<UserInfo> userInfoList = userDao.findByUid(userId);
+        if (userInfoList.isEmpty()) {
+            UserInfo userInfo = new UserInfo();
+            userInfo.setUid(userId);
+            userInfo.setName(userName);
+            userInfo.setCreateDt(curTime);
+            userInfo.setUpdateDt(curTime);
+            userDao.save(userInfo);
+        } else {
+            UserInfo user = userInfoList.get(0);
+            user.setUpdateDt(curTime);
+            userDao.save(user);
+        }
+
+        userResult.setUserName(userName);
+        userResult.setUserId(userId);
+        userResult.setRole(roleEnum.getValue());
+        roomResult.setUserInfo(userResult);
+        roomResult.setDisplay(display);
+
+        //generate authorization
+        jwtUser = new JwtUser();
+        jwtUser.setUserId(userId);
+        jwtUser.setUserName(userName);
+        jwtUser.setRoomId(roomId);
+//        JwtToken jwtToken = tokenHelper.createJwtToken(jwtUser);
+        IMTokenInfo tokenInfo = imHelper.getToken(userId, userId, "");
+        if (tokenInfo.isSuccess()) {
+            roomResult.setImToken(tokenInfo.getToken());
+        } else {
+            throw new ApiException(ErrorEnum.ERR_IM_TOKEN_ERROR, tokenInfo.getErrorMessage());
+        }
+//        roomResult.setAuthorization(jwtToken.getToken());
+        roomResult.setRoomId(roomId);
+        List<RoomMember> roomMemberList = roomMemberDao.findByRid(roomId);
+        roomResult.setMembers(roomMemberList);
+
+        List<Whiteboard> whiteboardList = whiteboardDao.findByRid(roomId);
+        roomResult.setWhiteboards(whiteboardList);
+        log.info("join success: roomId = {}, userId = {}, userName={}, role = {}", roomId, userId, userName, roleEnum);
+        return roomResult;
+    }
+
+    private void saveRoom(String roomId, String roomName, Date createTime, String display) {
+        Room room = new Room();
+        room.setRid(roomId);
+        room.setName(roomName);
+        room.setCreateDt(createTime);
+        room.setDisplay(display);
+        room.setWhiteboardNameIndex(0);
+        roomDao.save(room);
+    }
+
+    private void saveRoomMember(String userId, String userName, String roomId, int role, boolean cameraOn, Date joinTime) {
+        RoomMember roomMember = new RoomMember();
+        roomMember.setUid(userId);
+        roomMember.setName(userName);
+        roomMember.setRid(roomId);
+        roomMember.setRole(role);
+        roomMember.setCamera(cameraOn);
+        roomMember.setJoinDt(joinTime);
+        roomMemberDao.save(roomMember);
+    }
+
+    @Transactional
+    @Override
+    public Boolean leaveRoom(JwtUser jwtUser, String roomId) throws Exception {
+        CheckUtils.checkArgument(jwtUser.getUserId() != null, "userId must't be null");
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+        CheckUtils.checkArgument(roomId.equals(jwtUser.getRoomId()), "roomId not exist");
+
+        List<Room> roomList = roomDao.findByRid(roomId);
+        if (roomList.size() == 0) {
+            log.error("room : {} not exist ", roomId);
+            throw new ApiException(ErrorEnum.ERR_ROOM_NOT_EXIST);
+        }
+
+        List<RoomMember> roomMemberList = roomMemberDao.findByRidAndUid(roomId, jwtUser.getUserId());
+        if (roomMemberList.size() == 0) {
+            log.error("{} not exist in room: {}", jwtUser.getUserId(), roomId);
+            throw new ApiException(ErrorEnum.ERR_USER_NOT_EXIST_IN_ROOM);
+        }
+
+        int userRole = roomMemberList.get(0).getRole();
+        log.info("leaveRoom: roomId={}, {}, role={}", roomId, jwtUser, RoleEnum.getEnumByValue(userRole));
+
+        if (userRole == RoleEnum.RoleTeacher.getValue() || userRole == RoleEnum.RoleAssistant.getValue()) {
+            if (isUserDisplay(roomList.get(0), jwtUser.getUserId())) {
+                updateDisplay(roomId, jwtUser.getUserId(), "", 0);
+                log.info("clear display cause speaker leave: roomId={}, {}", roomId, jwtUser);
+            } else {
+                log.info("don't update current display: room={}, role={}", roomList.get(0), RoleEnum.getEnumByValue(userRole));
+            }
+        } else {
+            log.info("don't update current display: room={}, userRole={}", roomList.get(0), RoleEnum.getEnumByValue(userRole));
+        }
+
+        if (roomMemberDao.countByRid(roomId) == 1) {
+            IMApiResultInfo apiResultInfo = null;
+            try {
+                apiResultInfo = imHelper.dismiss(jwtUser.getUserId(), roomId);
+                if (apiResultInfo.getCode() == 200) {
+                    roomMemberDao.deleteUserByRidAndUid(roomId, jwtUser.getUserId());
+                    roomDao.deleteByRid(roomId);
+                    deleteWhiteboardByUser(roomId, jwtUser.getUserId());
+                    log.info("dismiss the room: {}", roomId);
+                } else {
+                    log.error("{} exit {} room error: {}", jwtUser.getUserId(), roomId, apiResultInfo.getErrorMessage());
+                    throw new ApiException(ErrorEnum.ERR_EXIT_ROOM_ERROR, apiResultInfo.getErrorMessage());
+                }
+            } catch (Exception e) {
+                log.error("{} exit {} room error: {}", jwtUser.getUserId(), roomId, e.getMessage());
+                throw new ApiException(ErrorEnum.ERR_EXIT_ROOM_ERROR, e.getMessage());
+            }
+        } else {
+            IMApiResultInfo apiResultInfo = null;
+            try {
+                apiResultInfo = imHelper.quit(new String[]{jwtUser.getUserId()}, roomId);
+                if (apiResultInfo.isSuccess()) {
+                    roomMemberDao.deleteUserByRidAndUid(roomId, jwtUser.getUserId());
+                    MemberChangedMessage msg = new MemberChangedMessage(MemberChangedMessage.Action_Leave, jwtUser.getUserId(), userRole);
+                    msg.setUserName(jwtUser.getUserName());
+                    imHelper.publishMessage(jwtUser.getUserId(), roomId, msg);
+                    imHelper.quit(new String[]{jwtUser.getUserId()}, roomId);
+                    log.info("quit group: roomId={}, {}", roomId, jwtUser);
+                } else {
+                    throw new ApiException(ErrorEnum.ERR_EXIT_ROOM_ERROR, apiResultInfo.getErrorMessage());
+                }
+            } catch (Exception e) {
+                log.error("leave room error: roomId={}, {}, {}", roomId, jwtUser, e.getMessage());
+                throw new ApiException(ErrorEnum.ERR_EXIT_ROOM_ERROR);
+            }
+        }
+        userDao.deleteByUid(jwtUser.getUserId());
+
+        return true;
+    }
+
+    private void deleteWhiteboardByUser(String roomId, String userId) throws Exception {
+        List<Whiteboard> whiteboardList = whiteboardDao.findByRidAndCreator(roomId, userId);
+        if (!whiteboardList.isEmpty()) {
+            whiteboardDao.deleteByRidAndCreator(roomId, userId);
+            for (Whiteboard wb : whiteboardList) {
+                whiteBoardHelper.destroy(wb.getWbRoom());
+            }
+        }
+    }
+
+    @Transactional
+    @Override
+    public void destroyRoom(String roomId) {
+        roomDao.deleteByRid(roomId);
+        whiteboardDao.deleteByRid(roomId);
+
+        List<RoomMember> list = roomMemberDao.findByRid(roomId);
+        if (!list.isEmpty()) {
+            try {
+                imHelper.dismiss(list.get(0).getUid(), roomId);
+            } catch (Exception e) {
+                log.error("destroyRoom: {}", e.getMessage());
+                e.printStackTrace();
+            }
+        }
+        roomMemberDao.deleteByRid(roomId);
+        log.info("destroyRoom: {}", roomId);
+    }
+
+    @Transactional
+    @Override
+    public Boolean downgrade(String roomId, JwtUser jwtUser, List<ReqChangeUserRoleData.ChangedUser> users) throws ApiException, Exception {
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+        CheckUtils.checkArgument(users.size() > 0, "the changed user list must't be null");
+
+        List<Room> roomList = roomDao.findByRid(roomId);
+        if (roomList.isEmpty()) {
+            throw new ApiException(ErrorEnum.ERR_ROOM_NOT_EXIST);
+        }
+
+        boolean result = false;
+        List<RoleChangedMessage.ChangedUser> changedUsers = new ArrayList<>();
+        for (ReqChangeUserRoleData.ChangedUser user : users) {
+            String changedUserId = user.getUserId();
+            RoleEnum changedRole = RoleEnum.getEnumByValue(user.getRole());
+            if (changedUserId.equals(jwtUser.getUserId())) {
+                log.error("can not change self role: {}, {}, {}", roomId, jwtUser.getUserId(), changedRole);
+                throw new ApiException(ErrorEnum.ERR_CHANGE_SELF_ROLE);
+            } else {
+                List<RoomMember> oldUsers = roomMemberDao.findByRidAndUid(roomId, changedUserId);
+                if (oldUsers.size() > 0) {
+                    if (changedRole.equals(RoleEnum.RoleAudience)) {
+                        int r = roomMemberDao.updateRoleByRidAndUid(roomId, changedUserId, changedRole.getValue());
+                        RoleChangedMessage.ChangedUser u = new RoleChangedMessage.ChangedUser(changedUserId, changedRole.getValue());
+                        List<UserInfo> userInfoList = userDao.findByUid(changedUserId);
+                        if (!userInfoList.isEmpty()) {
+                            u.setUserName(userInfoList.get(0).getName());
+                        }
+                        changedUsers.add(u);
+                        log.info("change the role: {}, {}, {}, result: {}", roomId, jwtUser.getUserId(), changedRole, r);
+                        result = true;
+                    }
+                    if (oldUsers.get(0).getRole() == RoleEnum.RoleTeacher.getValue() && isUserDisplay(roomList.get(0), oldUsers.get(0).getUid())) {
+                        updateDisplay(roomId, jwtUser.getUserId(), "", 1);
+                    } else {
+                        log.info("don't update display: room={}, userRole={}", roomList.get(0), RoleEnum.getEnumByValue(oldUsers.get(0).getRole()));
+                    }
+                } else {
+                    log.info("role changed fail, not exist: {} - {} - {}", roomId, jwtUser.getUserId(), changedRole);
+                }
+            }
+        }
+        if (result) {
+            RoleChangedMessage msg = new RoleChangedMessage(jwtUser.getUserId());
+            msg.setUsers(changedUsers);
+            imHelper.publishMessage(jwtUser.getUserId(), roomId, msg, 1);
+        }
+        return result;
+    }
+
+    @Override
+    public Boolean kickMember(String roomId, String userId, JwtUser jwtUser) throws ApiException, Exception {
+        CheckUtils.checkArgument(userId != null, "userId must't be null");
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+
+        List<RoomMember> kickedUsers = roomMemberDao.findByRidAndUid(roomId, userId);
+        int result = roomMemberDao.deleteUserByRidAndUid(roomId, userId);
+        log.info("kickMember: roomId={}, userId={}, result = {}, {}", roomId, userId, result, jwtUser);
+        if (result == 0) {
+            throw new ApiException(ErrorEnum.ERR_USER_NOT_EXIST_IN_ROOM);
+        } else {
+            MemberChangedMessage msg = new MemberChangedMessage(MemberChangedMessage.Action_Kick, userId, kickedUsers.get(0).getRole());
+            List<UserInfo> userInfoList = userDao.findByUid(userId);
+            if (!userInfoList.isEmpty()) {
+                msg.setUserName(userInfoList.get(0).getName());
+            }
+            IMApiResultInfo apiResultInfo = imHelper.publishMessage(jwtUser.getUserId(), roomId, msg, 1);
+            if (!apiResultInfo.isSuccess()) {
+                throw new ApiException(ErrorEnum.ERR_MESSAGE_ERROR);
+            }
+            Thread.sleep(50);
+            log.info("published msg: {}, msg={}", jwtUser, msg);
+            List<Room> roomList = roomDao.findByRid(roomId);
+            if (kickedUsers.get(0).getRole() == RoleEnum.RoleTeacher.getValue() && isUserDisplay(roomList.get(0), userId)) {
+                updateDisplay(roomId, jwtUser.getUserId(), "", 1);
+            } else {
+                log.info("don't update display: room={}, userRole={}", roomId, RoleEnum.getEnumByValue(kickedUsers.get(0).getRole()));
+            }
+        }
+        userDao.deleteByUid(userId);
+        IMApiResultInfo apiResultInfo = imHelper.quit(new String[]{userId}, roomId);
+        if (!apiResultInfo.isSuccess()) {
+            throw new ApiException(ErrorEnum.ERR_EXIT_ROOM_ERROR, apiResultInfo.getErrorMessage());
+        }
+        return true;
+    }
+
+    @Override
+    public Boolean display(String roomId, int type, String userId, String uri, JwtUser jwtUser) throws ApiException, Exception {
+        log.info("display in room: {}, type = {}, uri = {}, {}", roomId, type, uri, jwtUser);
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+        CheckUtils.checkArgument(type >= 0 && type < DisplayEnum.values().length, "type not exist");
+        DisplayEnum displayEnum = DisplayEnum.values()[type];
+
+        if (displayEnum.equals(DisplayEnum.None)) {
+            roomDao.updateDisplayByRid(roomId, "");
+            DisplayMessage displayMessage = new DisplayMessage("");
+            IMApiResultInfo apiResultInfo = imHelper.publishMessage(jwtUser.getUserId(), roomId, displayMessage);
+            if (apiResultInfo.isSuccess()) {
+                return true;
+            } else {
+                throw new ApiException(ErrorEnum.ERR_MESSAGE_ERROR, apiResultInfo.getErrorMessage());
+            }
+        }
+
+        String display = "display://type=" + type;
+        if (displayEnum.equals(DisplayEnum.Teacher)) {
+            List<RoomMember> teachers = roomMemberDao.findByRidAndRole(roomId, RoleEnum.RoleTeacher.getValue());
+            if (teachers.isEmpty()) {
+                throw new ApiException(ErrorEnum.ERR_TEACHER_NOT_EXIST_IN_ROOM);
+            } else {
+                display += "?userId=" + teachers.get(0).getUid() + "?uri=";
+                roomDao.updateDisplayByRid(roomId, display);
+                DisplayMessage displayMessage = new DisplayMessage(display);
+                imHelper.publishMessage(jwtUser.getUserId(), roomId, displayMessage);
+                log.info("change display to teacher: roomId={}, {}, display={}", roomId, jwtUser, display);
+            }
+        } else if (displayEnum.equals(DisplayEnum.Assistant)) {
+            List<RoomMember> assistants = roomMemberDao.findByRidAndRole(roomId, RoleEnum.RoleAssistant.getValue());
+            if (assistants.isEmpty()) {
+                throw new ApiException(ErrorEnum.ERR_ASSISTANT_NOT_EXIST_IN_ROOM);
+            } else {
+                display += "?userId=" + assistants.get(0).getUid() + "?uri=";
+                roomDao.updateDisplayByRid(roomId, display);
+                DisplayMessage displayMessage = new DisplayMessage(display);
+                imHelper.publishMessage(jwtUser.getUserId(), roomId, displayMessage);
+                log.info("change display to assistant: roomId={}, {}, display={}", roomId, jwtUser, display);
+            }
+        } else if (displayEnum.equals(DisplayEnum.Screen)) {
+            display += "?userId=" + userId + "?uri=";
+            roomDao.updateDisplayByRid(roomId, display);
+            DisplayMessage displayMessage = new DisplayMessage(display);
+            imHelper.publishMessage(jwtUser.getUserId(), roomId, displayMessage);
+            log.info("change display to screen: roomId={}, {}, display={}", roomId, jwtUser, display);
+        } else {
+            display += "?userId=" + "?uri=" + uri;
+            CheckUtils.checkArgument(uri != null, "uri must't be null");
+            CheckUtils.checkArgument(whiteboardDao.findByRidAndWbid(roomId, uri).size() > 0, "whiteboard not exist");
+
+            roomDao.updateDisplayByRid(roomId, display);
+            DisplayMessage displayMessage = new DisplayMessage(display);
+            imHelper.publishMessage(jwtUser.getUserId(), roomId, displayMessage);
+        }
+        log.info("result display in room: {}, type = {}, uri = {}, {}", roomId, type, uri, jwtUser);
+        return true;
+    }
+
+    @Override
+    public String createWhiteBoard(String roomId, JwtUser jwtUser) throws ApiException, Exception {
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");
+        CheckUtils.checkArgument(roomMemberDao.existsByRidAndUid(roomId, jwtUser.getUserId()), "room member not exist");
+
+        log.info("createWhiteBoard: roomId = {},  {}", roomId, jwtUser);
+
+        String wbRoom = IdentifierUtils.uuid();
+        WhiteBoardApiResultInfo resultInfo = whiteBoardHelper.create(wbRoom);
+        if (resultInfo.isSuccess()) {
+            String wbId = resultInfo.getData();
+            Date date = DateTimeUtils.currentUTC();
+            List<Room> roomList = roomDao.findByRid(roomId);
+            int whiteboardNameIndex = roomList.get(0).getWhiteboardNameIndex() + 1;
+            String name = "白板" + whiteboardNameIndex;
+            roomDao.updateWhiteboardNameIndexByRid(roomId, whiteboardNameIndex);
+            Whiteboard wb = new Whiteboard();
+            wb.setRid(roomId);
+            wb.setWbRoom(wbRoom);
+            wb.setWbid(wbId);
+            wb.setName(name);
+            wb.setCreator(jwtUser.getUserId());
+            wb.setCreateDt(date);
+            wb.setCurPg(0);
+            whiteboardDao.save(wb);
+            WhiteboardMessage wbmsg = new WhiteboardMessage(WhiteboardMessage.Create);
+            wbmsg.setWhiteboardId(wbId);
+            wbmsg.setWhiteboardName(name);
+            imHelper.publishMessage(jwtUser.getUserId(), roomId, wbmsg);
+            String display = "display://type=2?userId=" + jwtUser.getUserId() + "?uri=" + wbId;
+            roomDao.updateDisplayByRid(roomId, display);
+            DisplayMessage displayMessage = new DisplayMessage(display);
+            imHelper.publishMessage(jwtUser.getUserId(), roomId, displayMessage, 1);
+
+            return wbId;
+        } else {
+            throw new ApiException(ErrorEnum.ERR_CREATE_WHITE_BOARD, resultInfo.getMsg());
+        }
+    }
+
+    @Override
+    public Boolean deleteWhiteboard(String roomId, JwtUser jwtUser, String whiteBoardId) throws ApiException, Exception {
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+        CheckUtils.checkArgument(whiteBoardId != null, "whiteBoardId must't be null");
+
+        List<Whiteboard> whiteboardList = whiteboardDao.findByRidAndWbid(roomId, whiteBoardId);
+        CheckUtils.checkArgument(whiteboardList.size() > 0, "whiteboard not exist");
+
+        List<Room> roomList = roomDao.findByRid(roomId);
+        CheckUtils.checkArgument(!roomList.isEmpty(), "room not exist");
+
+        log.info("deleteWhiteboard: room={}, whiteBoardId={}, {}", roomList.get(0), whiteBoardId, jwtUser);
+
+        String display = roomList.get(0).getDisplay();
+        if (display.contains("uri=" + whiteBoardId)) {
+            int result = roomDao.updateDisplayByRid(roomId, "");
+            log.info("clear room display, room: {}, result: {}", roomId, result);
+            DisplayMessage displayMessage = new DisplayMessage("");
+            imHelper.publishMessage(jwtUser.getUserId(), roomId, displayMessage, 1);
+        } else {
+            log.info("no display to clean: room={}", roomList.get(0));
+        }
+
+        String wbRoom = whiteboardList.get(0).getWbRoom();
+        WhiteBoardApiResultInfo resultInfo = whiteBoardHelper.destroy(wbRoom);
+        if (resultInfo.isSuccess()) {
+            int result = whiteboardDao.deleteByWbid(whiteBoardId);
+            log.info("delete whiteboard: roomId = {}, whiteBoardId = {}, result = {}", roomId, whiteBoardId, result);
+            WhiteboardMessage wbmsg = new WhiteboardMessage(WhiteboardMessage.Delete);
+            wbmsg.setWhiteboardId(whiteBoardId);
+            imHelper.publishMessage(jwtUser.getUserId(), roomId, wbmsg, 1);
+            return true;
+        } else {
+            throw new ApiException(ErrorEnum.ERR_DELETE_WHITE_BOARD, resultInfo.getMsg());
+        }
+    }
+
+    @Override
+    public List<RoomResult.WhiteboardResult> getWhiteboard(String roomId, JwtUser jwtUser) throws ApiException, Exception {
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");
+
+        List<Whiteboard> whiteboards = whiteboardDao.findByRid(roomId);
+        List<RoomResult.WhiteboardResult> result = new ArrayList<>();
+        for (Whiteboard wb : whiteboards) {
+            RoomResult.WhiteboardResult r = new RoomResult.WhiteboardResult();
+            r.setName(wb.getName());
+            r.setCurPg(wb.getCurPg());
+            r.setWhiteboardId(wb.getWbid());
+            result.add(r);
+        }
+        return result;
+    }
+
+    @Override
+    public Boolean turnWhiteBoardPage(String roomId, String whiteBoardId, int page, JwtUser jwtUser) throws ApiException, Exception {
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+        CheckUtils.checkArgument(whiteBoardId != null, "whiteBoardId must't be null");
+        List<Room> roomList = roomDao.findByRid(roomId);
+        CheckUtils.checkArgument(!roomList.isEmpty(), "room not exist");
+
+        int result = whiteboardDao.updatePageByRidAndWbid(roomId, whiteBoardId, page);
+        log.info("turn page to: {}, room: {}, wb : {}; r: {}", page, roomId, whiteBoardId, result);
+
+        TurnPageMessage turnPageMessage = new TurnPageMessage(whiteBoardId, jwtUser.getUserId(), page);
+        imHelper.publishMessage(jwtUser.getUserId(), roomId, turnPageMessage);
+        return true;
+    }
+
+    @Override
+    public Boolean controlDevice(String roomId, String userId, DeviceTypeEnum typeEnum, boolean enable, JwtUser jwtUser) throws ApiException, Exception {
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+        CheckUtils.checkArgument(userId != null, "userId must't be null");
+        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");
+        CheckUtils.checkArgument(roomMemberDao.existsByRidAndUid(roomId, userId), "room member not exist");
+
+        log.info("controlDevice: {}, userId={}, typeEnum={}, onOff={}", jwtUser, userId, typeEnum, enable);
+
+        if (enable) {
+            String ticket = IdentifierUtils.uuid();
+            ControlDeviceTaskInfo taskInfo = new ControlDeviceTaskInfo();
+            taskInfo.setRoomId(roomId);
+            taskInfo.setTypeEnum(typeEnum);
+            taskInfo.setOnOff(true);
+            taskInfo.setApplyUserId(jwtUser.getUserId());
+            taskInfo.setTargetUserId(userId);
+            taskInfo.setTicket(ticket);
+            scheduleManager.addTask(taskInfo);
+            ControlDeviceNotifyMessage msg = new ControlDeviceNotifyMessage(ActionEnum.Invite.ordinal());
+            msg.setTicket(ticket);
+            msg.setType(taskInfo.getTypeEnum().ordinal());
+            msg.setOpUserId(jwtUser.getUserId());
+            msg.setOpUserName(jwtUser.getUserName());
+            imHelper.publishMessage(jwtUser.getUserId(), userId, roomId, msg);
+        } else {
+            if (typeEnum.equals(DeviceTypeEnum.Camera)) {
+                roomMemberDao.updateCameraByRidAndUid(roomId, jwtUser.getUserId(), false);
+            } else {
+                roomMemberDao.updateMicByRidAndUid(roomId, jwtUser.getUserId(), false);
+            }
+            DeviceStateChangedMessage deviceResourceMessage = new DeviceStateChangedMessage(typeEnum.ordinal(), false);
+            deviceResourceMessage.setUserId(userId);
+            List<UserInfo> userInfoList = userDao.findByUid(userId);
+            if (!userInfoList.isEmpty()) {
+                deviceResourceMessage.setUserName(userInfoList.get(0).getName());
+            }
+            imHelper.publishMessage(jwtUser.getUserId(), roomId, deviceResourceMessage, 1);
+        }
+        return true;
+    }
+
+    @Override
+    public Boolean approveControlDevice(String roomId, String ticket, JwtUser jwtUser) throws ApiException, Exception {
+        CheckUtils.checkArgument(ticket != null, "ticket must't be null");
+
+        log.info("approveControlDevice: jwtUser={}, ticket={}", jwtUser, ticket);
+        ControlDeviceTaskInfo taskInfo = (ControlDeviceTaskInfo) scheduleManager.executeTask(ticket);
+        if (taskInfo.getTypeEnum().equals(DeviceTypeEnum.Camera)) {
+            roomMemberDao.updateCameraByRidAndUid(roomId, jwtUser.getUserId(), taskInfo.isOnOff());
+        } else {
+            roomMemberDao.updateMicByRidAndUid(roomId, jwtUser.getUserId(), taskInfo.isOnOff());
+        }
+        ControlDeviceNotifyMessage msg = new ControlDeviceNotifyMessage(ActionEnum.Approve.ordinal());
+        msg.setType(taskInfo.getTypeEnum().ordinal());
+        msg.setOpUserId(jwtUser.getUserId());
+        msg.setOpUserName(jwtUser.getUserName());
+        imHelper.publishMessage(jwtUser.getUserId(), taskInfo.getApplyUserId(), roomId, msg);
+
+        DeviceStateChangedMessage deviceResourceMessage = new DeviceStateChangedMessage(taskInfo.getTypeEnum().ordinal(), taskInfo.isOnOff());
+        deviceResourceMessage.setUserId(jwtUser.getUserId());
+        imHelper.publishMessage(jwtUser.getUserId(), roomId, deviceResourceMessage, 1);
+        return true;
+    }
+
+    @Override
+    public Boolean rejectControlDevice(String roomId, String ticket, JwtUser jwtUser) throws ApiException, Exception {
+        CheckUtils.checkArgument(ticket != null, "ticket must't be null");
+
+        log.info("rejectControlDevice: jwtUser={}, ticket={}", jwtUser, ticket);
+        ControlDeviceTaskInfo taskInfo = (ControlDeviceTaskInfo) scheduleManager.executeTask(ticket);
+        ControlDeviceNotifyMessage msg = new ControlDeviceNotifyMessage(ActionEnum.Reject.ordinal());
+        msg.setType(taskInfo.getTypeEnum().ordinal());
+        msg.setOpUserId(jwtUser.getUserId());
+        msg.setOpUserName(jwtUser.getUserName());
+        imHelper.publishMessage(jwtUser.getUserId(), taskInfo.getApplyUserId(), roomId, msg);
+        return true;
+    }
+
+    @Override
+    public Boolean syncDeviceState(String roomId, DeviceTypeEnum type, boolean enable, JwtUser jwtUser) throws ApiException, Exception {
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");
+
+        int result;
+        DeviceStateChangedMessage deviceResourceMessage;
+        if (type.equals(DeviceTypeEnum.Camera)) {
+            result = roomMemberDao.updateCameraByRidAndUid(roomId, jwtUser.getUserId(), enable);
+            deviceResourceMessage = new DeviceStateChangedMessage(type.ordinal(), enable);
+        } else {
+            result = roomMemberDao.updateMicByRidAndUid(roomId, jwtUser.getUserId(), enable);
+            deviceResourceMessage = new DeviceStateChangedMessage(type.ordinal(), enable);
+        }
+        deviceResourceMessage.setUserId(jwtUser.getUserId());
+        imHelper.publishMessage(jwtUser.getUserId(), roomId, deviceResourceMessage, 1);
+        log.info("syncDeviceState : {}, {}, result = {}, jwtUser={}", roomId, enable, result, jwtUser);
+        return true;
+    }
+
+    @Override
+    public List<RoomResult.MemberResult> getMembers(String roomId, JwtUser jwtUser) throws ApiException, Exception {
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+
+        List<RoomMember> roomMemberList = roomMemberDao.findByRid(roomId);
+        RoomResult roomResult = new RoomResult();
+        roomResult.setMembers(roomMemberList);
+        return roomResult.getMembers();
+    }
+
+    @Override
+    public Boolean applySpeech(String roomId, JwtUser jwtUser) throws ApiException, Exception {
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");
+
+        List<RoomMember> assistants = roomMemberDao.findByRidAndRole(roomId, RoleEnum.RoleAssistant.getValue());
+        if (assistants.isEmpty()) {
+            throw new ApiException(ErrorEnum.ERR_ASSISTANT_NOT_EXIST_IN_ROOM);
+        }
+
+        String ticket = IdentifierUtils.uuid();
+        ScheduledTaskInfo scheduledTaskInfo = new ScheduledTaskInfo();
+        scheduledTaskInfo.setTicket(ticket);
+        scheduledTaskInfo.setRoomId(roomId);
+        scheduledTaskInfo.setApplyUserId(jwtUser.getUserId());
+        scheduledTaskInfo.setTargetUserId(assistants.get(0).getUid());
+        scheduleManager.addTask(scheduledTaskInfo);
+
+        log.info("applySpeech: task = {}, jwtUser={}", scheduledTaskInfo, jwtUser);
+
+        ApplyForSpeechMessage msg = new ApplyForSpeechMessage();
+        msg.setTicket(ticket);
+        msg.setReqUserId(jwtUser.getUserId());
+        IMApiResultInfo resultInfo = imHelper.publishMessage(jwtUser.getUserId(), assistants.get(0).getUid(), roomId, msg);
+
+        log.info("apply for speech: {}, jwtUser = {}, task = {}", roomId, jwtUser, scheduledTaskInfo);
+        if (resultInfo.isSuccess()) {
+            return true;
+        } else {
+            throw new ApiException(ErrorEnum.ERR_MESSAGE_ERROR, resultInfo.getErrorMessage());
+        }
+    }
+
+    @Override
+    public Boolean approveSpeech(String roomId, String ticket, JwtUser jwtUser) throws ApiException, Exception {
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");
+
+        int count = roomMemberDao.countByRidAndExcludeRole(roomId, RoleEnum.RoleAudience.getValue());
+        if (count == roomProperties.getMaxCount()) {
+            log.error("approveSpeech error: roomId = {}, jwtUser = {}, ticket={}", roomId, jwtUser, ticket);
+            throw new ApiException(ErrorEnum.ERR_OVER_MAX_COUNT);
+        }
+
+        ScheduledTaskInfo taskInfo = scheduleManager.executeTask(ticket);
+        log.info("approveSpeech: task = {}, jwtUser={}", taskInfo, jwtUser);
+        roomMemberDao.updateRoleByRidAndUid(roomId, taskInfo.getApplyUserId(), RoleEnum.RoleStudent.getValue());
+
+        SpeechResultMessage msg = new SpeechResultMessage(SpeechResultMessage.Action_Approve);
+        List<UserInfo> userInfoList = userDao.findByUid(taskInfo.getApplyUserId());
+        msg.setOpUserId(jwtUser.getUserId());
+        msg.setOpUserName(jwtUser.getUserName());
+        msg.setReqUserId(taskInfo.getApplyUserId());
+        if (!userInfoList.isEmpty()) {
+            msg.setReqUserName(userInfoList.get(0).getName());
+        }
+        msg.setRole(RoleEnum.RoleStudent.getValue());
+        IMApiResultInfo resultInfo = imHelper.publishMessage(jwtUser.getUserId(), taskInfo.getApplyUserId(), roomId, msg);
+        if (!resultInfo.isSuccess()) {
+            throw new ApiException(ErrorEnum.ERR_MESSAGE_ERROR, resultInfo.getErrorMessage());
+        }
+
+        RoleChangedMessage rcMsg = new RoleChangedMessage(jwtUser.getUserId());
+        List<RoleChangedMessage.ChangedUser> changedUserList = new ArrayList<>();
+        RoleChangedMessage.ChangedUser user = new RoleChangedMessage.ChangedUser(taskInfo.getApplyUserId(), RoleEnum.RoleStudent.getValue());
+        if (!userInfoList.isEmpty()) {
+            user.setUserName(userInfoList.get(0).getName());
+        }
+        changedUserList.add(user);
+        rcMsg.setUsers(changedUserList);
+        imHelper.publishMessage(jwtUser.getUserId(), roomId, rcMsg, 1);
+
+        return true;
+    }
+
+    @Override
+    public Boolean rejectSpeech(String roomId, String ticket, JwtUser jwtUser) throws ApiException, Exception {
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");
+
+        ScheduledTaskInfo taskInfo = scheduleManager.executeTask(ticket);
+
+        log.info("rejectSpeech: task = {}, jwtUser={}", taskInfo, jwtUser);
+        SpeechResultMessage msg = new SpeechResultMessage(SpeechResultMessage.Action_Reject);
+        msg.setOpUserId(jwtUser.getUserId());
+        msg.setOpUserName(jwtUser.getUserName());
+        msg.setRole(RoleEnum.RoleStudent.getValue());
+        IMApiResultInfo resultInfo = imHelper.publishMessage(jwtUser.getUserId(), taskInfo.getApplyUserId(), roomId, msg);
+        if (resultInfo.isSuccess()) {
+            return true;
+        } else {
+            throw new ApiException(ErrorEnum.ERR_MESSAGE_ERROR, resultInfo.getErrorMessage());
+        }
+    }
+
+    private void checkOverMax(String roomId, RoomMember targetUser, int targetRole) {
+        if (RoleEnum.getEnumByValue(targetUser.getRole()).equals(RoleEnum.RoleAudience)) {
+            int count = roomMemberDao.countByRidAndExcludeRole(roomId, RoleEnum.RoleAudience.getValue());
+            if (count == roomProperties.getMaxCount()) {
+                log.error("assign error: roomId = {}, userId = {}, role = {}", roomId, targetUser.getRid(), targetUser.getRole());
+                throw new ApiException(ErrorEnum.ERR_OVER_MAX_COUNT);
+            }
+        } else if (targetRole > targetUser.getRole()) {
+            throw new ApiException(ErrorEnum.ERR_DOWNGRADE_ROLE);
+        }
+    }
+
+    @Override
+    public Boolean transfer(String roomId, String userId, JwtUser jwtUser) throws ApiException, Exception {
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+        CheckUtils.checkArgument(userId != null, "userId must't be null");
+        CheckUtils.checkArgument(!userId.equals(jwtUser.getUserId()), "can't set self role");
+
+        log.info("transfer: roomId = {}, userId = {}, {}", roomId, userId, jwtUser);
+        List<RoomMember> roomMemberList = roomMemberDao.findByRidAndUid(roomId, userId);
+        if (roomMemberList.size() == 0) {
+            log.error("assistant transfer error: {} toUser = {}, opUser={}", roomId, userId, jwtUser.getUserId());
+            throw new ApiException(ErrorEnum.ERR_USER_NOT_EXIST_IN_ROOM);
+        }
+
+        List<Room> roomList = roomDao.findByRid(roomId);
+        if (roomList.size() == 0) {
+            log.error("assistant transfer error: {} toUser = {}, opUser={}", roomId, userId, jwtUser.getUserId());
+            throw new ApiException(ErrorEnum.ERR_ROOM_NOT_EXIST);
+        }
+
+        if (isUserDisplay(roomList.get(0), jwtUser.getUserId()) || isUserDisplay(roomList.get(0), userId)) {
+            updateDisplay(roomId, jwtUser.getUserId(), "", 1);
+        } else {
+            log.info("don't update display: room={}", roomList.get(0));
+        }
+
+        roomMemberDao.updateRoleByRidAndUid(roomId, jwtUser.getUserId(), RoleEnum.RoleStudent.getValue());
+        roomMemberDao.updateRoleByRidAndUid(roomId, userId, RoleEnum.RoleAssistant.getValue());
+
+        AssistantTransferMessage msg = new AssistantTransferMessage();
+        msg.setOpUserId(jwtUser.getUserId());
+        msg.setToUserId(userId);
+        IMApiResultInfo resultInfo = imHelper.publishMessage(jwtUser.getUserId(), roomId, msg, 1);
+        if (resultInfo.isSuccess()) {
+            return true;
+        } else {
+            throw new ApiException(ErrorEnum.ERR_MESSAGE_ERROR, resultInfo.getErrorMessage());
+        }
+    }
+
+    @Override
+    public Boolean inviteUpgradeRole(String roomId, String targetUserId, int targetRole, JwtUser jwtUser) throws ApiException, Exception {
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+        CheckUtils.checkArgument(targetUserId != null, "userId must't be null");
+        CheckUtils.checkArgument(!targetUserId.equals(jwtUser.getUserId()), "can't set self role");
+        CheckUtils.checkArgument(roomMemberDao.existsByRidAndUid(roomId, targetUserId), "room member not exist");
+
+        log.info("inviteUpgradeRole roomId = {}, targetUserId = {}, targetRole = {}, jwtUser={}", roomId, targetUserId, targetRole, jwtUser);
+
+        List<RoomMember> targetUser = roomMemberDao.findByRidAndUid(roomId, targetUserId);
+        if (targetUser.isEmpty()) {
+            throw new ApiException(ErrorEnum.ERR_USER_NOT_EXIST_IN_ROOM);
+        }
+
+        checkOverMax(roomId, targetUser.get(0), targetRole);
+
+        String ticket = IdentifierUtils.uuid();
+
+        UpgradeRoleTaskInfo taskInfo = new UpgradeRoleTaskInfo();
+        taskInfo.setTicket(ticket);
+        taskInfo.setRoomId(roomId);
+        taskInfo.setApplyUserId(jwtUser.getUserId());
+        taskInfo.setTargetUserId(targetUserId);
+        taskInfo.setRole(RoleEnum.getEnumByValue(targetRole));
+        scheduleManager.addTask(taskInfo);
+
+        UpgradeRoleMessage msg = new UpgradeRoleMessage(ActionEnum.Invite.ordinal());
+        msg.setTicket(ticket);
+        msg.setOpUserId(jwtUser.getUserId());
+        msg.setOpUserName(jwtUser.getUserName());
+        msg.setRole(targetRole);
+        IMApiResultInfo resultInfo = imHelper.publishMessage(jwtUser.getUserId(), targetUserId, roomId, msg);
+        if (resultInfo.isSuccess()) {
+            return true;
+        } else {
+            throw new ApiException(ErrorEnum.ERR_MESSAGE_ERROR, resultInfo.getErrorMessage());
+        }
+    }
+
+    @Override
+    public Boolean approveUpgradeRole(String roomId, String ticket, JwtUser jwtUser) throws ApiException, Exception {
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+        CheckUtils.checkArgument(ticket != null, "ticket must't be null");
+        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");
+        CheckUtils.checkArgument(roomMemberDao.existsByRidAndUid(roomId, jwtUser.getUserId()), "room member not exist");
+
+        UpgradeRoleTaskInfo taskInfo = (UpgradeRoleTaskInfo) scheduleManager.executeTask(ticket);
+        log.info("approveUpgradeRole roomId = {}, task={}, jwtUser={}", roomId, taskInfo, jwtUser);
+
+        List<RoomMember> targetUser = roomMemberDao.findByRidAndUid(roomId, jwtUser.getUserId());
+        if (targetUser.isEmpty()) {
+            throw new ApiException(ErrorEnum.ERR_USER_NOT_EXIST_IN_ROOM);
+        }
+        if (!taskInfo.getTargetUserId().equals(jwtUser.getUserId())) {
+            throw new ApiException(ErrorEnum.ERR_APPLY_TICKET_INVALID);
+        }
+
+        checkOverMax(roomId, targetUser.get(0), taskInfo.getRole().getValue());
+        roomMemberDao.updateRoleByRidAndUid(roomId, jwtUser.getUserId(), taskInfo.getRole().getValue());
+
+        UpgradeRoleMessage msg = new UpgradeRoleMessage(ActionEnum.Approve.ordinal());
+        msg.setOpUserName(jwtUser.getUserName());
+        msg.setOpUserId(jwtUser.getUserId());
+        msg.setRole(taskInfo.getRole().getValue());
+        IMApiResultInfo resultInfo = imHelper.publishMessage(jwtUser.getUserId(), taskInfo.getApplyUserId(), roomId, msg);
+        if (!resultInfo.isSuccess()) {
+            throw new ApiException(ErrorEnum.ERR_MESSAGE_ERROR, resultInfo.getErrorMessage());
+        }
+
+        RoleChangedMessage rcMsg = new RoleChangedMessage(jwtUser.getUserId());
+        List<RoleChangedMessage.ChangedUser> changedUserList = new ArrayList<>();
+        RoleChangedMessage.ChangedUser user = new RoleChangedMessage.ChangedUser(jwtUser.getUserId(), taskInfo.getRole().getValue());
+        user.setUserName(jwtUser.getUserName());
+        changedUserList.add(user);
+        rcMsg.setUsers(changedUserList);
+        imHelper.publishMessage(jwtUser.getUserId(), roomId, rcMsg, 1);
+
+        return true;
+    }
+
+    @Override
+    public Boolean rejectUpgradeRole(String roomId, String ticket, JwtUser jwtUser) throws ApiException, Exception {
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+        CheckUtils.checkArgument(ticket != null, "ticket must't be null");
+        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");
+        CheckUtils.checkArgument(roomMemberDao.existsByRidAndUid(roomId, jwtUser.getUserId()), "room member not exist");
+
+        UpgradeRoleTaskInfo taskInfo = (UpgradeRoleTaskInfo) scheduleManager.executeTask(ticket);
+        UpgradeRoleMessage msg = new UpgradeRoleMessage(ActionEnum.Reject.ordinal());
+        msg.setOpUserName(jwtUser.getUserName());
+        msg.setOpUserId(jwtUser.getUserId());
+        msg.setRole(taskInfo.getRole().getValue());
+        IMApiResultInfo resultInfo = imHelper.publishMessage(jwtUser.getUserId(), taskInfo.getApplyUserId(), roomId, msg);
+        if (resultInfo.isSuccess()) {
+            return true;
+        } else {
+            throw new ApiException(ErrorEnum.ERR_MESSAGE_ERROR, resultInfo.getErrorMessage());
+        }
+    }
+
+    @Override
+    public Boolean changeRole(String roomId, String targetUserId, int targetRole, JwtUser jwtUser) throws ApiException, Exception {
+        CheckUtils.checkArgument(roomId != null, "roomId must't be null");
+        CheckUtils.checkArgument(targetUserId != null, "userId must't be null");
+        CheckUtils.checkArgument(roomDao.existsByRid(roomId), "room not exist");
+        CheckUtils.checkArgument(RoleEnum.getEnumByValue(targetRole).equals(RoleEnum.RoleTeacher), "only set to teacher");
+        CheckUtils.checkArgument(roomMemberDao.existsByRidAndUid(roomId, targetUserId), "room member not exist");
+
+        List<RoomMember> targetUser = roomMemberDao.findByRidAndUid(roomId, targetUserId);
+        if (targetUser.isEmpty()) {
+            throw new ApiException(ErrorEnum.ERR_USER_NOT_EXIST_IN_ROOM);
+        } else {
+            if (!RoleEnum.getEnumByValue(targetUser.get(0).getRole()).equals(RoleEnum.RoleStudent)) {
+                log.error("change role error: {}, targetUserId={}, targetRole = {}", jwtUser, targetUser, RoleEnum.getEnumByValue(targetRole));
+                throw new ApiException(ErrorEnum.ERR_CHANGE_ROLE);
+            }
+        }
+
+        log.info("changeRole: roomId={}, {}, targetUserId={}", roomId, jwtUser, targetUserId);
+        List<RoleChangedMessage.ChangedUser> changedUserList = new ArrayList<>();
+        RoleChangedMessage msg = new RoleChangedMessage(jwtUser.getUserId());
+
+        List<RoomMember> teachers = roomMemberDao.findByRidAndRole(roomId, RoleEnum.RoleTeacher.getValue());
+        if (!teachers.isEmpty()) {
+            roomMemberDao.updateRoleByRidAndUid(roomId, teachers.get(0).getUid(), RoleEnum.RoleStudent.getValue());
+            RoleChangedMessage.ChangedUser user = new RoleChangedMessage.ChangedUser(teachers.get(0).getUid(), RoleEnum.RoleStudent.getValue());
+            List<UserInfo> userInfoList = userDao.findByUid(teachers.get(0).getUid());
+            if (!userInfoList.isEmpty()) {
+                user.setUserName(userInfoList.get(0).getName());
+            }
+            changedUserList.add(user);
+        } else {
+            log.info("change directly cause no teacher exist in room, roomId={}", roomId);
+        }
+
+        roomMemberDao.updateRoleByRidAndUid(roomId, targetUserId, targetRole);
+        RoleChangedMessage.ChangedUser user = new RoleChangedMessage.ChangedUser(targetUserId, targetRole);
+        List<UserInfo> userInfoList = userDao.findByUid(targetUserId);
+        if (!userInfoList.isEmpty()) {
+            user.setUserName(userInfoList.get(0).getName());
+        }
+        changedUserList.add(user);
+        msg.setUsers(changedUserList);
+        imHelper.publishMessage(jwtUser.getUserId(), roomId, msg, 1);
+
+        String display = "display://type=1?userId=" + targetUserId + "?uri=";
+        DisplayMessage displayMessage = new DisplayMessage(display);
+        roomDao.updateDisplayByRid(roomId, display);
+        imHelper.publishMessage(jwtUser.getUserId(), roomId, displayMessage, 1);
+        log.info("changeRole, display changed: roomId={}, {}, targetUserId={}", roomId, display, targetUserId);
+
+        return true;
+    }
+
+   @Override
+    public Boolean memberOnlineStatus(List<ReqMemberOnlineStatus> statusList, String nonce, String timestamp, String signature) throws ApiException, Exception {
+        String sign = imProperties.getSecret() + nonce + timestamp;
+        String signSHA1 = CodeUtil.hexSHA1(sign);
+        if (!signSHA1.equals(signature)) {
+            log.info("memberOnlineStatus signature error");
+            return true;
+        }
+
+        for (ReqMemberOnlineStatus status : statusList) {
+            int s = Integer.parseInt(status.getStatus());
+            String userId = status.getUserId();
+
+            log.info("memberOnlineStatus, userId={}, status={}", userId, status);
+            //1:offline 离线; 0: online 在线
+            if (s == 1) {
+                List<RoomMember> members = roomMemberDao.findByUid(userId);
+                if (!members.isEmpty()) {
+                    scheduleManager.userIMOffline(userId);
+                }
+            } else if (s == 0) {
+                scheduleManager.userIMOnline(userId);
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public void userIMOfflineKick(String userId) {
+        List<RoomMember> members = roomMemberDao.findByUid(userId);
+        for (RoomMember member : members) {
+            int userRole = member.getRole();
+            log.info("userIMOfflineKick: roomId={}, {}, role={}", member.getRid(), userId, RoleEnum.getEnumByValue(userRole));
+            try {
+                if (userRole == RoleEnum.RoleTeacher.getValue() || userRole == RoleEnum.RoleAssistant.getValue()) {
+                    List<Room> rooms = roomDao.findByRid(member.getRid());
+                    if (rooms.isEmpty()) {
+                        break;
+                    }
+                    if (isUserDisplay(rooms.get(0), member.getUid())) {
+                        updateDisplay(member.getRid(), member.getUid(), "", 0);
+                        log.info("memberOnlineStatus offline: roomId={}, {}", member.getRid(), member.getUid());
+                    }
+                }
+                if (roomMemberDao.countByRid(member.getRid()) == 1) {
+                    IMApiResultInfo apiResultInfo = null;
+                    apiResultInfo = imHelper.dismiss(member.getUid(), member.getRid());
+                    if (apiResultInfo.getCode() == 200) {
+                        roomMemberDao.deleteUserByRidAndUid(member.getRid(), member.getUid());
+                        roomDao.deleteByRid(member.getRid());
+                        deleteWhiteboardByUser(member.getRid(), member.getUid());
+                        log.info("dismiss the room: {}", member.getRid());
+                    } else {
+                        log.error("{} exit {} room error: {}", member.getUid(), member.getRid(), apiResultInfo.getErrorMessage());
+                    }
+                } else {
+                    IMApiResultInfo apiResultInfo = null;
+                    apiResultInfo = imHelper.quit(new String[]{member.getUid()}, member.getRid());
+                    if (apiResultInfo.isSuccess()) {
+                        roomMemberDao.deleteUserByRidAndUid(member.getRid(), member.getUid());
+                        MemberChangedMessage msg = new MemberChangedMessage(MemberChangedMessage.Action_Leave, member.getUid(), userRole);
+                        msg.setUserName(member.getName());
+                        imHelper.publishMessage(member.getUid(), member.getRid(), msg);
+                        imHelper.quit(new String[]{member.getUid()}, member.getRid());
+                        log.info("quit group: roomId={}, {}", member.getRid(), member.getUid());
+                    } else {
+                        log.error("{} exit {} room error: {}", member.getUid(), member.getRid(), apiResultInfo.getErrorMessage());
+                    }
+                }
+                userDao.deleteByUid(member.getUid());
+            } catch (Exception e) {
+                log.error("userIMOfflineKick error: userId={}", userId);
+            }
+        }
+    }
+	private void updateDisplay(String roomId, String senderId, String display, Integer isIncludeSender) throws ApiException, Exception {
+        roomDao.updateDisplayByRid(roomId, display);
+        DisplayMessage displayMessage = new DisplayMessage(display);
+        imHelper.publishMessage(senderId, roomId, displayMessage, isIncludeSender);
+    }
+    private boolean isTeacherDisplay(Room room, String userId) {
+        return !room.getDisplay().isEmpty() && room.getDisplay().contains("userId=" + userId);
+    }
+
+    private boolean isTeacherDisplayWhiteboard(Room room, String userId) {
+        return !room.getDisplay().isEmpty() && room.getDisplay().contains("userId=" + userId) && room.getDisplay().contains("type=2");
+    }
+
+    private boolean isAssistantDisplay(Room room, String userId) {
+        return !room.getDisplay().isEmpty() && room.getDisplay().contains("userId=" + userId);
+    }
+
+    private boolean isUserDisplay(Room room, String userId) {
+        boolean result = false;
+        if (!room.getDisplay().isEmpty() && room.getDisplay().contains("userId=" + userId)) {
+            if (room.getDisplay().contains("type=0") || room.getDisplay().contains("type=1") || room.getDisplay().contains("type=3")) {
+                result = true;
+            }
+        }
+        return result;
+    }
+
+
+}

+ 165 - 0
mec-im/src/main/java/com/ym/service/Impl/UserServiceImpl.java

@@ -0,0 +1,165 @@
+package com.ym.service.Impl;
+
+import com.ym.common.ApiException;
+import com.ym.common.ErrorEnum;
+import com.ym.dao.UserDao;
+import com.ym.im.IMHelper;
+import com.ym.pojo.IMTokenInfo;
+import com.ym.service.UserService;
+import io.rong.RongCloud;
+import io.rong.methods.user.User;
+import io.rong.methods.user.blacklist.Blacklist;
+import io.rong.methods.user.block.Block;
+import io.rong.methods.user.mute.MuteChatrooms;
+import io.rong.methods.user.mute.MuteGroups;
+import io.rong.methods.user.tag.Tag;
+import io.rong.models.Result;
+import io.rong.models.chatroom.ChatroomModel;
+import io.rong.models.group.GroupModel;
+import io.rong.models.response.TokenResult;
+import io.rong.models.user.BatchTagModel;
+import io.rong.models.user.GetTagModel;
+import io.rong.models.user.TagModel;
+import io.rong.models.user.UserModel;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+@Slf4j
+@Service
+public class UserServiceImpl implements UserService {
+    @Value("${cn.rongcloud.im.appkey}")
+    private String appKey;
+    @Value("${cn.rongcloud.im.secret}")
+    private String appSecret;
+    @Autowired
+    UserDao userDao;
+
+    @Autowired
+    IMHelper imHelper;
+
+    @Override
+    public String refreshToken(String userId, String name) throws ApiException, Exception  {
+        log.info("request token: {}, {}", userId, name);
+        IMTokenInfo tokenInfo = imHelper.getToken(userId, name, "");
+        if (tokenInfo.isSuccess()) {
+            return tokenInfo.getToken();
+        } else {
+            throw new ApiException(ErrorEnum.ERR_IM_TOKEN_ERROR, tokenInfo.getErrorMessage());
+        }
+    }
+
+    private User getUser(){
+        RongCloud rongCloud = RongCloud.getInstance(appKey, appSecret);
+        return rongCloud.user;
+    }
+    private Blacklist getBlacklist(){
+        RongCloud rongCloud = RongCloud.getInstance(appKey, appSecret);
+        return new Blacklist(appKey,appSecret,rongCloud);
+    }
+    private Block getBlock(){
+        RongCloud rongCloud = RongCloud.getInstance(appKey, appSecret);
+        return new Block(appKey,appSecret,rongCloud);
+    }
+    private Tag getTag(){
+        RongCloud rongCloud = RongCloud.getInstance(appKey, appSecret);
+        return new Tag(appKey,appSecret,rongCloud);
+    }
+    private MuteGroups getMuteGroups(){
+        RongCloud rongCloud = RongCloud.getInstance(appKey, appSecret);
+        return new MuteGroups(appKey,appSecret,rongCloud);
+    }
+    private MuteChatrooms getMuteChatrooms(){
+        RongCloud rongCloud = RongCloud.getInstance(appKey, appSecret);
+        return new MuteChatrooms(appKey,appSecret,rongCloud);
+    }
+
+    @Override
+    public TokenResult register(UserModel userModel) throws Exception {
+        return getUser().register(userModel);
+    }
+
+    @Override
+    public Result update(UserModel userModel) throws Exception {
+        return getUser().update(userModel);
+    }
+
+    @Override
+    public Result blackListAdd(UserModel userModel) throws Exception {
+        return getBlacklist().add(userModel);
+    }
+
+    @Override
+    public Result blackListRemove(UserModel userModel) throws Exception {
+        return getBlacklist().remove(userModel);
+    }
+
+    @Override
+    public Result blackListGetList(UserModel userModel) throws Exception {
+        return getBlacklist().getList(userModel);
+    }
+
+    @Override
+    public Result blockAdd(UserModel userModel) throws Exception {
+        return getBlock().add(userModel);
+    }
+
+    @Override
+    public Result blockRemove(String userId) throws Exception {
+        return getBlock().remove(userId);
+    }
+
+    @Override
+    public Result blockGetList() throws Exception {
+        return getBlock().getList();
+    }
+
+    @Override
+    public Result tagSet(TagModel tagModel) throws Exception {
+        return getTag().set(tagModel);
+    }
+
+    @Override
+    public Result tagBatchSet(BatchTagModel batchTagModel) throws Exception {
+        return getTag().batchSet(batchTagModel);
+    }
+
+    @Override
+    public Result tagGet(GetTagModel getTagModel) throws Exception {
+        return getTag().get(getTagModel);
+    }
+
+    @Override
+    public Result muteGroupsAdd(GroupModel groupModel) throws Exception {
+        return getMuteGroups().add(groupModel);
+    }
+
+    @Override
+    public Result muteGroupsRemove(GroupModel groupModel) throws Exception {
+        return getMuteGroups().remove(groupModel);
+    }
+
+    @Override
+    public Result muteGroupsGetList() throws Exception {
+        return getMuteGroups().getList();
+    }
+
+    @Override
+    public Result muteChatroomsAdd(ChatroomModel chatroomModel) throws Exception {
+        return getMuteChatrooms().add(chatroomModel);
+    }
+
+    @Override
+    public Result muteChatroomsRemove(ChatroomModel chatroomModel) throws Exception {
+        return getMuteChatrooms().remove(chatroomModel);
+    }
+
+    @Override
+    public Result muteChatroomsGetList() throws Exception {
+        return getMuteChatrooms().getList();
+    }
+}

+ 51 - 0
mec-im/src/main/java/com/ym/service/MessageService.java

@@ -0,0 +1,51 @@
+package com.ym.service;
+
+import io.rong.models.Result;
+import io.rong.models.message.*;
+import io.rong.models.push.BroadcastModel;
+import io.rong.models.push.PushModel;
+
+public interface MessageService {
+
+    Result privateSend(PrivateMessage privateMessage) throws Exception;
+
+    Result privateRecall(RecallMessage recallMessage) throws Exception;
+
+    Result privateSendTemplate(TemplateMessage templateMessage) throws Exception;
+
+
+    //群组消息
+    Result groupSend(GroupMessage groupMessage) throws Exception;
+
+    Result groupSendDirection(GroupMessage groupMessage) throws Exception;
+
+    Result groupSendMention(MentionMessage mentionMessage) throws Exception;
+
+    Result groupRecall(RecallMessage recallMessage) throws Exception;
+
+
+    //聊天室
+    Result chatroomSend(ChatroomMessage chatroomMessage) throws Exception;
+
+    Result chatroomBroadcast(ChatroomMessage chatroomMessage) throws Exception;
+
+
+    //系统消息
+    Result systemSend(SystemMessage systemMessage) throws Exception;
+
+    Result systemBroadcast(BroadcastMessage broadcastMessage) throws Exception;
+
+    Result systemSendTemplate(TemplateMessage templateMessage) throws Exception;
+
+
+    //历史消息
+    Result historyGet(String date) throws Exception;
+
+    Result historyRemove(String date) throws Exception;
+
+
+    //广播推送模块
+    Result pushPush(PushModel pushModel) throws Exception;
+
+    Result pushMessage(BroadcastModel broadcastModel) throws Exception;
+}

+ 67 - 0
mec-im/src/main/java/com/ym/service/RoomService.java

@@ -0,0 +1,67 @@
+package com.ym.service;
+
+import com.ym.common.ApiException;
+import com.ym.common.JwtUser;
+import com.ym.pojo.DeviceTypeEnum;
+import com.ym.pojo.ReqChangeUserRoleData;
+import com.ym.pojo.ReqMemberOnlineStatus;
+import com.ym.pojo.RoomResult;
+
+import java.util.List;
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+public interface RoomService {
+    //everyone
+    public RoomResult joinRoom(String userName, String roomId, boolean isAudience, boolean isDisableCamera, JwtUser jwtUser) throws ApiException, Exception;
+
+    public Boolean leaveRoom(JwtUser jwtUser, String roomId) throws ApiException, Exception;
+
+    //only host
+    public Boolean downgrade(String roomId, JwtUser jwtUser, List<ReqChangeUserRoleData.ChangedUser> users) throws ApiException, Exception;
+
+    public Boolean kickMember(String roomId, String userId, JwtUser jwtUser) throws ApiException, Exception;
+
+
+    //only teacher
+    public Boolean display(String roomId, int type, String userId, String uri, JwtUser jwtUser) throws ApiException, Exception;
+
+    public String createWhiteBoard(String roomId, JwtUser jwtUser) throws ApiException, Exception;
+
+    public Boolean deleteWhiteboard(String roomId, JwtUser jwtUser, String whiteBoardId) throws ApiException, Exception;
+
+    public List<RoomResult.WhiteboardResult> getWhiteboard(String roomId, JwtUser jwtUser) throws ApiException, Exception;
+
+    public Boolean turnWhiteBoardPage(String roomId, String whiteBoardId, int page, JwtUser jwtUser) throws ApiException, Exception;
+
+    public Boolean controlDevice(String roomId, String userId, DeviceTypeEnum type, boolean enable, JwtUser jwtUser) throws ApiException, Exception;
+
+    public Boolean approveControlDevice(String roomId, String ticket, JwtUser jwtUser) throws ApiException, Exception;
+
+    public Boolean rejectControlDevice(String roomId, String ticket, JwtUser jwtUser) throws ApiException, Exception;
+
+
+    public List<RoomResult.MemberResult> getMembers(String roomId, JwtUser jwtUser) throws  ApiException, Exception;
+
+    public Boolean applySpeech(String roomId, JwtUser jwtUser) throws  ApiException, Exception;
+
+    public Boolean approveSpeech(String roomId, String requestId, JwtUser jwtUser) throws  ApiException, Exception;
+
+    public Boolean rejectSpeech(String roomId, String requestId, JwtUser jwtUser) throws  ApiException, Exception;
+
+
+    public Boolean transfer(String roomId, String userId, JwtUser jwtUser) throws ApiException, Exception;
+
+    public Boolean inviteUpgradeRole(String roomId, String userId, int role, JwtUser jwtUser) throws  ApiException, Exception;
+    public Boolean approveUpgradeRole(String roomId, String ticket, JwtUser jwtUser) throws ApiException, Exception;
+    public Boolean rejectUpgradeRole(String roomId, String ticket, JwtUser jwtUser) throws ApiException, Exception;
+
+    public Boolean syncDeviceState(String roomId, DeviceTypeEnum type, boolean enable, JwtUser jwtUser) throws ApiException, Exception;
+
+    public Boolean changeRole(String roomId, String userId, int role, JwtUser jwtUser) throws ApiException, Exception;
+
+    public void destroyRoom(String roomId);
+    public Boolean memberOnlineStatus(List<ReqMemberOnlineStatus> statusList, String nonce, String timestamp, String signature) throws ApiException, Exception;
+    public void userIMOfflineKick(String userId);
+}

+ 64 - 0
mec-im/src/main/java/com/ym/service/UserService.java

@@ -0,0 +1,64 @@
+package com.ym.service;
+
+import com.ym.common.ApiException;
+import io.rong.models.Result;
+import io.rong.models.chatroom.ChatroomModel;
+import io.rong.models.group.GroupModel;
+import io.rong.models.response.TokenResult;
+import io.rong.models.user.BatchTagModel;
+import io.rong.models.user.GetTagModel;
+import io.rong.models.user.TagModel;
+import io.rong.models.user.UserModel;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+public interface UserService {
+
+    public String refreshToken(String userId, String name) throws ApiException, Exception;
+
+    //用户
+    TokenResult register(UserModel userModel) throws Exception;
+
+    Result update(UserModel userModel) throws Exception;
+
+
+    //黑名单
+    Result blackListAdd(UserModel userModel) throws Exception;
+
+    Result blackListRemove(UserModel userModel) throws Exception;
+
+    Result blackListGetList(UserModel userModel) throws Exception;
+
+
+    //用户封禁
+    Result blockAdd(UserModel userModel) throws Exception;
+
+    Result blockRemove(String userId) throws Exception;
+
+    Result blockGetList() throws Exception;
+
+
+    //用户标签
+    Result tagSet(TagModel tagModel) throws Exception;
+
+    Result tagBatchSet(BatchTagModel batchTagModel) throws Exception;
+
+    Result tagGet(GetTagModel getTagModel) throws Exception;
+
+
+    //全局群主禁言
+    Result muteGroupsAdd(GroupModel groupModel) throws Exception;
+
+    Result muteGroupsRemove(GroupModel groupModel) throws Exception;
+
+    Result muteGroupsGetList() throws Exception;
+
+
+    //全局聊天室禁言
+    Result muteChatroomsAdd(ChatroomModel chatroomModel) throws Exception;
+
+    Result muteChatroomsRemove(ChatroomModel chatroomModel) throws Exception;
+
+    Result muteChatroomsGetList() throws Exception;
+}

+ 12 - 0
mec-im/src/main/java/com/ym/utils/CheckUtils.java

@@ -0,0 +1,12 @@
+package com.ym.utils;
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+public class CheckUtils {
+    public static void checkArgument(boolean condition, String message) throws IllegalArgumentException {
+        if (!condition) {
+            throw new IllegalArgumentException(message);
+        }
+    }
+}

+ 34 - 0
mec-im/src/main/java/com/ym/utils/CodeUtil.java

@@ -0,0 +1,34 @@
+package com.ym.utils;
+
+import org.apache.commons.codec.binary.Hex;
+
+import java.security.MessageDigest;
+
+public class CodeUtil {
+
+    public static String hexSHA1(String value) {
+        try {
+            MessageDigest md = MessageDigest.getInstance("SHA-1");
+            md.update(value.getBytes("utf-8"));
+            byte[] digest = md.digest();
+            return byteToHexString(digest);
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    public static String hexMD5(String value) {
+        try {
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            md.update(value.getBytes("utf-8"));
+            byte[] digest = md.digest();
+            return byteToHexString(digest);
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    public static String byteToHexString(byte[] bytes) {
+        return String.valueOf(Hex.encodeHex(bytes));
+    }
+}

+ 15 - 0
mec-im/src/main/java/com/ym/utils/DateTimeUtils.java

@@ -0,0 +1,15 @@
+package com.ym.utils;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+
+import java.util.Date;
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+public class DateTimeUtils {
+    public static Date currentUTC() {
+        return new DateTime(DateTimeZone.UTC).toDate();
+    }
+}

+ 45 - 0
mec-im/src/main/java/com/ym/utils/IdentifierUtils.java

@@ -0,0 +1,45 @@
+package com.ym.utils;
+
+/**
+ * Created by weiqinxiao on 2019/2/25.
+ */
+import org.apache.commons.lang.RandomStringUtils;
+
+import java.nio.ByteBuffer;
+import java.util.Base64;
+import java.util.UUID;
+
+public class IdentifierUtils {
+
+    private IdentifierUtils() {
+        throw new IllegalStateException("IdentifierUtils Utility class");
+    }
+
+    public static String random(int length) {
+        return RandomStringUtils.randomAlphanumeric(length);
+    }
+
+    public static String uuid() {
+        return uuid24();
+    }
+
+    public static String uuid24() {
+        UUID uuid = UUID.randomUUID();
+        return base64Encode(uuid.getMostSignificantBits()) + base64Encode(
+                uuid.getLeastSignificantBits());
+    }
+
+    public static String uuid32() {
+        return uuid36().replaceAll("-", "");
+    }
+
+    public static String uuid36() {
+        return UUID.randomUUID().toString();
+    }
+
+    private static String base64Encode(long value) {
+        ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES).putLong(value);
+        return Base64.getUrlEncoder().withoutPadding().encodeToString(buffer.array());
+    }
+
+}

+ 79 - 0
mec-im/src/main/java/com/ym/utils/SecurityUtils.java

@@ -0,0 +1,79 @@
+package com.ym.utils;
+
+import org.apache.commons.lang.RandomStringUtils;
+
+import javax.crypto.*;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.NoSuchAlgorithmException;
+
+public class SecurityUtils {
+	public static enum HmacAlgorithm {
+		HMAC_MD5("HmacMD5"), HMAC_SHA1("HmacSHA1"), HMAC_SHA256("HmacSHA256"), HMAC_SHA384("HmacSHA384"), HMAC_SHA512(
+				"HmacSHA512");
+
+		private String algorithm;
+
+		private HmacAlgorithm(String algorithm) {
+			this.algorithm = algorithm;
+		}
+
+		public String getAlgorithm() {
+			return algorithm;
+		}
+	}
+	//AES加密方法
+	public static final String AES_ECB_PKCS7 = "AES/ECB/PKCS7Padding";
+    public static final String AES_ECB_PKCS5 = "AES/ECB/PKCS5Padding";
+    public static final String AES_CBC_PKCS7 = "AES/CBC/PKCS7Padding";
+    public static final String AES_CBC_PKCS5 = "AES/CBC/PKCS5Padding";
+
+	public static byte[] encryptHMAC(String secret, String data, HmacAlgorithm algorithm) throws IOException {
+		return encryptHMAC(secret.getBytes("utf-8"), data.getBytes("utf-8"), algorithm);
+	}
+
+	public static byte[] encryptHMAC(byte[] secret, byte[] data, HmacAlgorithm algorithm) throws IOException {
+		byte[] bytes = null;
+		try {
+			SecretKey secretKey = new SecretKeySpec(secret, algorithm.getAlgorithm());
+			Mac mac = Mac.getInstance(secretKey.getAlgorithm());
+			mac.init(secretKey);
+			bytes = mac.doFinal(data);
+		} catch (GeneralSecurityException gse) {
+			throw new IOException(gse.toString());
+		}
+		return bytes;
+	}
+
+	public static String byte2hex(byte[] bytes) {
+		StringBuilder sign = new StringBuilder();
+		for (byte aByte : bytes) {
+			String hex = Integer.toHexString(aByte & 0xFF);
+			if (hex.length() == 1) {
+				sign.append("0");
+			}
+			sign.append(hex);
+		}
+		return sign.toString();
+	}
+
+	public static String generateSalt(int length) {
+		return RandomStringUtils.random(length);
+	}
+
+	public static byte[] encryptWithAES128ECB(byte[] in, String key) throws UnsupportedEncodingException, NoSuchAlgorithmException,
+			NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
+		if (key == null || key.length() != 16) {
+			throw new InvalidParameterException("key is invalid");
+		}
+
+		SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("utf-8"), "AES");
+		Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+		cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
+		return cipher.doFinal(in);
+	}
+}

+ 56 - 0
mec-im/src/main/java/com/ym/whiteboard/WhiteBoardHelper.java

@@ -0,0 +1,56 @@
+package com.ym.whiteboard;
+
+import com.alibaba.fastjson.JSON;
+import com.ym.config.IMProperties;
+import com.ym.config.WhiteBoardProperties;
+import com.ym.http.HttpHelper;
+import com.ym.pojo.WhiteBoardApiResultInfo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.net.HttpURLConnection;
+import java.net.URLEncoder;
+
+/**
+ * Created by weiqinxiao on 2019/3/7.
+ */
+@Component
+public class WhiteBoardHelper {
+    private static final String UTF8 = "UTF-8";
+
+    @Autowired
+    HttpHelper httpHelper;
+
+    @Autowired
+    WhiteBoardProperties whiteBoardProperties;
+    @Autowired
+    IMProperties imProperties;
+
+    public WhiteBoardApiResultInfo create(String roomId) throws Exception {
+        if (roomId == null) {
+            throw new IllegalArgumentException("Paramer 'roomId' is required");
+        }
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("&appId=").append(URLEncoder.encode(imProperties.getAppKey(), UTF8));
+        sb.append("&roomNr=").append(URLEncoder.encode(roomId, UTF8));
+        HttpURLConnection connection= httpHelper.createWhiteBoardPostHttpConnection(whiteBoardProperties.getHost(), "/room/create", "application/x-www-form-urlencoded");
+        httpHelper.setBodyParameter(sb, connection);
+
+        return JSON.parseObject(httpHelper.returnResult(connection, sb.toString()), WhiteBoardApiResultInfo.class);
+    }
+
+    public WhiteBoardApiResultInfo destroy(String roomId) throws Exception {
+        if (roomId == null) {
+            throw new IllegalArgumentException("Paramer 'roomId' is required");
+        }
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("&appId=").append(URLEncoder.encode(imProperties.getAppKey(), UTF8));
+        sb.append("&roomNr=").append(URLEncoder.encode(roomId, UTF8));
+        HttpURLConnection connection= httpHelper.createWhiteBoardPostHttpConnection(whiteBoardProperties.getHost(), "/room/destroy", "application/x-www-form-urlencoded");
+        httpHelper.setBodyParameter(sb, connection);
+
+        return JSON.parseObject(httpHelper.returnResult(connection, sb.toString()), WhiteBoardApiResultInfo.class);
+    }
+}

+ 98 - 0
mec-im/src/main/resources/application.yml

@@ -0,0 +1,98 @@
+server:
+  port: 9999
+  tomcat:
+    uri-encoding: UTF-8
+  ssl:
+    enabled: false
+    key-alias: sealclass
+    key-store: classpath:sealclass.key
+    key-store-password: 123456
+
+eureka:
+  client:
+    serviceUrl:
+      defaultZone: http://admin:admin123@localhost:8761/eureka/eureka/
+
+spring:
+  application:
+    name: im-server
+    
+  datasource:
+    name: test
+    url: jdbc:mysql://47.99.212.176:3306/mec_dev?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai
+    username: mec_dev
+    password: mec_dev
+    # 使用druid数据源
+    type: com.alibaba.druid.pool.DruidDataSource
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    filters: stat
+    maxActive: 20
+    initialSize: 1
+    maxWait: 60000
+    minIdle: 1
+    timeBetweenEvictionRunsMillis: 60000
+    minEvictableIdleTimeMillis: 300000
+    validationQuery: select 'x'
+    testWhileIdle: true
+    testOnBorrow: false
+    testOnReturn: false
+    poolPreparedStatements: true
+    maxOpenPreparedStatements: 20
+    
+  redis:
+    host: 47.99.212.176
+    port: 6379
+    password: 
+    database: 0
+    #连接超时时间(毫秒)
+    timeout: 10000
+    pool:
+      #连接池最大连接数(使用负值表示没有限制)
+      max-active: 10
+      #连接池最大阻塞等待时间(使用负值表示没有限制)
+      max-wait: -1
+      #连接池中的最大空闲连接
+      max-idle: 10
+      #连接池中的最小空闲连接
+      min-idle: 0
+    
+swagger:
+  base-package: com.ym.controller
+ 
+##认证 
+security:
+  oauth2:
+    client:
+      client-id: app
+      client-secret: app
+    resource:
+      token-info-uri: http://localhost:8001/oauth/check_token
+  
+#spring boot admin 相关配置
+management:
+  endpoints:
+    web:
+      exposure:
+        include: "*"
+  endpoint:
+    health:
+      show-details: ALWAYS
+
+cn:
+  rongcloud:
+    jwt:
+      secret: ay9pL#$Ws8Lpapo
+      ttlInMilliSec: -1
+    im:
+      appkey: c9kqb3rdc451j
+      secret: gnskN9VRnbm
+      host: http://api-cn.ronghub.com
+    whiteboard:
+      host: https://sealclass.rongcloud.cn/ewb
+    web:
+      enableCors: true
+    room:
+      maxCount: 16
+      taskTtl: 30000
+      roomTtl: 7200000
+      userIMOfflineKickTtl: 300000

+ 16 - 0
mec-im/src/main/resources/bootstrap.properties

@@ -0,0 +1,16 @@
+#\u6307\u5b9a\u5f00\u53d1\u73af\u5883
+#spring.profiles.active=dev
+#\u670d\u52a1\u5668\u5730\u5740
+spring.cloud.nacos.config.server-addr=47.99.212.176:8848
+#\u9ed8\u8ba4\u4e3aPublic\u547d\u540d\u7a7a\u95f4,\u53ef\u4ee5\u7701\u7565\u4e0d\u5199
+spring.cloud.nacos.config.namespace=e246d169-227d-4012-8c34-e90e057d95d2
+#\u6307\u5b9a\u914d\u7f6e\u7fa4\u7ec4 --\u5982\u679c\u662fPublic\u547d\u540d\u7a7a\u95f4 \u5219\u53ef\u4ee5\u7701\u7565\u7fa4\u7ec4\u914d\u7f6e
+spring.cloud.nacos.config.group=DEFAULT_GROUP
+#\u6587\u4ef6\u540d -- \u5982\u679c\u6ca1\u6709\u914d\u7f6e\u5219\u9ed8\u8ba4\u4e3a ${spring.appliction.name}
+spring.cloud.nacos.config.prefix=im
+#\u6307\u5b9a\u6587\u4ef6\u540e\u7f00
+spring.cloud.nacos.config.file-extension=yaml
+#\u662f\u5426\u52a8\u6001\u5237\u65b0
+spring.cloud.nacos.config.refresh.enabled=true
+#\u662f\u5426\u542f\u7528nacos\u914d\u7f6e\u4e2d\u5fc3
+spring.cloud.nacos.config.enabled=true

+ 15 - 0
mec-im/src/main/resources/logback-spring.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <include resource="org/springframework/boot/logging/logback/base.xml" />
+    <springProfile name="dev">
+        <logger name="cn.rongcloud" level="INFO" additivity="false">
+            <appender-ref ref="CONSOLE" />
+            <appender-ref ref="FILE" />
+        </logger>
+    </springProfile>
+    <springProfile name="prod">
+        <logger name="cn.rongcloud" level="INFO" additivity="false">
+            <appender-ref ref="FILE" />
+        </logger>
+    </springProfile>
+</configuration>

Vissa filer visades inte eftersom för många filer har ändrats