瀏覽代碼

Merge branch 'websockt-whiteboard' into saas

Eric 2 年之前
父節點
當前提交
3e13350314
共有 26 個文件被更改,包括 2476 次插入93 次删除
  1. 1 0
      .gitignore
  2. 1 1
      mec-auth/mec-auth-api/src/main/java/com/ym/mec/auth/api/client/fallback/SysUserFeignServiceFallback.java
  3. 96 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/dao/websocket/WsConnectRecordInfoMapper.java
  4. 302 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/entity/websocket/WsConnectRecordInfo.java
  5. 873 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/entity/websocket/WsConnectRecordInfoExample.java
  6. 26 0
      mec-biz/src/main/java/com/ym/mec/biz/service/WsConnectService.java
  7. 86 0
      mec-biz/src/main/java/com/ym/mec/biz/service/impl/WsConnectServiceImpl.java
  8. 351 0
      mec-biz/src/main/resources/config/mybatis/WsConnectRecordInfoMapper.xml
  9. 6 1
      mec-websocket/pom.xml
  10. 0 17
      mec-websocket/src/main/java/com/ym/mec/web/WebSocketApplication.java
  11. 0 52
      mec-websocket/src/main/java/com/ym/mec/web/config/PermissionCheckService.java
  12. 4 4
      mec-websocket/src/main/java/com/ym/mec/web/config/ResourceServerConfig.java
  13. 73 0
      mec-websocket/src/main/java/com/ym/mec/web/config/SocketIoConfig.java
  14. 40 7
      mec-websocket/src/main/java/com/ym/mec/web/config/WebMvcConfig.java
  15. 3 10
      mec-websocket/src/main/java/com/ym/mec/web/controller/WebsocketController.java
  16. 46 0
      mec-websocket/src/main/java/com/ym/mec/web/handler/Chat2.java
  17. 274 0
      mec-websocket/src/main/java/com/ym/mec/web/handler/WhiteboardHandler.java
  18. 33 0
      mec-websocket/src/main/java/com/ym/mec/web/handler/WhiteboardMessage.java
  19. 11 0
      mec-websocket/src/main/java/com/ym/mec/web/support/anno/NamespaceReference.java
  20. 12 0
      mec-websocket/src/main/java/com/ym/mec/web/support/anno/OnNamespace.java
  21. 31 0
      mec-websocket/src/main/java/com/ym/mec/web/support/mes/ChatObject.java
  22. 46 0
      mec-websocket/src/main/java/com/ym/mec/web/support/socket/NamespaceFactoryBean.java
  23. 46 0
      mec-websocket/src/main/java/com/ym/mec/web/support/socket/ServerRunner.java
  24. 89 0
      mec-websocket/src/main/java/com/ym/mec/web/support/socket/SocketEventScanner.java
  25. 0 0
      mec-websocket/src/main/resources/application-template.yml
  26. 26 1
      pom.xml

+ 1 - 0
.gitignore

@@ -35,6 +35,7 @@ mvnw.cmd
 **/bootstrap-dev.properties
 **/bootstrap-local.yml
 **/bootstrap-local.properties
+**/logback-test.xml
 
 /bin/
 **/logback-spring.xml

+ 1 - 1
mec-auth/mec-auth-api/src/main/java/com/ym/mec/auth/api/client/fallback/SysUserFeignServiceFallback.java

@@ -9,7 +9,7 @@ import com.ym.mec.auth.api.client.SysUserFeignService;
 import com.ym.mec.auth.api.entity.SysUser;
 import com.ym.mec.common.entity.HttpResponseResult;
 
-@Component
+//@Component
 public class SysUserFeignServiceFallback implements SysUserFeignService {
 
 	@Override

+ 96 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/dao/websocket/WsConnectRecordInfoMapper.java

@@ -0,0 +1,96 @@
+package com.ym.mec.biz.dal.dao.websocket;
+
+import com.ym.mec.biz.dal.entity.websocket.WsConnectRecordInfo;
+import com.ym.mec.biz.dal.entity.websocket.WsConnectRecordInfoExample;
+import java.util.List;
+import org.apache.ibatis.annotations.Param;
+
+public interface WsConnectRecordInfoMapper {
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Mon Oct 31 11:58:02 CST 2022
+     */
+    long countByExample(WsConnectRecordInfoExample example);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Mon Oct 31 11:58:02 CST 2022
+     */
+    int deleteByExample(WsConnectRecordInfoExample example);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Mon Oct 31 11:58:02 CST 2022
+     */
+    int deleteByPrimaryKey(Long id);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Mon Oct 31 11:58:02 CST 2022
+     */
+    int insert(WsConnectRecordInfo record);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Mon Oct 31 11:58:02 CST 2022
+     */
+    int insertSelective(WsConnectRecordInfo record);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Mon Oct 31 11:58:02 CST 2022
+     */
+    List<WsConnectRecordInfo> selectByExample(WsConnectRecordInfoExample example);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Mon Oct 31 11:58:02 CST 2022
+     */
+    WsConnectRecordInfo selectByPrimaryKey(Long id);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Mon Oct 31 11:58:02 CST 2022
+     */
+    int updateByExampleSelective(@Param("record") WsConnectRecordInfo record, @Param("example") WsConnectRecordInfoExample example);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Mon Oct 31 11:58:02 CST 2022
+     */
+    int updateByExample(@Param("record") WsConnectRecordInfo record, @Param("example") WsConnectRecordInfoExample example);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Mon Oct 31 11:58:02 CST 2022
+     */
+    int updateByPrimaryKeySelective(WsConnectRecordInfo record);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Mon Oct 31 11:58:02 CST 2022
+     */
+    int updateByPrimaryKey(WsConnectRecordInfo record);
+}

+ 302 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/entity/websocket/WsConnectRecordInfo.java

@@ -0,0 +1,302 @@
+package com.ym.mec.biz.dal.entity.websocket;
+
+import java.util.Date;
+
+public class WsConnectRecordInfo {
+    /**
+     *
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database column ws_connect_record_info.id_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    private Long id;
+
+    /**
+     *
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database column ws_connect_record_info.session_id_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    private String sessionId;
+
+    /**
+     *
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database column ws_connect_record_info.room_id_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    private String roomId;
+
+    /**
+     *
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database column ws_connect_record_info.user_id_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    private Long userId;
+
+    /**
+     *
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database column ws_connect_record_info.client_type_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    private String clientType;
+
+    /**
+     *
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database column ws_connect_record_info.connect_time_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    private Date connectTime;
+
+    /**
+     *
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database column ws_connect_record_info.disconnect_time_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    private Date disconnectTime;
+
+    /**
+     *
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database column ws_connect_record_info.online_time_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    private Long onlineTime;
+
+    /**
+     *
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database column ws_connect_record_info.created_time_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    private Date createdTime;
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method returns the value of the database column ws_connect_record_info.id_
+     *
+     * @return the value of ws_connect_record_info.id_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method sets the value of the database column ws_connect_record_info.id_
+     *
+     * @param id the value for ws_connect_record_info.id_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method returns the value of the database column ws_connect_record_info.session_id_
+     *
+     * @return the value of ws_connect_record_info.session_id_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public String getSessionId() {
+        return sessionId;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method sets the value of the database column ws_connect_record_info.session_id_
+     *
+     * @param sessionId the value for ws_connect_record_info.session_id_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public void setSessionId(String sessionId) {
+        this.sessionId = sessionId;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method returns the value of the database column ws_connect_record_info.room_id_
+     *
+     * @return the value of ws_connect_record_info.room_id_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public String getRoomId() {
+        return roomId;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method sets the value of the database column ws_connect_record_info.room_id_
+     *
+     * @param roomId the value for ws_connect_record_info.room_id_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public void setRoomId(String roomId) {
+        this.roomId = roomId;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method returns the value of the database column ws_connect_record_info.user_id_
+     *
+     * @return the value of ws_connect_record_info.user_id_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public Long getUserId() {
+        return userId;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method sets the value of the database column ws_connect_record_info.user_id_
+     *
+     * @param userId the value for ws_connect_record_info.user_id_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method returns the value of the database column ws_connect_record_info.client_type_
+     *
+     * @return the value of ws_connect_record_info.client_type_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public String getClientType() {
+        return clientType;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method sets the value of the database column ws_connect_record_info.client_type_
+     *
+     * @param clientType the value for ws_connect_record_info.client_type_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public void setClientType(String clientType) {
+        this.clientType = clientType;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method returns the value of the database column ws_connect_record_info.connect_time_
+     *
+     * @return the value of ws_connect_record_info.connect_time_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public Date getConnectTime() {
+        return connectTime;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method sets the value of the database column ws_connect_record_info.connect_time_
+     *
+     * @param connectTime the value for ws_connect_record_info.connect_time_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public void setConnectTime(Date connectTime) {
+        this.connectTime = connectTime;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method returns the value of the database column ws_connect_record_info.disconnect_time_
+     *
+     * @return the value of ws_connect_record_info.disconnect_time_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public Date getDisconnectTime() {
+        return disconnectTime;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method sets the value of the database column ws_connect_record_info.disconnect_time_
+     *
+     * @param disconnectTime the value for ws_connect_record_info.disconnect_time_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public void setDisconnectTime(Date disconnectTime) {
+        this.disconnectTime = disconnectTime;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method returns the value of the database column ws_connect_record_info.online_time_
+     *
+     * @return the value of ws_connect_record_info.online_time_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public Long getOnlineTime() {
+        return onlineTime;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method sets the value of the database column ws_connect_record_info.online_time_
+     *
+     * @param onlineTime the value for ws_connect_record_info.online_time_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public void setOnlineTime(Long onlineTime) {
+        this.onlineTime = onlineTime;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method returns the value of the database column ws_connect_record_info.created_time_
+     *
+     * @return the value of ws_connect_record_info.created_time_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public Date getCreatedTime() {
+        return createdTime;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method sets the value of the database column ws_connect_record_info.created_time_
+     *
+     * @param createdTime the value for ws_connect_record_info.created_time_
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public void setCreatedTime(Date createdTime) {
+        this.createdTime = createdTime;
+    }
+}

+ 873 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/entity/websocket/WsConnectRecordInfoExample.java

@@ -0,0 +1,873 @@
+package com.ym.mec.biz.dal.entity.websocket;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class WsConnectRecordInfoExample {
+    /**
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    protected String orderByClause;
+
+    /**
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    protected boolean distinct;
+
+    /**
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    protected List<Criteria> oredCriteria;
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public WsConnectRecordInfoExample() {
+        oredCriteria = new ArrayList<Criteria>();
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public void setOrderByClause(String orderByClause) {
+        this.orderByClause = orderByClause;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public String getOrderByClause() {
+        return orderByClause;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public void setDistinct(boolean distinct) {
+        this.distinct = distinct;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public boolean isDistinct() {
+        return distinct;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public List<Criteria> getOredCriteria() {
+        return oredCriteria;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public void or(Criteria criteria) {
+        oredCriteria.add(criteria);
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public Criteria or() {
+        Criteria criteria = createCriteriaInternal();
+        oredCriteria.add(criteria);
+        return criteria;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public Criteria createCriteria() {
+        Criteria criteria = createCriteriaInternal();
+        if (oredCriteria.size() == 0) {
+            oredCriteria.add(criteria);
+        }
+        return criteria;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    protected Criteria createCriteriaInternal() {
+        Criteria criteria = new Criteria();
+        return criteria;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public void clear() {
+        oredCriteria.clear();
+        orderByClause = null;
+        distinct = false;
+    }
+
+    /**
+     * This class was generated by MyBatis Generator.
+     * This class corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    protected abstract static class GeneratedCriteria {
+        protected List<Criterion> criteria;
+
+        protected GeneratedCriteria() {
+            super();
+            criteria = new ArrayList<Criterion>();
+        }
+
+        public boolean isValid() {
+            return criteria.size() > 0;
+        }
+
+        public List<Criterion> getAllCriteria() {
+            return criteria;
+        }
+
+        public List<Criterion> getCriteria() {
+            return criteria;
+        }
+
+        protected void addCriterion(String condition) {
+            if (condition == null) {
+                throw new RuntimeException("Value for condition cannot be null");
+            }
+            criteria.add(new Criterion(condition));
+        }
+
+        protected void addCriterion(String condition, Object value, String property) {
+            if (value == null) {
+                throw new RuntimeException("Value for " + property + " cannot be null");
+            }
+            criteria.add(new Criterion(condition, value));
+        }
+
+        protected void addCriterion(String condition, Object value1, Object value2, String property) {
+            if (value1 == null || value2 == null) {
+                throw new RuntimeException("Between values for " + property + " cannot be null");
+            }
+            criteria.add(new Criterion(condition, value1, value2));
+        }
+
+        public Criteria andIdIsNull() {
+            addCriterion("id_ is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdIsNotNull() {
+            addCriterion("id_ is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdEqualTo(Long value) {
+            addCriterion("id_ =", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdNotEqualTo(Long value) {
+            addCriterion("id_ <>", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdGreaterThan(Long value) {
+            addCriterion("id_ >", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdGreaterThanOrEqualTo(Long value) {
+            addCriterion("id_ >=", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdLessThan(Long value) {
+            addCriterion("id_ <", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdLessThanOrEqualTo(Long value) {
+            addCriterion("id_ <=", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdIn(List<Long> values) {
+            addCriterion("id_ in", values, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdNotIn(List<Long> values) {
+            addCriterion("id_ not in", values, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdBetween(Long value1, Long value2) {
+            addCriterion("id_ between", value1, value2, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdNotBetween(Long value1, Long value2) {
+            addCriterion("id_ not between", value1, value2, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andSessionIdIsNull() {
+            addCriterion("session_id_ is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andSessionIdIsNotNull() {
+            addCriterion("session_id_ is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andSessionIdEqualTo(String value) {
+            addCriterion("session_id_ =", value, "sessionId");
+            return (Criteria) this;
+        }
+
+        public Criteria andSessionIdNotEqualTo(String value) {
+            addCriterion("session_id_ <>", value, "sessionId");
+            return (Criteria) this;
+        }
+
+        public Criteria andSessionIdGreaterThan(String value) {
+            addCriterion("session_id_ >", value, "sessionId");
+            return (Criteria) this;
+        }
+
+        public Criteria andSessionIdGreaterThanOrEqualTo(String value) {
+            addCriterion("session_id_ >=", value, "sessionId");
+            return (Criteria) this;
+        }
+
+        public Criteria andSessionIdLessThan(String value) {
+            addCriterion("session_id_ <", value, "sessionId");
+            return (Criteria) this;
+        }
+
+        public Criteria andSessionIdLessThanOrEqualTo(String value) {
+            addCriterion("session_id_ <=", value, "sessionId");
+            return (Criteria) this;
+        }
+
+        public Criteria andSessionIdLike(String value) {
+            addCriterion("session_id_ like", value, "sessionId");
+            return (Criteria) this;
+        }
+
+        public Criteria andSessionIdNotLike(String value) {
+            addCriterion("session_id_ not like", value, "sessionId");
+            return (Criteria) this;
+        }
+
+        public Criteria andSessionIdIn(List<String> values) {
+            addCriterion("session_id_ in", values, "sessionId");
+            return (Criteria) this;
+        }
+
+        public Criteria andSessionIdNotIn(List<String> values) {
+            addCriterion("session_id_ not in", values, "sessionId");
+            return (Criteria) this;
+        }
+
+        public Criteria andSessionIdBetween(String value1, String value2) {
+            addCriterion("session_id_ between", value1, value2, "sessionId");
+            return (Criteria) this;
+        }
+
+        public Criteria andSessionIdNotBetween(String value1, String value2) {
+            addCriterion("session_id_ not between", value1, value2, "sessionId");
+            return (Criteria) this;
+        }
+
+        public Criteria andRoomIdIsNull() {
+            addCriterion("room_id_ is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andRoomIdIsNotNull() {
+            addCriterion("room_id_ is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andRoomIdEqualTo(String value) {
+            addCriterion("room_id_ =", value, "roomId");
+            return (Criteria) this;
+        }
+
+        public Criteria andRoomIdNotEqualTo(String value) {
+            addCriterion("room_id_ <>", value, "roomId");
+            return (Criteria) this;
+        }
+
+        public Criteria andRoomIdGreaterThan(String value) {
+            addCriterion("room_id_ >", value, "roomId");
+            return (Criteria) this;
+        }
+
+        public Criteria andRoomIdGreaterThanOrEqualTo(String value) {
+            addCriterion("room_id_ >=", value, "roomId");
+            return (Criteria) this;
+        }
+
+        public Criteria andRoomIdLessThan(String value) {
+            addCriterion("room_id_ <", value, "roomId");
+            return (Criteria) this;
+        }
+
+        public Criteria andRoomIdLessThanOrEqualTo(String value) {
+            addCriterion("room_id_ <=", value, "roomId");
+            return (Criteria) this;
+        }
+
+        public Criteria andRoomIdLike(String value) {
+            addCriterion("room_id_ like", value, "roomId");
+            return (Criteria) this;
+        }
+
+        public Criteria andRoomIdNotLike(String value) {
+            addCriterion("room_id_ not like", value, "roomId");
+            return (Criteria) this;
+        }
+
+        public Criteria andRoomIdIn(List<String> values) {
+            addCriterion("room_id_ in", values, "roomId");
+            return (Criteria) this;
+        }
+
+        public Criteria andRoomIdNotIn(List<String> values) {
+            addCriterion("room_id_ not in", values, "roomId");
+            return (Criteria) this;
+        }
+
+        public Criteria andRoomIdBetween(String value1, String value2) {
+            addCriterion("room_id_ between", value1, value2, "roomId");
+            return (Criteria) this;
+        }
+
+        public Criteria andRoomIdNotBetween(String value1, String value2) {
+            addCriterion("room_id_ not between", value1, value2, "roomId");
+            return (Criteria) this;
+        }
+
+        public Criteria andUserIdIsNull() {
+            addCriterion("user_id_ is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andUserIdIsNotNull() {
+            addCriterion("user_id_ is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andUserIdEqualTo(Long value) {
+            addCriterion("user_id_ =", value, "userId");
+            return (Criteria) this;
+        }
+
+        public Criteria andUserIdNotEqualTo(Long value) {
+            addCriterion("user_id_ <>", value, "userId");
+            return (Criteria) this;
+        }
+
+        public Criteria andUserIdGreaterThan(Long value) {
+            addCriterion("user_id_ >", value, "userId");
+            return (Criteria) this;
+        }
+
+        public Criteria andUserIdGreaterThanOrEqualTo(Long value) {
+            addCriterion("user_id_ >=", value, "userId");
+            return (Criteria) this;
+        }
+
+        public Criteria andUserIdLessThan(Long value) {
+            addCriterion("user_id_ <", value, "userId");
+            return (Criteria) this;
+        }
+
+        public Criteria andUserIdLessThanOrEqualTo(Long value) {
+            addCriterion("user_id_ <=", value, "userId");
+            return (Criteria) this;
+        }
+
+        public Criteria andUserIdIn(List<Long> values) {
+            addCriterion("user_id_ in", values, "userId");
+            return (Criteria) this;
+        }
+
+        public Criteria andUserIdNotIn(List<Long> values) {
+            addCriterion("user_id_ not in", values, "userId");
+            return (Criteria) this;
+        }
+
+        public Criteria andUserIdBetween(Long value1, Long value2) {
+            addCriterion("user_id_ between", value1, value2, "userId");
+            return (Criteria) this;
+        }
+
+        public Criteria andUserIdNotBetween(Long value1, Long value2) {
+            addCriterion("user_id_ not between", value1, value2, "userId");
+            return (Criteria) this;
+        }
+
+        public Criteria andClientTypeIsNull() {
+            addCriterion("client_type_ is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andClientTypeIsNotNull() {
+            addCriterion("client_type_ is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andClientTypeEqualTo(String value) {
+            addCriterion("client_type_ =", value, "clientType");
+            return (Criteria) this;
+        }
+
+        public Criteria andClientTypeNotEqualTo(String value) {
+            addCriterion("client_type_ <>", value, "clientType");
+            return (Criteria) this;
+        }
+
+        public Criteria andClientTypeGreaterThan(String value) {
+            addCriterion("client_type_ >", value, "clientType");
+            return (Criteria) this;
+        }
+
+        public Criteria andClientTypeGreaterThanOrEqualTo(String value) {
+            addCriterion("client_type_ >=", value, "clientType");
+            return (Criteria) this;
+        }
+
+        public Criteria andClientTypeLessThan(String value) {
+            addCriterion("client_type_ <", value, "clientType");
+            return (Criteria) this;
+        }
+
+        public Criteria andClientTypeLessThanOrEqualTo(String value) {
+            addCriterion("client_type_ <=", value, "clientType");
+            return (Criteria) this;
+        }
+
+        public Criteria andClientTypeLike(String value) {
+            addCriterion("client_type_ like", value, "clientType");
+            return (Criteria) this;
+        }
+
+        public Criteria andClientTypeNotLike(String value) {
+            addCriterion("client_type_ not like", value, "clientType");
+            return (Criteria) this;
+        }
+
+        public Criteria andClientTypeIn(List<String> values) {
+            addCriterion("client_type_ in", values, "clientType");
+            return (Criteria) this;
+        }
+
+        public Criteria andClientTypeNotIn(List<String> values) {
+            addCriterion("client_type_ not in", values, "clientType");
+            return (Criteria) this;
+        }
+
+        public Criteria andClientTypeBetween(String value1, String value2) {
+            addCriterion("client_type_ between", value1, value2, "clientType");
+            return (Criteria) this;
+        }
+
+        public Criteria andClientTypeNotBetween(String value1, String value2) {
+            addCriterion("client_type_ not between", value1, value2, "clientType");
+            return (Criteria) this;
+        }
+
+        public Criteria andConnectTimeIsNull() {
+            addCriterion("connect_time_ is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andConnectTimeIsNotNull() {
+            addCriterion("connect_time_ is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andConnectTimeEqualTo(Date value) {
+            addCriterion("connect_time_ =", value, "connectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andConnectTimeNotEqualTo(Date value) {
+            addCriterion("connect_time_ <>", value, "connectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andConnectTimeGreaterThan(Date value) {
+            addCriterion("connect_time_ >", value, "connectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andConnectTimeGreaterThanOrEqualTo(Date value) {
+            addCriterion("connect_time_ >=", value, "connectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andConnectTimeLessThan(Date value) {
+            addCriterion("connect_time_ <", value, "connectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andConnectTimeLessThanOrEqualTo(Date value) {
+            addCriterion("connect_time_ <=", value, "connectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andConnectTimeIn(List<Date> values) {
+            addCriterion("connect_time_ in", values, "connectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andConnectTimeNotIn(List<Date> values) {
+            addCriterion("connect_time_ not in", values, "connectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andConnectTimeBetween(Date value1, Date value2) {
+            addCriterion("connect_time_ between", value1, value2, "connectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andConnectTimeNotBetween(Date value1, Date value2) {
+            addCriterion("connect_time_ not between", value1, value2, "connectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andDisconnectTimeIsNull() {
+            addCriterion("disconnect_time_ is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andDisconnectTimeIsNotNull() {
+            addCriterion("disconnect_time_ is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andDisconnectTimeEqualTo(Date value) {
+            addCriterion("disconnect_time_ =", value, "disconnectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andDisconnectTimeNotEqualTo(Date value) {
+            addCriterion("disconnect_time_ <>", value, "disconnectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andDisconnectTimeGreaterThan(Date value) {
+            addCriterion("disconnect_time_ >", value, "disconnectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andDisconnectTimeGreaterThanOrEqualTo(Date value) {
+            addCriterion("disconnect_time_ >=", value, "disconnectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andDisconnectTimeLessThan(Date value) {
+            addCriterion("disconnect_time_ <", value, "disconnectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andDisconnectTimeLessThanOrEqualTo(Date value) {
+            addCriterion("disconnect_time_ <=", value, "disconnectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andDisconnectTimeIn(List<Date> values) {
+            addCriterion("disconnect_time_ in", values, "disconnectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andDisconnectTimeNotIn(List<Date> values) {
+            addCriterion("disconnect_time_ not in", values, "disconnectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andDisconnectTimeBetween(Date value1, Date value2) {
+            addCriterion("disconnect_time_ between", value1, value2, "disconnectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andDisconnectTimeNotBetween(Date value1, Date value2) {
+            addCriterion("disconnect_time_ not between", value1, value2, "disconnectTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andOnlineTimeIsNull() {
+            addCriterion("online_time_ is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andOnlineTimeIsNotNull() {
+            addCriterion("online_time_ is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andOnlineTimeEqualTo(Long value) {
+            addCriterion("online_time_ =", value, "onlineTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andOnlineTimeNotEqualTo(Long value) {
+            addCriterion("online_time_ <>", value, "onlineTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andOnlineTimeGreaterThan(Long value) {
+            addCriterion("online_time_ >", value, "onlineTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andOnlineTimeGreaterThanOrEqualTo(Long value) {
+            addCriterion("online_time_ >=", value, "onlineTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andOnlineTimeLessThan(Long value) {
+            addCriterion("online_time_ <", value, "onlineTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andOnlineTimeLessThanOrEqualTo(Long value) {
+            addCriterion("online_time_ <=", value, "onlineTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andOnlineTimeIn(List<Long> values) {
+            addCriterion("online_time_ in", values, "onlineTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andOnlineTimeNotIn(List<Long> values) {
+            addCriterion("online_time_ not in", values, "onlineTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andOnlineTimeBetween(Long value1, Long value2) {
+            addCriterion("online_time_ between", value1, value2, "onlineTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andOnlineTimeNotBetween(Long value1, Long value2) {
+            addCriterion("online_time_ not between", value1, value2, "onlineTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedTimeIsNull() {
+            addCriterion("created_time_ is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedTimeIsNotNull() {
+            addCriterion("created_time_ is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedTimeEqualTo(Date value) {
+            addCriterion("created_time_ =", value, "createdTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedTimeNotEqualTo(Date value) {
+            addCriterion("created_time_ <>", value, "createdTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedTimeGreaterThan(Date value) {
+            addCriterion("created_time_ >", value, "createdTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedTimeGreaterThanOrEqualTo(Date value) {
+            addCriterion("created_time_ >=", value, "createdTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedTimeLessThan(Date value) {
+            addCriterion("created_time_ <", value, "createdTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedTimeLessThanOrEqualTo(Date value) {
+            addCriterion("created_time_ <=", value, "createdTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedTimeIn(List<Date> values) {
+            addCriterion("created_time_ in", values, "createdTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedTimeNotIn(List<Date> values) {
+            addCriterion("created_time_ not in", values, "createdTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedTimeBetween(Date value1, Date value2) {
+            addCriterion("created_time_ between", value1, value2, "createdTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedTimeNotBetween(Date value1, Date value2) {
+            addCriterion("created_time_ not between", value1, value2, "createdTime");
+            return (Criteria) this;
+        }
+    }
+
+    /**
+     * This class was generated by MyBatis Generator.
+     * This class corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated do_not_delete_during_merge Fri Nov 04 16:18:23 CST 2022
+     */
+    public static class Criteria extends GeneratedCriteria {
+
+        protected Criteria() {
+            super();
+        }
+    }
+
+    /**
+     * This class was generated by MyBatis Generator.
+     * This class corresponds to the database table ws_connect_record_info
+     *
+     * @mbg.generated Fri Nov 04 16:18:23 CST 2022
+     */
+    public static class Criterion {
+        private String condition;
+
+        private Object value;
+
+        private Object secondValue;
+
+        private boolean noValue;
+
+        private boolean singleValue;
+
+        private boolean betweenValue;
+
+        private boolean listValue;
+
+        private String typeHandler;
+
+        public String getCondition() {
+            return condition;
+        }
+
+        public Object getValue() {
+            return value;
+        }
+
+        public Object getSecondValue() {
+            return secondValue;
+        }
+
+        public boolean isNoValue() {
+            return noValue;
+        }
+
+        public boolean isSingleValue() {
+            return singleValue;
+        }
+
+        public boolean isBetweenValue() {
+            return betweenValue;
+        }
+
+        public boolean isListValue() {
+            return listValue;
+        }
+
+        public String getTypeHandler() {
+            return typeHandler;
+        }
+
+        protected Criterion(String condition) {
+            super();
+            this.condition = condition;
+            this.typeHandler = null;
+            this.noValue = true;
+        }
+
+        protected Criterion(String condition, Object value, String typeHandler) {
+            super();
+            this.condition = condition;
+            this.value = value;
+            this.typeHandler = typeHandler;
+            if (value instanceof List<?>) {
+                this.listValue = true;
+            } else {
+                this.singleValue = true;
+            }
+        }
+
+        protected Criterion(String condition, Object value) {
+            this(condition, value, null);
+        }
+
+        protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {
+            super();
+            this.condition = condition;
+            this.value = value;
+            this.secondValue = secondValue;
+            this.typeHandler = typeHandler;
+            this.betweenValue = true;
+        }
+
+        protected Criterion(String condition, Object value, Object secondValue) {
+            this(condition, value, secondValue, null);
+        }
+    }
+}

+ 26 - 0
mec-biz/src/main/java/com/ym/mec/biz/service/WsConnectService.java

@@ -0,0 +1,26 @@
+package com.ym.mec.biz.service;
+
+/**
+ * Created by Eric.Shang on 2022/10/31.
+ */
+public interface WsConnectService {
+
+    /**
+     * 保存用户ws连接记录信息
+     * @param sessionId 会话ID
+     * @param roomId 房间ID
+     * @param userid 用户ID
+     * @param clientType 客户端类型
+     * @return int
+     */
+    int saveWsConnectRecordInfo(String sessionId, String roomId, String userid, String clientType);
+
+    /**
+     * 更新用户ws连接断开时间
+     * @param roomId 房间ID
+     * @param userId 用户
+     * @param clientType 客户端类型
+     * @return int
+     */
+    int updateWsDisconnectRecordInfo(String sessionId, String roomId, String userId, String clientType);
+}

+ 86 - 0
mec-biz/src/main/java/com/ym/mec/biz/service/impl/WsConnectServiceImpl.java

@@ -0,0 +1,86 @@
+package com.ym.mec.biz.service.impl;
+
+import com.ym.mec.biz.dal.dao.websocket.WsConnectRecordInfoMapper;
+import com.ym.mec.biz.dal.entity.websocket.WsConnectRecordInfo;
+import com.ym.mec.biz.dal.entity.websocket.WsConnectRecordInfoExample;
+import com.ym.mec.biz.service.WsConnectService;
+import org.apache.commons.collections.CollectionUtils;
+import org.joda.time.DateTime;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Created by Eric.Shang on 2022/10/31.
+ */
+@Service
+public class WsConnectServiceImpl implements WsConnectService {
+
+    @Autowired
+    private WsConnectRecordInfoMapper wsConnectRecordInfoMapper;
+
+
+    /**
+     * 保存用户ws连接记录信息
+     *
+     * @param sessionId  会放ID
+     * @param roomId     房间ID
+     * @param userid     用户ID
+     * @param clientType 客户端类型
+     * @return int
+     */
+    @Override
+    public int saveWsConnectRecordInfo(String sessionId, String roomId, String userid, String clientType) {
+
+        Date date = DateTime.now().toDate();
+        // 连接记录信息
+        WsConnectRecordInfo record = new WsConnectRecordInfo();
+        record.setSessionId(sessionId);
+        record.setRoomId(roomId);
+        record.setUserId(Long.parseLong(userid));
+        record.setClientType(clientType);
+        record.setConnectTime(date);
+        record.setCreatedTime(date);
+
+        // 保存连接记录信息
+        return wsConnectRecordInfoMapper.insertSelective(record);
+    }
+
+    /**
+     * 更新用户ws连接断开时间
+     * @param sessionId  会话ID
+     * @param roomId     房间ID
+     * @param userId     用户
+     * @param clientType 客户端类型
+     * @return int
+     */
+    @Override
+    public int updateWsDisconnectRecordInfo(String sessionId, String roomId, String userId, String clientType) {
+
+        // 查询连接记录信息
+        WsConnectRecordInfoExample example = new WsConnectRecordInfoExample();
+        example.or()
+                .andSessionIdEqualTo(sessionId)
+                .andRoomIdEqualTo(roomId)
+                .andUserIdEqualTo(Long.parseLong(userId))
+                .andClientTypeEqualTo(clientType);
+
+        List<WsConnectRecordInfo> recordInfos = wsConnectRecordInfoMapper.selectByExample(example);
+        if (CollectionUtils.isEmpty(recordInfos)) {
+            return 0;
+        }
+
+        // 连接记录信息
+        WsConnectRecordInfo record = recordInfos.get(0);
+
+        // 更新数据
+        WsConnectRecordInfo recordInfo = new WsConnectRecordInfo();
+        recordInfo.setId(record.getId());
+        recordInfo.setDisconnectTime(DateTime.now().toDate());
+        recordInfo.setOnlineTime(recordInfo.getDisconnectTime().getTime() - record.getConnectTime().getTime());
+
+        return wsConnectRecordInfoMapper.updateByPrimaryKeySelective(recordInfo);
+    }
+}

+ 351 - 0
mec-biz/src/main/resources/config/mybatis/WsConnectRecordInfoMapper.xml

@@ -0,0 +1,351 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ym.mec.biz.dal.dao.websocket.WsConnectRecordInfoMapper">
+  <resultMap id="BaseResultMap" type="com.ym.mec.biz.dal.entity.websocket.WsConnectRecordInfo">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Fri Nov 04 16:18:23 CST 2022.
+    -->
+    <id column="id_" jdbcType="BIGINT" property="id" />
+    <result column="session_id_" jdbcType="VARCHAR" property="sessionId" />
+    <result column="room_id_" jdbcType="VARCHAR" property="roomId" />
+    <result column="user_id_" jdbcType="BIGINT" property="userId" />
+    <result column="client_type_" jdbcType="VARCHAR" property="clientType" />
+    <result column="connect_time_" jdbcType="TIMESTAMP" property="connectTime" />
+    <result column="disconnect_time_" jdbcType="TIMESTAMP" property="disconnectTime" />
+    <result column="online_time_" jdbcType="BIGINT" property="onlineTime" />
+    <result column="created_time_" jdbcType="TIMESTAMP" property="createdTime" />
+  </resultMap>
+  <sql id="Example_Where_Clause">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Fri Nov 04 16:18:23 CST 2022.
+    -->
+    <where>
+      <foreach collection="oredCriteria" item="criteria" separator="or">
+        <if test="criteria.valid">
+          <trim prefix="(" prefixOverrides="and" suffix=")">
+            <foreach collection="criteria.criteria" item="criterion">
+              <choose>
+                <when test="criterion.noValue">
+                  and ${criterion.condition}
+                </when>
+                <when test="criterion.singleValue">
+                  and ${criterion.condition} #{criterion.value}
+                </when>
+                <when test="criterion.betweenValue">
+                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
+                </when>
+                <when test="criterion.listValue">
+                  and ${criterion.condition}
+                  <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
+                    #{listItem}
+                  </foreach>
+                </when>
+              </choose>
+            </foreach>
+          </trim>
+        </if>
+      </foreach>
+    </where>
+  </sql>
+  <sql id="Update_By_Example_Where_Clause">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Fri Nov 04 16:18:23 CST 2022.
+    -->
+    <where>
+      <foreach collection="example.oredCriteria" item="criteria" separator="or">
+        <if test="criteria.valid">
+          <trim prefix="(" prefixOverrides="and" suffix=")">
+            <foreach collection="criteria.criteria" item="criterion">
+              <choose>
+                <when test="criterion.noValue">
+                  and ${criterion.condition}
+                </when>
+                <when test="criterion.singleValue">
+                  and ${criterion.condition} #{criterion.value}
+                </when>
+                <when test="criterion.betweenValue">
+                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
+                </when>
+                <when test="criterion.listValue">
+                  and ${criterion.condition}
+                  <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
+                    #{listItem}
+                  </foreach>
+                </when>
+              </choose>
+            </foreach>
+          </trim>
+        </if>
+      </foreach>
+    </where>
+  </sql>
+  <sql id="Base_Column_List">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Fri Nov 04 16:18:23 CST 2022.
+    -->
+    id_, session_id_, room_id_, user_id_, client_type_, connect_time_, disconnect_time_, 
+    online_time_, created_time_
+  </sql>
+  <select id="selectByExample" parameterType="com.ym.mec.biz.dal.entity.websocket.WsConnectRecordInfoExample" resultMap="BaseResultMap">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Fri Nov 04 16:18:23 CST 2022.
+    -->
+    select
+    <if test="distinct">
+      distinct
+    </if>
+    <include refid="Base_Column_List" />
+    from ws_connect_record_info
+    <if test="_parameter != null">
+      <include refid="Example_Where_Clause" />
+    </if>
+    <if test="orderByClause != null">
+      order by ${orderByClause}
+    </if>
+  </select>
+  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Fri Nov 04 16:18:23 CST 2022.
+    -->
+    select 
+    <include refid="Base_Column_List" />
+    from ws_connect_record_info
+    where id_ = #{id,jdbcType=BIGINT}
+  </select>
+  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Fri Nov 04 16:18:23 CST 2022.
+    -->
+    delete from ws_connect_record_info
+    where id_ = #{id,jdbcType=BIGINT}
+  </delete>
+  <delete id="deleteByExample" parameterType="com.ym.mec.biz.dal.entity.websocket.WsConnectRecordInfoExample">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Fri Nov 04 16:18:23 CST 2022.
+    -->
+    delete from ws_connect_record_info
+    <if test="_parameter != null">
+      <include refid="Example_Where_Clause" />
+    </if>
+  </delete>
+  <insert id="insert" parameterType="com.ym.mec.biz.dal.entity.websocket.WsConnectRecordInfo">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Fri Nov 04 16:18:23 CST 2022.
+    -->
+    insert into ws_connect_record_info (id_, session_id_, room_id_, 
+      user_id_, client_type_, connect_time_, 
+      disconnect_time_, online_time_, created_time_
+      )
+    values (#{id,jdbcType=BIGINT}, #{sessionId,jdbcType=VARCHAR}, #{roomId,jdbcType=VARCHAR}, 
+      #{userId,jdbcType=BIGINT}, #{clientType,jdbcType=VARCHAR}, #{connectTime,jdbcType=TIMESTAMP}, 
+      #{disconnectTime,jdbcType=TIMESTAMP}, #{onlineTime,jdbcType=BIGINT}, #{createdTime,jdbcType=TIMESTAMP}
+      )
+  </insert>
+  <insert id="insertSelective" parameterType="com.ym.mec.biz.dal.entity.websocket.WsConnectRecordInfo">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Fri Nov 04 16:18:23 CST 2022.
+    -->
+    insert into ws_connect_record_info
+    <trim prefix="(" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        id_,
+      </if>
+      <if test="sessionId != null">
+        session_id_,
+      </if>
+      <if test="roomId != null">
+        room_id_,
+      </if>
+      <if test="userId != null">
+        user_id_,
+      </if>
+      <if test="clientType != null">
+        client_type_,
+      </if>
+      <if test="connectTime != null">
+        connect_time_,
+      </if>
+      <if test="disconnectTime != null">
+        disconnect_time_,
+      </if>
+      <if test="onlineTime != null">
+        online_time_,
+      </if>
+      <if test="createdTime != null">
+        created_time_,
+      </if>
+    </trim>
+    <trim prefix="values (" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        #{id,jdbcType=BIGINT},
+      </if>
+      <if test="sessionId != null">
+        #{sessionId,jdbcType=VARCHAR},
+      </if>
+      <if test="roomId != null">
+        #{roomId,jdbcType=VARCHAR},
+      </if>
+      <if test="userId != null">
+        #{userId,jdbcType=BIGINT},
+      </if>
+      <if test="clientType != null">
+        #{clientType,jdbcType=VARCHAR},
+      </if>
+      <if test="connectTime != null">
+        #{connectTime,jdbcType=TIMESTAMP},
+      </if>
+      <if test="disconnectTime != null">
+        #{disconnectTime,jdbcType=TIMESTAMP},
+      </if>
+      <if test="onlineTime != null">
+        #{onlineTime,jdbcType=BIGINT},
+      </if>
+      <if test="createdTime != null">
+        #{createdTime,jdbcType=TIMESTAMP},
+      </if>
+    </trim>
+  </insert>
+  <select id="countByExample" parameterType="com.ym.mec.biz.dal.entity.websocket.WsConnectRecordInfoExample" resultType="java.lang.Long">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Fri Nov 04 16:18:23 CST 2022.
+    -->
+    select count(*) from ws_connect_record_info
+    <if test="_parameter != null">
+      <include refid="Example_Where_Clause" />
+    </if>
+  </select>
+  <update id="updateByExampleSelective" parameterType="map">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Fri Nov 04 16:18:23 CST 2022.
+    -->
+    update ws_connect_record_info
+    <set>
+      <if test="record.id != null">
+        id_ = #{record.id,jdbcType=BIGINT},
+      </if>
+      <if test="record.sessionId != null">
+        session_id_ = #{record.sessionId,jdbcType=VARCHAR},
+      </if>
+      <if test="record.roomId != null">
+        room_id_ = #{record.roomId,jdbcType=VARCHAR},
+      </if>
+      <if test="record.userId != null">
+        user_id_ = #{record.userId,jdbcType=BIGINT},
+      </if>
+      <if test="record.clientType != null">
+        client_type_ = #{record.clientType,jdbcType=VARCHAR},
+      </if>
+      <if test="record.connectTime != null">
+        connect_time_ = #{record.connectTime,jdbcType=TIMESTAMP},
+      </if>
+      <if test="record.disconnectTime != null">
+        disconnect_time_ = #{record.disconnectTime,jdbcType=TIMESTAMP},
+      </if>
+      <if test="record.onlineTime != null">
+        online_time_ = #{record.onlineTime,jdbcType=BIGINT},
+      </if>
+      <if test="record.createdTime != null">
+        created_time_ = #{record.createdTime,jdbcType=TIMESTAMP},
+      </if>
+    </set>
+    <if test="_parameter != null">
+      <include refid="Update_By_Example_Where_Clause" />
+    </if>
+  </update>
+  <update id="updateByExample" parameterType="map">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Fri Nov 04 16:18:23 CST 2022.
+    -->
+    update ws_connect_record_info
+    set id_ = #{record.id,jdbcType=BIGINT},
+      session_id_ = #{record.sessionId,jdbcType=VARCHAR},
+      room_id_ = #{record.roomId,jdbcType=VARCHAR},
+      user_id_ = #{record.userId,jdbcType=BIGINT},
+      client_type_ = #{record.clientType,jdbcType=VARCHAR},
+      connect_time_ = #{record.connectTime,jdbcType=TIMESTAMP},
+      disconnect_time_ = #{record.disconnectTime,jdbcType=TIMESTAMP},
+      online_time_ = #{record.onlineTime,jdbcType=BIGINT},
+      created_time_ = #{record.createdTime,jdbcType=TIMESTAMP}
+    <if test="_parameter != null">
+      <include refid="Update_By_Example_Where_Clause" />
+    </if>
+  </update>
+  <update id="updateByPrimaryKeySelective" parameterType="com.ym.mec.biz.dal.entity.websocket.WsConnectRecordInfo">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Fri Nov 04 16:18:23 CST 2022.
+    -->
+    update ws_connect_record_info
+    <set>
+      <if test="sessionId != null">
+        session_id_ = #{sessionId,jdbcType=VARCHAR},
+      </if>
+      <if test="roomId != null">
+        room_id_ = #{roomId,jdbcType=VARCHAR},
+      </if>
+      <if test="userId != null">
+        user_id_ = #{userId,jdbcType=BIGINT},
+      </if>
+      <if test="clientType != null">
+        client_type_ = #{clientType,jdbcType=VARCHAR},
+      </if>
+      <if test="connectTime != null">
+        connect_time_ = #{connectTime,jdbcType=TIMESTAMP},
+      </if>
+      <if test="disconnectTime != null">
+        disconnect_time_ = #{disconnectTime,jdbcType=TIMESTAMP},
+      </if>
+      <if test="onlineTime != null">
+        online_time_ = #{onlineTime,jdbcType=BIGINT},
+      </if>
+      <if test="createdTime != null">
+        created_time_ = #{createdTime,jdbcType=TIMESTAMP},
+      </if>
+    </set>
+    where id_ = #{id,jdbcType=BIGINT}
+  </update>
+  <update id="updateByPrimaryKey" parameterType="com.ym.mec.biz.dal.entity.websocket.WsConnectRecordInfo">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Fri Nov 04 16:18:23 CST 2022.
+    -->
+    update ws_connect_record_info
+    set session_id_ = #{sessionId,jdbcType=VARCHAR},
+      room_id_ = #{roomId,jdbcType=VARCHAR},
+      user_id_ = #{userId,jdbcType=BIGINT},
+      client_type_ = #{clientType,jdbcType=VARCHAR},
+      connect_time_ = #{connectTime,jdbcType=TIMESTAMP},
+      disconnect_time_ = #{disconnectTime,jdbcType=TIMESTAMP},
+      online_time_ = #{onlineTime,jdbcType=BIGINT},
+      created_time_ = #{createdTime,jdbcType=TIMESTAMP}
+    where id_ = #{id,jdbcType=BIGINT}
+  </update>
+</mapper>

+ 6 - 1
mec-websocket/pom.xml

@@ -66,7 +66,12 @@
 			<artifactId>easy-captcha</artifactId>
 			<version>1.6.2</version>
 		</dependency>
-
+    <!--	netty-socketio	-->
+		<dependency>
+			<groupId>com.corundumstudio.socketio</groupId>
+			<artifactId>netty-socketio</artifactId>
+			<version>1.7.19</version>
+		</dependency>
 	</dependencies>
 	<build>
 		<plugins>

+ 0 - 17
mec-websocket/src/main/java/com/ym/mec/web/WebSocketApplication.java

@@ -29,21 +29,4 @@ public class WebSocketApplication {
 		SpringApplication.run(WebSocketApplication.class, args);
 	}
 
-	/**
-	 * 注册filter
-	 * @return
-	 */
-	@Bean
-	public FilterRegistrationBean<Filter> filterRegistrationBean() {
-		FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<Filter>();
-		// 注入过滤器
-		registration.setFilter(new EmojiEncodingFilter());
-		// 过滤器名称
-		registration.setName("emojiEncodingFilter");
-		// 拦截规则
-		registration.addUrlPatterns("/*");
-		// 过滤器顺序(值越小,优先级越高)
-		registration.setOrder(1);
-		return registration;
-	}
 }

+ 0 - 52
mec-websocket/src/main/java/com/ym/mec/web/config/PermissionCheckService.java

@@ -1,52 +0,0 @@
-package com.ym.mec.web.config;
-
-import java.util.Collection;
-
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Lazy;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.stereotype.Component;
-
-import com.ym.mec.auth.api.client.SysUserFeignService;
-import com.ym.mec.auth.api.entity.SysUser;
-import com.ym.mec.common.security.SecurityUtils;
-
-@Component("pcs")
-public class PermissionCheckService {
-	
-	@Autowired
-	@Lazy
-	private SysUserFeignService sysUserFeignService;
-
-	public boolean hasPermissions(String... permissions) {
-		Authentication authentication = SecurityUtils.getAuthentication();
-		if (authentication == null) {
-			return false;
-		}
-
-		SysUser user = sysUserFeignService.queryUserInfo();
-		if(user.getIsSuperAdmin()){
-			return true;
-		}
-
-		Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
-
-		for (String perm : permissions) {
-			for (GrantedAuthority authority : authorities) {
-				if (StringUtils.equalsIgnoreCase(perm, authority.getAuthority())) {
-					return true;
-				}
-			}
-		}
-
-		return false;
-	}
-
-	public boolean hasRoles(String... roles) {
-
-		return hasPermissions(roles);
-	}
-
-}

+ 4 - 4
mec-websocket/src/main/java/com/ym/mec/web/config/ResourceServerConfig.java

@@ -1,5 +1,7 @@
 package com.ym.mec.web.config;
 
+import com.ym.mec.common.security.BaseAccessDeniedHandler;
+import com.ym.mec.common.security.BaseAuthenticationEntryPoint;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@@ -8,9 +10,6 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.E
 import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
 import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
 
-import com.ym.mec.common.security.BaseAccessDeniedHandler;
-import com.ym.mec.common.security.BaseAuthenticationEntryPoint;
-
 @Configuration
 @EnableResourceServer
 @EnableGlobalMethodSecurity(prePostEnabled = true)
@@ -33,7 +32,8 @@ public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
                 .authorizeRequests()
                 .antMatchers("/task/**")
                 .hasIpAddress("0.0.0.0/0")
-                .antMatchers("/v2/api-docs").permitAll().anyRequest().authenticated().and().httpBasic();
+                .antMatchers("/v2/api-docs", "/ws-server").permitAll().anyRequest()
+                .authenticated().and().httpBasic();
     }
 
     @Override

+ 73 - 0
mec-websocket/src/main/java/com/ym/mec/web/config/SocketIoConfig.java

@@ -0,0 +1,73 @@
+package com.ym.mec.web.config;
+
+import com.corundumstudio.socketio.SocketConfig;
+import com.corundumstudio.socketio.SocketIOServer;
+import com.corundumstudio.socketio.Transport;
+import com.corundumstudio.socketio.store.RedissonStoreFactory;
+import com.corundumstudio.socketio.store.pubsub.PubSubStore;
+import lombok.extern.slf4j.Slf4j;
+import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Slf4j
+@Configuration
+public class SocketIoConfig {
+    @Value("${socket.server.host:127.0.0.1}")
+    private String host;
+
+    @Value("${socket.server.port:3002}")
+    private Integer port;
+
+    private final RedissonClient redissonClient;
+
+    @Autowired
+    public SocketIoConfig(RedissonClient redissonClient) {
+        this.redissonClient = redissonClient;
+    }
+
+    @Bean
+    public SocketIOServer socketIOServer() {
+        com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();
+        config.setHostname(host);
+        config.setPort(port);
+        config.setPingInterval(25000);
+        config.setPingTimeout(60000);
+        config.setWorkerThreads(100);
+        config.setRandomSession(true);
+        //设置最大每帧处理数据的长度,防止他人利用大数据来攻击服务器
+        config.setMaxFramePayloadLength(1024 * 1024);
+        //设置http交互最大内容长度
+        config.setMaxHttpContentLength(1024 * 1024);
+        //授权
+        config.setAuthorizationListener(data -> true);
+        config.setTransports(Transport.WEBSOCKET, Transport.POLLING);
+        // 推荐使用redisson
+        config.setStoreFactory(new RedissonStoreFactory(redissonClient));
+        // 配置一个统一的URL,nginx做后端请求转发; ****注意"/"不能少****
+        config.setContext("/ws-server");
+        log.info("--------SocketIO------- context={}", config.getContext());
+
+        // 服务端配置
+        SocketConfig socketConfig = new SocketConfig();
+        socketConfig.setReuseAddress(true);
+        socketConfig.setTcpNoDelay(true);
+        socketConfig.setSoLinger(0);
+        config.setSocketConfig(socketConfig);
+
+        return new SocketIOServer(config);
+    }
+
+    /**
+     * 解决分布式服务器实例环境
+     * @param socketServer SocketIOServer
+     * @return PubSubStore
+     */
+    @Bean
+    public PubSubStore pubSubStore(SocketIOServer socketServer) {
+
+        return socketServer.getConfiguration().getStoreFactory().pubSubStore();
+    }
+}

+ 40 - 7
mec-websocket/src/main/java/com/ym/mec/web/config/WebMvcConfig.java

@@ -1,20 +1,23 @@
 package com.ym.mec.web.config;
 
-import java.util.ArrayList;
-import java.util.List;
-
+import com.ym.mec.common.config.EnumConverterFactory;
+import com.ym.mec.common.config.LocalFastJsonHttpMessageConverter;
+import com.ym.mec.common.filters.EmojiEncodingFilter;
+import com.ym.mec.web.interceptor.OperationLogInterceptor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.format.FormatterRegistry;
 import org.springframework.http.MediaType;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
-import com.ym.mec.common.config.EnumConverterFactory;
-import com.ym.mec.common.config.LocalFastJsonHttpMessageConverter;
-import com.ym.mec.web.interceptor.OperationLogInterceptor;
+import javax.servlet.Filter;
+import java.util.ArrayList;
+import java.util.List;
 
 @Configuration
 public class WebMvcConfig implements WebMvcConfigurer {
@@ -36,13 +39,43 @@ public class WebMvcConfig implements WebMvcConfigurer {
 		registry.addInterceptor(operationLogInterceptor).addPathPatterns("/**").excludePathPatterns("/login");
 	}
 
+	@Override
+	public void addCorsMappings(CorsRegistry registry) {
+		registry.addMapping("/**").allowedOrigins("*")
+				// 是否允许证书
+				.allowCredentials(true)
+				// 设置允许的方法
+				.allowedMethods("*")
+				// 设置允许的header属性
+				.allowedHeaders("*")
+				// 跨域允许时间
+				.maxAge(3600);
+	}
+
 	@Bean
 	public HttpMessageConverters fastJsonHttpMessageConverters() {
 		LocalFastJsonHttpMessageConverter converter = new LocalFastJsonHttpMessageConverter();
-		List<MediaType> fastMediaTypes = new ArrayList<MediaType>();
+		List<MediaType> fastMediaTypes = new ArrayList<>();
 		fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
 		converter.setSupportedMediaTypes(fastMediaTypes);
 		return new HttpMessageConverters(converter);
 	}
 
+	/**
+	 * 注册filter
+	 * @return FilterRegistrationBean<Filter>
+	 */
+	@Bean
+	public FilterRegistrationBean<Filter> filterRegistrationBean() {
+		FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<Filter>();
+		// 注入过滤器
+		registration.setFilter(new EmojiEncodingFilter());
+		// 过滤器名称
+		registration.setName("emojiEncodingFilter");
+		// 拦截规则
+		registration.addUrlPatterns("/*");
+		// 过滤器顺序(值越小,优先级越高)
+		registration.setOrder(1);
+		return registration;
+	}
 }

+ 3 - 10
mec-websocket/src/main/java/com/ym/mec/web/controller/WebsocketController.java

@@ -1,23 +1,16 @@
 package com.ym.mec.web.controller;
 
-import com.alibaba.fastjson.JSON;
-import com.ym.mec.biz.dal.dto.MusicPitchDetailDto;
 import com.ym.mec.biz.handler.WebSocketHandler;
-import com.ym.mec.biz.service.SoundService;
 import com.ym.mec.common.controller.BaseController;
 import com.ym.mec.common.entity.HttpResponseResult;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.socket.TextMessage;
 
 import java.io.IOException;
-import java.util.List;
 
 /**
  * @Author Joburgess

+ 46 - 0
mec-websocket/src/main/java/com/ym/mec/web/handler/Chat2.java

@@ -0,0 +1,46 @@
+package com.ym.mec.web.handler;
+
+import com.corundumstudio.socketio.AckRequest;
+import com.corundumstudio.socketio.SocketIOClient;
+import com.corundumstudio.socketio.SocketIONamespace;
+import com.corundumstudio.socketio.annotation.OnConnect;
+import com.corundumstudio.socketio.annotation.OnDisconnect;
+import com.corundumstudio.socketio.annotation.OnEvent;
+import com.ym.mec.web.support.anno.NamespaceReference;
+import com.ym.mec.web.support.anno.OnNamespace;
+import com.ym.mec.web.support.mes.ChatObject;
+import org.springframework.stereotype.Component;
+
+
+@Component
+@OnNamespace("/chat2")
+public class Chat2 {
+    @NamespaceReference
+    private SocketIONamespace namespace;
+
+    /**
+     * 添加connect事件,当客户端发起连接时调用,本文中将clientid与sessionid存入数据库
+     * 方便后面发送消息时查找到对应的目标client
+     * //
+     */
+    @OnConnect
+    public void onConnect(SocketIOClient client) {
+
+    }
+
+    /**
+     * 添加@OnDisconnect事件,客户端断开连接时调用,刷新客户端信息
+     */
+    @OnDisconnect
+    public void onDisconnect(SocketIOClient client) {
+    }
+
+    @OnEvent(value = "message")
+    public void onEvent(SocketIOClient client, ChatObject data, AckRequest ackRequest) {
+        System.out.println("chat2 namespace " + namespace.getName());
+        namespace.getBroadcastOperations().sendEvent("message", data);
+
+        System.out.println("chat2 sessionId " + client.getSessionId());
+    }
+
+}

+ 274 - 0
mec-websocket/src/main/java/com/ym/mec/web/handler/WhiteboardHandler.java

@@ -0,0 +1,274 @@
+package com.ym.mec.web.handler;
+
+import com.corundumstudio.socketio.BroadcastOperations;
+import com.corundumstudio.socketio.SocketIOClient;
+import com.corundumstudio.socketio.SocketIONamespace;
+import com.corundumstudio.socketio.annotation.OnConnect;
+import com.corundumstudio.socketio.annotation.OnDisconnect;
+import com.corundumstudio.socketio.annotation.OnEvent;
+import com.corundumstudio.socketio.protocol.Packet;
+import com.corundumstudio.socketio.protocol.PacketType;
+import com.corundumstudio.socketio.store.pubsub.PubSubStore;
+import com.google.common.collect.Lists;
+import com.ym.mec.biz.service.WsConnectService;
+import com.ym.mec.web.support.anno.NamespaceReference;
+import com.ym.mec.web.support.anno.OnNamespace;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.joda.time.DateTime;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ *    var socket = io.connect('http://ip:prot/namespeace')
+ */
+@Slf4j
+@Data
+@Component
+@OnNamespace("/whiteboard")
+public class WhiteboardHandler {
+
+    // 初始化房间
+    private static final String EVENT_INIT_ROOM = "init-room";
+    // 心跳配置
+    private static final String EVENT_HEARTBEAT_CONFIG = "heartbeat-config";
+    // 心跳事件
+    private static final String EVENT_HEARTBEAT_SYNC = "heartbeat-sync";
+    // 第一次进入房间
+    private static final String EVENT_FIRST_IN_ROOM = "first-in-room";
+    // 加入房间
+    private static final String EVENT_JOIN_ROOM = "join-room";
+    // 新用户加入
+    private static final String EVENT_NEW_USER = "new-user";
+    // 房间用户改变(加入或退出)
+    private static final String EVENT_ROOM_USER_CHANGE = "room-user-change";
+    // 服务端广播事件
+    private static final String EVENT_SERVER_BROADCAST = "server-broadcast";
+    // 服务端异常广播事件
+    private static final String EVENT_SERVER_VOLATILE_BROADCAST = "server-volatile-broadcast";
+    // 客户端广播
+    private static final String EVENT_CLIENT_BROADCAST = "client-broadcast";
+
+    // 房间编号
+    private static final String ROOM_ID = "roomId";
+    private static final String USER_ID = "userId";
+    private static final String CLIENT_TYPE = "clientType";
+
+    @NamespaceReference
+    private SocketIONamespace namespace;
+    @Autowired
+    private PubSubStore pubSubStore;
+    @Autowired
+    private WsConnectService wsConnectService;
+
+    /**
+     * 分布式服务器,消息分发
+     *
+     * @param client    SocketIOClient
+     * @param roomId    房间ID
+     * @param eventName 事件名称
+     * @param data      发送数据
+     */
+    public void dispatchMessage(SocketIOClient client, String roomId, String eventName, List<Object> data) {
+
+        // 分发消息(当前服务不会向client推送自己分发出去的消息)
+        try {
+
+            // 命名空间
+            String namespace = client.getNamespace().getName();
+
+            // 发送数据包
+            Packet packet = new Packet(PacketType.MESSAGE);
+            packet.setSubType(PacketType.EVENT);
+            packet.setName(eventName);
+            packet.setNsp(namespace);
+            packet.setData(data);
+
+            //pubSubStore.publish(PubSubType.DISPATCH, new DispatchMessage(roomId, packet, namespace));
+        } catch (Exception e) {
+            log.error("PubSubType.DISPATCH roomId={}, sid={}", roomId, client.getSessionId(), e);
+        }
+    }
+
+    /**
+     * 发送初始化房间事件
+     * @param client SocketIOClient
+     */
+    @OnConnect
+    public void onConnect(SocketIOClient client) {
+        // ws连接请求,需要包含roomId, userId, clientType参数
+        // 房间ID
+        String roomId = client.getHandshakeData().getSingleUrlParam(ROOM_ID);
+        // 用户ID
+        String userId = client.getHandshakeData().getSingleUrlParam(USER_ID);
+        // 客户端类型
+        String clientType = client.getHandshakeData().getSingleUrlParam(CLIENT_TYPE);
+
+        log.debug("onConnect client={}, ns={}, roomId={}, userId={}", client.getSessionId(),
+                client.getNamespace().getName(), roomId, userId);
+        //发送初始化房间事件
+        client.sendEvent(EVENT_INIT_ROOM);
+
+        // 发送心跳同步事件
+        WhiteboardMessage.HeartbeatConfig config = WhiteboardMessage.HeartbeatConfig
+                .builder()
+                .event(EVENT_HEARTBEAT_SYNC)
+                .timestamp(DateTime.now().getMillis())
+                .interval(15)
+                .build();
+
+        client.sendEvent(EVENT_HEARTBEAT_CONFIG, config);
+
+        if (StringUtils.isNoneBlank(roomId, userId, clientType)) {
+
+            wsConnectService.saveWsConnectRecordInfo(client.getSessionId().toString(), roomId,
+                    userId, clientType);
+        }
+    }
+
+    /**
+     * 添加@OnDisconnect事件,客户端断开连接时调用
+     * @param client SocketIOClient
+     */
+    @OnDisconnect
+    public void onDisconnect(SocketIOClient client) {
+
+        // 房间ID
+        String roomId = client.getHandshakeData().getSingleUrlParam(ROOM_ID);
+        // 用户ID
+        String userId = client.getHandshakeData().getSingleUrlParam(USER_ID);
+        // 客户端类型
+        String clientType = client.getHandshakeData().getSingleUrlParam(CLIENT_TYPE);
+
+        log.debug("onDisconnect client={}, ns={}, roomId={}, userId={}", client.getSessionId(),
+                client.getNamespace().getName(), roomId, userId);
+        client.disconnect();
+
+        // 通知用户参与所有房间,用户变化信息
+        if (StringUtils.isNotEmpty(roomId)) {
+
+            BroadcastOperations roomOperations = namespace.getRoomOperations(roomId);
+
+            List<String> collect = roomOperations.getClients().stream()
+                    .map(x -> x.getSessionId().toString()).distinct().collect(Collectors.toList());
+
+            if (!CollectionUtils.isEmpty(collect)) {
+                roomOperations.sendEvent(EVENT_ROOM_USER_CHANGE, collect);
+            }
+
+            // 消息分发
+            dispatchMessage(client, roomId, EVENT_ROOM_USER_CHANGE, Collections.singletonList(collect));
+        }
+
+        if (StringUtils.isNoneBlank(roomId, userId, clientType)) {
+
+            wsConnectService.updateWsDisconnectRecordInfo(client.getSessionId().toString(), roomId, userId, clientType);
+        }
+    }
+
+    /**
+     * 画板心跳同步监听
+     * @param client SocketIOClient
+     * @param timestamp 时间戳
+     */
+    @OnEvent(value = EVENT_HEARTBEAT_SYNC)
+    public void onHeartbeatSync(SocketIOClient client, Long timestamp) {
+
+        // 房间ID
+        String roomId = client.getHandshakeData().getSingleUrlParam(ROOM_ID);
+        // 用户ID
+        String userId = client.getHandshakeData().getSingleUrlParam(USER_ID);
+        // 客户端类型
+        String clientType = client.getHandshakeData().getSingleUrlParam(CLIENT_TYPE);
+
+        /*log.debug("onHeartbeatSync client={}, ns={}, roomId={}, userId={}, timestamp={}", client.getSessionId(),
+                client.getNamespace().getName(), roomId, userId, timestamp);*/
+
+        if (StringUtils.isNoneBlank(roomId, userId, clientType)) {
+
+            wsConnectService.updateWsDisconnectRecordInfo(client.getSessionId().toString(), roomId, userId, clientType);
+        }
+    }
+
+    /**
+     * 加入房间事件
+     * @param client SocketIOClient
+     * @param roomId 房间ID
+     */
+    @OnEvent(value = EVENT_JOIN_ROOM)
+    public void joinRoom(SocketIOClient client, String roomId) {
+        log.debug("joinRoom roomId={}", roomId);
+
+        // 加入房间
+        client.joinRoom(roomId);
+
+        BroadcastOperations roomOperations = namespace.getRoomOperations(roomId);
+        Collection<SocketIOClient> clients = roomOperations.getClients();
+
+        log.debug("joinRoom clients={}", clients.size());
+        if (clients.size() > 1) {
+            roomOperations.sendEvent(EVENT_NEW_USER, client.getSessionId().toString());
+        } else {
+            //发送
+            client.sendEvent(EVENT_FIRST_IN_ROOM);
+        }
+
+        List<String> collect = Optional.of(clients).orElse(Lists.newArrayList()).stream()
+                .map(x -> x.getSessionId().toString()).distinct().collect(Collectors.toList());
+        //发送
+        if (!CollectionUtils.isEmpty(collect)) {
+
+            roomOperations.sendEvent(EVENT_ROOM_USER_CHANGE, collect);
+        }
+
+        // 消息分发
+        dispatchMessage(client, roomId, EVENT_JOIN_ROOM, Collections.singletonList(roomId));
+    }
+
+    /**
+     * 转发 server-broadcast =>client-broadcast
+     * @param client SocketIOClient
+     * @param roomId 房间ID
+     * @param encryptedData 接收透传数据
+     * @param iv 接收透传数据
+     */
+    @OnEvent(value = EVENT_SERVER_BROADCAST)
+    public void serverBroadcast(SocketIOClient client, String roomId, byte[] encryptedData, byte[] iv) {
+        //log.info("serverBroadcast roomId={}", roomId);
+
+        BroadcastOperations roomOperations = namespace.getRoomOperations(roomId);
+        // 发送房间广播消息
+        roomOperations.sendEvent(EVENT_CLIENT_BROADCAST, encryptedData, iv);
+
+        // 消息分发
+        dispatchMessage(client, roomId, EVENT_CLIENT_BROADCAST, Arrays.asList(encryptedData, iv));
+    }
+
+    /**
+     * 转发 server-volatile-broadcast =>client-broadcast
+     * @param client SocketIOClient
+     * @param roomId 房间ID
+     * @param encryptedData 接收透传数据
+     * @param iv 接收透传数据
+     */
+    @OnEvent(value = EVENT_SERVER_VOLATILE_BROADCAST)
+    public void serverVolatileBroadcast(SocketIOClient client, String roomId, byte[] encryptedData, byte[] iv) {
+        //log.info("serverVolatileBroadcast roomId={}", roomId);
+
+        BroadcastOperations roomOperations = namespace.getRoomOperations(roomId);
+        // 发送房间广播消息
+        roomOperations.sendEvent(EVENT_CLIENT_BROADCAST, encryptedData, iv);
+
+        // 消息分发
+        dispatchMessage(client, roomId, EVENT_CLIENT_BROADCAST, Arrays.asList(encryptedData, iv));
+    }
+}

+ 33 - 0
mec-websocket/src/main/java/com/ym/mec/web/handler/WhiteboardMessage.java

@@ -0,0 +1,33 @@
+package com.ym.mec.web.handler;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 画板消息
+ * Created by Eric.Shang on 2022/11/4.
+ */
+public class WhiteboardMessage {
+
+    /**
+     * 心跳同步配置
+     */
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class HeartbeatConfig implements Serializable {
+
+        // 事件名称
+        private String event;
+        // 当前时间戳
+        private Long timestamp;
+        // 心跳同步间隔(默认60s)
+        private Integer interval = 60;
+
+    }
+}

+ 11 - 0
mec-websocket/src/main/java/com/ym/mec/web/support/anno/NamespaceReference.java

@@ -0,0 +1,11 @@
+package com.ym.mec.web.support.anno;
+
+import java.lang.annotation.*;
+
+@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface NamespaceReference {
+
+
+}

+ 12 - 0
mec-websocket/src/main/java/com/ym/mec/web/support/anno/OnNamespace.java

@@ -0,0 +1,12 @@
+package com.ym.mec.web.support.anno;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface OnNamespace {
+    String value() default "";
+}

+ 31 - 0
mec-websocket/src/main/java/com/ym/mec/web/support/mes/ChatObject.java

@@ -0,0 +1,31 @@
+package com.ym.mec.web.support.mes;
+
+public class ChatObject {
+
+    private String userName;
+    private String message;
+
+    public ChatObject() {
+    }
+
+    public ChatObject(String userName, String message) {
+        super();
+        this.userName = userName;
+        this.message = message;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+}

+ 46 - 0
mec-websocket/src/main/java/com/ym/mec/web/support/socket/NamespaceFactoryBean.java

@@ -0,0 +1,46 @@
+package com.ym.mec.web.support.socket;
+
+import com.corundumstudio.socketio.SocketIONamespace;
+import com.corundumstudio.socketio.SocketIOServer;
+import com.ym.mec.web.support.anno.OnNamespace;
+import jodd.util.StringUtil;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.stereotype.Component;
+
+@Component
+public class NamespaceFactoryBean implements FactoryBean<SocketIONamespace> {
+
+    private Class<?> originalBeanClass;
+    private final SocketIOServer server;
+
+    @Autowired
+    public NamespaceFactoryBean(SocketIOServer server) {
+        this.server = server;
+    }
+
+    @Override
+    public SocketIONamespace getObject() {
+
+        OnNamespace onNamespace = AnnotationUtils.findAnnotation(originalBeanClass, OnNamespace.class);
+        if (onNamespace == null || StringUtil.isBlank(onNamespace.value())) {
+            return server.getNamespace("");
+        }
+
+        String namespace = onNamespace.value();
+        if (!namespace.startsWith("/")) {
+            namespace = "/" + namespace;
+        }
+        return server.addNamespace(namespace);
+    }
+
+    @Override
+    public Class<?> getObjectType() {
+        return SocketIONamespace.class;
+    }
+
+    public void setOriginalBeanClass(Class<?> originalBeanClass) {
+        this.originalBeanClass = originalBeanClass;
+    }
+}

+ 46 - 0
mec-websocket/src/main/java/com/ym/mec/web/support/socket/ServerRunner.java

@@ -0,0 +1,46 @@
+package com.ym.mec.web.support.socket;
+
+import com.corundumstudio.socketio.SocketIOServer;
+import com.corundumstudio.socketio.namespace.NamespacesHub;
+import com.corundumstudio.socketio.store.pubsub.PubSubStore;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+public class ServerRunner implements CommandLineRunner {
+
+    private final SocketIOServer server;
+    private final PubSubStore pubSubStore;
+    private final NamespacesHub namespacesHub;
+
+    @Autowired
+    public ServerRunner(SocketIOServer server, PubSubStore pubSubStore) {
+        this.server = server;
+        this.pubSubStore = pubSubStore;
+        this.namespacesHub = new NamespacesHub(server.getConfiguration());
+    }
+
+    @Override
+    public void run(String... args) {
+
+        // 启动服务
+        server.start();
+
+        // 订阅消息
+        /*pubSubStore.subscribe(PubSubType.DISPATCH, message -> {
+
+            // 分布式服务空间请求分发
+            Namespace namespace = namespacesHub.get(message.getNamespace());
+            if (Objects.nonNull(namespace)) {
+                log.info("PubSubType.DISPATCH room={}, package={}", message.getRoom(), message.getPacket());
+                namespace.dispatch(message.getRoom(), message.getPacket());
+            }
+
+        }, DispatchMessage.class);*/
+
+        log.info("--------SocketIO------- SERVER.START PORT={}", server.getConfiguration().getPort());
+    }
+}

+ 89 - 0
mec-websocket/src/main/java/com/ym/mec/web/support/socket/SocketEventScanner.java

@@ -0,0 +1,89 @@
+package com.ym.mec.web.support.socket;
+
+import com.corundumstudio.socketio.SocketIONamespace;
+import com.corundumstudio.socketio.annotation.OnConnect;
+import com.corundumstudio.socketio.annotation.OnDisconnect;
+import com.corundumstudio.socketio.annotation.OnEvent;
+import com.ym.mec.web.support.anno.NamespaceReference;
+import com.ym.mec.web.support.anno.OnNamespace;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ReflectionUtils;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+@Component
+public class SocketEventScanner implements BeanPostProcessor {
+
+    @Autowired
+    private NamespaceFactoryBean factoryBean;
+    private static final Logger LOGGER = LoggerFactory.getLogger(SocketEventScanner.class);
+
+    private final List<Class<? extends Annotation>> annotations =
+            Arrays.asList(OnConnect.class, OnDisconnect.class, OnEvent.class);
+
+    private Class<?> originalBeanClass;
+
+    @Override
+    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+        OnNamespace onNamespace = bean.getClass().getAnnotation(OnNamespace.class);
+        if (originalBeanClass != null && onNamespace != null) {
+            //前端调用方式 var socket =  io.connect('http://localhost:9092/chat1');
+            factoryBean.setOriginalBeanClass(originalBeanClass);
+            SocketIONamespace socketIONamespace = factoryBean.getObject();
+            socketIONamespace.addListeners(bean, originalBeanClass);
+            LOGGER.info("{} bean listeners added", beanName);
+            //注入属性
+            Field[] declaredFields = originalBeanClass.getDeclaredFields();
+            setNamespace(bean, socketIONamespace, declaredFields);
+            originalBeanClass = null;
+        }
+
+        return bean;
+    }
+
+
+    @Override
+    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+
+        final AtomicBoolean flag = new AtomicBoolean();
+        ReflectionUtils.doWithMethods(bean.getClass(),
+                method -> flag.set(true),
+                method -> {
+                    //匹配符合的方法
+                    for (Class<? extends Annotation> annotationClass : annotations) {
+                        if (method.isAnnotationPresent(annotationClass)) {
+                            return true;
+                        }
+                    }
+                    return false;
+                });
+        //找到赋值
+        if (flag.get()) {
+            originalBeanClass = bean.getClass();
+        }
+        return bean;
+    }
+
+    private void setNamespace(Object bean, SocketIONamespace socketIONamespace, Field[] declaredFields) {
+        for (Field declaredField : declaredFields) {
+            if (declaredField.isAnnotationPresent(NamespaceReference.class)) {
+                try {
+                    declaredField.setAccessible(true);
+                    declaredField.set(bean, socketIONamespace);
+                } catch (IllegalAccessException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+
+        }
+    }
+}

+ 0 - 0
mec-websocket/src/main/resources/application.yml → mec-websocket/src/main/resources/application-template.yml


+ 26 - 1
pom.xml

@@ -281,7 +281,11 @@
 			<artifactId>spring-boot-starter-test</artifactId>
 			<scope>test</scope>
 		</dependency>
-
+		<dependency>
+			<groupId>com.google.guava</groupId>
+			<artifactId>guava</artifactId>
+			<version>20.0</version>
+		</dependency>
 		<dependency>
 			<groupId>org.springframework.cloud</groupId>
 			<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
@@ -290,8 +294,29 @@
 					<groupId>commons-lang</groupId>
 					<artifactId>commons-lang</artifactId>
 				</exclusion>
+				<exclusion>
+					<artifactId>guava</artifactId>
+					<groupId>com.google.guava</groupId>
+				</exclusion>
 			</exclusions>
 		</dependency>
+
+		<!--增加通用依赖-->
+		<dependency>
+			<groupId>org.projectlombok</groupId>
+			<artifactId>lombok</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+			<scope>test</scope>
+		</dependency>
 	</dependencies>
 
 	<build>