Browse Source

Merge branch 'master_saas' of http://git.dayaedu.com/yonge/mec into master_saas

yonge 3 years ago
parent
commit
d63812fb0d
43 changed files with 2484 additions and 153 deletions
  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. 1 1
      mec-biz/src/main/java/com/ym/mec/biz/dal/dao/ClassGroupStudentMapperDao.java
  4. 19 3
      mec-biz/src/main/java/com/ym/mec/biz/dal/dao/IndexBaseMonthDataDao.java
  5. 96 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/dao/websocket/WsConnectRecordInfoMapper.java
  6. 11 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/entity/ClassGroup.java
  7. 269 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/entity/websocket/WsConnectRecordInfo.java
  8. 803 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/entity/websocket/WsConnectRecordInfoExample.java
  9. 3 1
      mec-biz/src/main/java/com/ym/mec/biz/dal/enums/IndexErrorType.java
  10. 11 1
      mec-biz/src/main/java/com/ym/mec/biz/dal/page/ArrearageStudentsQueryInfo.java
  11. 11 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/page/ClassGroupQueryInfo.java
  12. 1 1
      mec-biz/src/main/java/com/ym/mec/biz/dal/page/EndCourseScheduleQueryInfo.java
  13. 11 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/page/NoClassMusicStudentQueryInfo.java
  14. 25 0
      mec-biz/src/main/java/com/ym/mec/biz/service/WsConnectService.java
  15. 1 1
      mec-biz/src/main/java/com/ym/mec/biz/service/impl/ClassGroupServiceImpl.java
  16. 2 2
      mec-biz/src/main/java/com/ym/mec/biz/service/impl/CourseScheduleServiceImpl.java
  17. 49 30
      mec-biz/src/main/java/com/ym/mec/biz/service/impl/IndexBaseMonthDataServiceImpl.java
  18. 4 2
      mec-biz/src/main/java/com/ym/mec/biz/service/impl/MusicGroupPaymentCalenderDetailServiceImpl.java
  19. 80 0
      mec-biz/src/main/java/com/ym/mec/biz/service/impl/WsConnectServiceImpl.java
  20. 8 3
      mec-biz/src/main/resources/config/mybatis/ClassGroupMapper.xml
  21. 6 0
      mec-biz/src/main/resources/config/mybatis/ClassGroupStudentMapperMapper.xml
  22. 10 4
      mec-biz/src/main/resources/config/mybatis/CourseScheduleMapper.xml
  23. 39 11
      mec-biz/src/main/resources/config/mybatis/IndexBaseMonthDataMapper.xml
  24. 7 0
      mec-biz/src/main/resources/config/mybatis/MusicGroupPaymentCalenderDetailMapper.xml
  25. 6 0
      mec-biz/src/main/resources/config/mybatis/StudentRegistrationMapper.xml
  26. 334 0
      mec-biz/src/main/resources/config/mybatis/WsConnectRecordInfoMapper.xml
  27. 6 1
      mec-websocket/pom.xml
  28. 0 17
      mec-websocket/src/main/java/com/ym/mec/web/WebSocketApplication.java
  29. 0 52
      mec-websocket/src/main/java/com/ym/mec/web/config/PermissionCheckService.java
  30. 4 4
      mec-websocket/src/main/java/com/ym/mec/web/config/ResourceServerConfig.java
  31. 73 0
      mec-websocket/src/main/java/com/ym/mec/web/config/SocketIoConfig.java
  32. 40 7
      mec-websocket/src/main/java/com/ym/mec/web/config/WebMvcConfig.java
  33. 3 10
      mec-websocket/src/main/java/com/ym/mec/web/controller/WebsocketController.java
  34. 46 0
      mec-websocket/src/main/java/com/ym/mec/web/handler/Chat2.java
  35. 235 0
      mec-websocket/src/main/java/com/ym/mec/web/handler/WhiteboardHandler.java
  36. 11 0
      mec-websocket/src/main/java/com/ym/mec/web/support/anno/NamespaceReference.java
  37. 12 0
      mec-websocket/src/main/java/com/ym/mec/web/support/anno/OnNamespace.java
  38. 31 0
      mec-websocket/src/main/java/com/ym/mec/web/support/mes/ChatObject.java
  39. 46 0
      mec-websocket/src/main/java/com/ym/mec/web/support/socket/NamespaceFactoryBean.java
  40. 51 0
      mec-websocket/src/main/java/com/ym/mec/web/support/socket/ServerRunner.java
  41. 89 0
      mec-websocket/src/main/java/com/ym/mec/web/support/socket/SocketEventScanner.java
  42. 0 0
      mec-websocket/src/main/resources/application-template.yml
  43. 28 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

+ 1 - 1
mec-biz/src/main/java/com/ym/mec/biz/dal/dao/ClassGroupStudentMapperDao.java

@@ -475,7 +475,7 @@ public interface ClassGroupStudentMapperDao extends BaseDAO<Long, ClassGroupStud
     List<BaseMapDto<Integer, Integer>> getStudentClassGroupBishopTeacherMap(@Param("studentIds") List<Integer> studentIds,
                                                                             @Param("musicGroupId") String musicGroupId);
 
-    List<Long> getLessThenThreeClassGroupIds(@Param("organIds") String organIds);
+    List<Long> getLessThenThreeClassGroupIds(@Param("organIds") String organIds, @Param("hasDesc") Boolean hasDesc);
 
     /**
      * @describe 获取学员与老师所在班级

+ 19 - 3
mec-biz/src/main/java/com/ym/mec/biz/dal/dao/IndexBaseMonthDataDao.java

@@ -169,7 +169,10 @@ public interface IndexBaseMonthDataDao extends BaseDAO<Long, IndexBaseMonthData>
                                                                           @Param("teachMode") TeachModeEnum teachMode,
                                                                           @Param("organIds") List<Integer> organIds,@Param("tenantId") Integer tenantId);
 
-    int countLessThenThreeClassGroupNum(@Param("organIds") Set<Integer> organIds, @Param("educationUserId") Integer educationUserId,@Param("tenantId") Integer tenantId);
+    int countLessThenThreeClassGroupNum(@Param("organIds") Set<Integer> organIds,
+                                        @Param("educationUserId") Integer educationUserId,
+                                        @Param("tenantId") Integer tenantId,
+                                        @Param("hasDesc") Boolean hasDesc);
 
 
     List<Map<Integer, Integer>> countLessThenThreeClassOrganGroupNum(@Param("organIds") Set<Integer> organIds,
@@ -192,7 +195,8 @@ public interface IndexBaseMonthDataDao extends BaseDAO<Long, IndexBaseMonthData>
 
     int countNoPaymentStudentNum(@Param("organIds") Set<Integer> organIds,
                                  @Param("educationUserId") Integer educationUserId,
-                                 @Param("noPaymentType") Integer noPaymentType,@Param("tenantId") Integer tenantId);
+                                 @Param("tenantId") Integer tenantId,
+                                 @Param("hasCourse") Boolean hasCourse);
 
 
 
@@ -230,7 +234,19 @@ public interface IndexBaseMonthDataDao extends BaseDAO<Long, IndexBaseMonthData>
      * @param organIds
      * @return
      */
-    int getAttendanceError(@Param("organIds") Set<Integer> organIds, @Param("startTime") String startTime, @Param("classGroupIds") List<Long> classGroupIds,@Param("tenantId") Integer tenantId);
+    int getTeacherAttendanceError(@Param("organIds") Set<Integer> organIds,
+                           @Param("startTime") String startTime,
+                           @Param("classGroupIds") List<Long> classGroupIds,
+                           @Param("tenantId") Integer tenantId);
+    /**
+     * 获取考勤异常编号
+     * @param organIds
+     * @return
+     */
+    int getStudentAttendanceError(@Param("organIds") Set<Integer> organIds,
+                           @Param("startTime") String startTime,
+                           @Param("classGroupIds") List<Long> classGroupIds,
+                           @Param("tenantId") Integer tenantId);
     List<Map<Integer, Integer>> getOrganAttendanceError(@Param("organIds") Set<Integer> organIds,
                                                         @Param("startTime") String startTime,
                                                         @Param("classGroupIds") List<Long> classGroupIds,

+ 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);
+}

+ 11 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/entity/ClassGroup.java

@@ -96,11 +96,22 @@ public class ClassGroup extends BaseEntity {
 	
 	@ApiModelProperty(value = "备注",required = false)
 	private String memo;
+
+	@ApiModelProperty(value = "班级异常提醒备注",required = false)
+	private String desc;
 	
 	private List<ClassGroupTeacherMapper> teacherMapperList = new ArrayList<ClassGroupTeacherMapper>();
 	
 	private List<CourseSchedule> courseScheduleList = new ArrayList<CourseSchedule>();
 
+	public String getDesc() {
+		return desc;
+	}
+
+	public void setDesc(String desc) {
+		this.desc = desc;
+	}
+
 	public String getOrganName() {
 		return organName;
 	}

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

@@ -0,0 +1,269 @@
+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 Mon Oct 31 11:58:02 CST 2022
+     */
+    private Long id;
+
+    /**
+     *
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database column ws_connect_record_info.room_id_
+     *
+     * @mbg.generated Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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.room_id_
+     *
+     * @return the value of ws_connect_record_info.room_id_
+     *
+     * @mbg.generated Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 CST 2022
+     */
+    public void setCreatedTime(Date createdTime) {
+        this.createdTime = createdTime;
+    }
+}

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

@@ -0,0 +1,803 @@
+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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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);
+        }
+    }
+}

+ 3 - 1
mec-biz/src/main/java/com/ym/mec/biz/dal/enums/IndexErrorType.java

@@ -9,6 +9,7 @@ import com.ym.mec.common.enums.BaseEnum;
 public enum IndexErrorType implements BaseEnum<String, IndexErrorType> {
     MUSIC_PATROL("MUSIC_PATROL", "乐团巡查"),
     HIGH_CLASS_STUDENT_LESS_THAN_THREE("HIGH_CLASS_STUDENT_LESS_THAN_THREE", "基础技能班学员数量异常"),
+    HIGH_CLASS_STUDENT_LESS_THAN_THREE_INFO("HIGH_CLASS_STUDENT_LESS_THAN_THREE_INFO", "基础技能班学员数量异常提醒"),
     MUSIC_PATROL_ITEM("MUSIC_PATROL_ITEM", "乐团巡查事项异常"),
     NO_CLASS_MUSIC_GROUP_STUDENT_INFO("NO_CLASS_MUSIC_GROUP_STUDENT_INFO", "当前有{0}个乐团共{1}名学员未加入任何班级"),
 
@@ -38,7 +39,8 @@ public enum IndexErrorType implements BaseEnum<String, IndexErrorType> {
     STUDENT_VISIT("STUDENT_VISIT", "回访任务未完成"),
 
     ATTENDANCE_SERVE("ATTENDANCE_SERVE", "考勤及服务"),
-    TEACHER_EXCEPTION_ATTENDANCE("TEACHER_EXCEPTION_ATTENDANCE", "课程考勤异常"),
+    TEACHER_EXCEPTION_ATTENDANCE("TEACHER_EXCEPTION_ATTENDANCE", "老师课程考勤异常"),
+    STUDENT_EXCEPTION_ATTENDANCE("STUDENT_EXCEPTION_ATTENDANCE", "学员课程考勤异常"),
     TEACHER_NOT_A_CLASS("TEACHER_NOT_A_CLASS", "课程异常"),
     TEACHER_SERVE_ERROR("TEACHER_SERVE_ERROR", "服务指标异常"),
     HAS_FREE_COURSE_TIMES("HAS_FREE_COURSE_TIMES", "学员已排课时长未消耗完"),

+ 11 - 1
mec-biz/src/main/java/com/ym/mec/biz/dal/page/ArrearageStudentsQueryInfo.java

@@ -1,6 +1,5 @@
 package com.ym.mec.biz.dal.page;
 
-import com.ym.mec.biz.dal.entity.MusicGroupPaymentCalender;
 import com.ym.mec.common.page.QueryInfo;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -23,6 +22,17 @@ public class ArrearageStudentsQueryInfo extends QueryInfo {
 
     private Integer courseViewType;
 
+    @ApiModelProperty(value = "是否排课(用于区分欠费学员异常和欠费学员提醒)")
+    private Boolean hasCourse = true;
+
+    public Boolean getHasCourse() {
+        return hasCourse;
+    }
+
+    public void setHasCourse(Boolean hasCourse) {
+        this.hasCourse = hasCourse;
+    }
+
     public Integer getCourseViewType() {
         return courseViewType;
     }

+ 11 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/page/ClassGroupQueryInfo.java

@@ -26,6 +26,17 @@ public class ClassGroupQueryInfo extends EducationBaseQueryInfo {
     @ApiModelProperty(value = "少于3人的线上基础技能班,大于0")
     private Integer lessThenThreeHighOnline;
 
+    @ApiModelProperty(value = "是否有班级备注(用于线上基础技能班事项提醒),事项提醒传true,异常提醒传false")
+    private Boolean hasDesc = false;
+
+	public Boolean getHasDesc() {
+		return hasDesc;
+	}
+
+	public void setHasDesc(Boolean hasDesc) {
+		this.hasDesc = hasDesc;
+	}
+
 	public Integer getLessThenThreeHighOnline() {
 		return lessThenThreeHighOnline;
 	}

+ 1 - 1
mec-biz/src/main/java/com/ym/mec/biz/dal/page/EndCourseScheduleQueryInfo.java

@@ -82,7 +82,7 @@ public class EndCourseScheduleQueryInfo extends QueryInfo {
     @ApiModelProperty(value = "合并课程类型,MASTER 主课,ASSIST 副课,ALL 全部")
     private String mergeCourseType;
 
-    @ApiModelProperty(value = "ERR_ATTENDANCE 考勤异常,NO_ATTENDANCE 课程异常")
+    @ApiModelProperty(value = "TEACHER_ERR_ATTENDANCE 老师考勤异常,STUDENT_ERR_ATTENDANCE 学员考勤异常,NO_ATTENDANCE 课程异常")
     private String searchType;
 
     @ApiModelProperty(value = "课程编号搜索")

+ 11 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/page/NoClassMusicStudentQueryInfo.java

@@ -14,6 +14,17 @@ public class NoClassMusicStudentQueryInfo extends EducationBaseQueryInfo {
     @ApiModelProperty(value = "学员姓名/编号")
     private String studentSearch;
 
+    @ApiModelProperty(value = "乐团是否开课")
+    private Boolean hasCourse = true;
+
+    public Boolean getHasCourse() {
+        return hasCourse;
+    }
+
+    public void setHasCourse(Boolean hasCourse) {
+        this.hasCourse = hasCourse;
+    }
+
     public String getMusicGroupSearch() {
         return musicGroupSearch;
     }

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

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

+ 1 - 1
mec-biz/src/main/java/com/ym/mec/biz/service/impl/ClassGroupServiceImpl.java

@@ -3638,7 +3638,7 @@ public class ClassGroupServiceImpl extends BaseServiceImpl<Integer, ClassGroup>
         List<ClassGroupTeachersDto> dataList = null;
         int count;
         if (Objects.nonNull(queryInfo.getLessThenThreeHighOnline())) {
-            List<Long> lessThenThreeClassGroupIds = classGroupStudentMapperDao.getLessThenThreeClassGroupIds(queryInfo.getOrganIds());
+            List<Long> lessThenThreeClassGroupIds = classGroupStudentMapperDao.getLessThenThreeClassGroupIds(queryInfo.getOrganIds(),queryInfo.getHasDesc());
             params.put("classGroupIds", lessThenThreeClassGroupIds);
             if (CollectionUtils.isEmpty(lessThenThreeClassGroupIds)) {
                 count = 0;

+ 2 - 2
mec-biz/src/main/java/com/ym/mec/biz/service/impl/CourseScheduleServiceImpl.java

@@ -1914,7 +1914,7 @@ public class CourseScheduleServiceImpl extends BaseServiceImpl<Long, CourseSched
                 .map(CourseSchedule::getId)
                 .collect(Collectors.toList());
 
-        HashSet<Long> courseScheduleIdsSet = new HashSet<>(existCourseScheduleIds);
+//        HashSet<Long> courseScheduleIdsSet = new HashSet<>(existCourseScheduleIds);
 
         //合并新课程和已存在的课程
         List<CourseSchedule> allCourseSchedules;
@@ -1958,7 +1958,7 @@ public class CourseScheduleServiceImpl extends BaseServiceImpl<Long, CourseSched
         Map<Long, IntegerAndIntegerListDto> classGroupTeachingTeacherMap = classGroupAndUserIdsMap.stream()
                 .collect(Collectors.toMap(IntegerAndIntegerListDto::getId, integerAndIntegerListDto -> integerAndIntegerListDto));
 
-        Set<Long> existCourseScheduleIdsSet=new HashSet<>(existCourseScheduleIds);
+//        Set<Long> existCourseScheduleIdsSet=new HashSet<>(existCourseScheduleIds);
 
         Map<String, List<CourseSchedule>> existClassDateCoursesMap = allCourseSchedules.stream().collect(Collectors.groupingBy(c -> DateUtil.dateToString(c.getClassDate(), "yyyy-MM-dd")));
         Map<String, List<CourseSchedule>> newClassDateCoursesMap = courseSchedules.stream().collect(Collectors.groupingBy(c -> DateUtil.dateToString(c.getClassDate(), "yyyy-MM-dd")));

+ 49 - 30
mec-biz/src/main/java/com/ym/mec/biz/service/impl/IndexBaseMonthDataServiceImpl.java

@@ -664,7 +664,7 @@ public class IndexBaseMonthDataServiceImpl extends BaseServiceImpl<Long, IndexBa
 	@Override
 	public Map<String, Object> getIndexErrData(String organIdsStr, IndexErrorType errorType) {
 		//只筛选指定时间之后的数据
-		String startTime = DateUtil.format(DateUtil.getFirstDayOfMonth(DateUtil.addMonths(new Date(), -2)),DateUtil.ISO_EXPANDED_DATE_FORMAT);
+		String startTime = DateUtil.format(DateUtil.getFirstDayOfMonth(DateUtil.addMonths(new Date(), -1)),DateUtil.ISO_EXPANDED_DATE_FORMAT);
 
 		Set<Integer> organIds = null;
 		if(StringUtils.isNotBlank(organIdsStr)){
@@ -689,7 +689,7 @@ public class IndexBaseMonthDataServiceImpl extends BaseServiceImpl<Long, IndexBa
 			List<IndexErrInfoDto> oneChild = new ArrayList<>();
 
 			//基础技能班学员数量异常
-			oneChild.add(new IndexErrInfoDto(IndexErrorType.HIGH_CLASS_STUDENT_LESS_THAN_THREE, IndexErrorType.HIGH_CLASS_STUDENT_LESS_THAN_THREE.getMsg(), indexBaseMonthDataDao.countLessThenThreeClassGroupNum(organIds,educationUserId,tenantId), null));
+			oneChild.add(new IndexErrInfoDto(IndexErrorType.HIGH_CLASS_STUDENT_LESS_THAN_THREE, IndexErrorType.HIGH_CLASS_STUDENT_LESS_THAN_THREE.getMsg(), indexBaseMonthDataDao.countLessThenThreeClassGroupNum(organIds,educationUserId,tenantId,false), null));
 
 			//乐团巡查事项异常
 			int errInspection = indexBaseMonthDataDao.queryErrInspection(organIds,startTime,tenantId);
@@ -722,7 +722,8 @@ public class IndexBaseMonthDataServiceImpl extends BaseServiceImpl<Long, IndexBa
 			twoChild.add(new IndexErrInfoDto(IndexErrorType.STUDENT_ERROR_LEAVE, IndexErrorType.STUDENT_ERROR_LEAVE.getMsg(),studentErrorLeave , null));
 
 			//未缴费学员数
-			twoChild.add(new IndexErrInfoDto(IndexErrorType.STUDENT_ARREARAGE, IndexErrorType.STUDENT_ARREARAGE.getMsg(), indexBaseMonthDataDao.countNoPaymentStudentNum(organIds,educationUserId,0,tenantId), Arrays.asList(educationUserId)));
+			twoChild.add(new IndexErrInfoDto(IndexErrorType.STUDENT_ARREARAGE, IndexErrorType.STUDENT_ARREARAGE.getMsg(),
+					indexBaseMonthDataDao.countNoPaymentStudentNum(organIds,educationUserId,tenantId,true), Arrays.asList(educationUserId)));
 
 			//申请退团学员数
 			List<Long> quitGroupNum = indexBaseMonthDataDao.queryApplyForQuitGroupNum(organIds,educationUserId,tenantId);
@@ -780,22 +781,25 @@ public class IndexBaseMonthDataServiceImpl extends BaseServiceImpl<Long, IndexBa
 			List<IndexErrInfoDto> fourChild = new ArrayList<>();
 
 			//课程考勤异常
-			int attendanceError = 0;
+			int teacherAttendanceError = 0;
+			int studentAttendanceError = 0;
 			int noAttendance = 0;
 			//课程时间安排异常
-			int courseTimeError = 0;
+//			int courseTimeError = 0;
 			if(classGroupIds == null || classGroupIds.size() > 0){
-				attendanceError = indexBaseMonthDataDao.getAttendanceError(organIds,startTime,classGroupIds,tenantId);
+				teacherAttendanceError = indexBaseMonthDataDao.getTeacherAttendanceError(organIds,startTime,classGroupIds,tenantId);
+				studentAttendanceError = indexBaseMonthDataDao.getStudentAttendanceError(organIds,startTime,classGroupIds,tenantId);
 				noAttendance = indexBaseMonthDataDao.getNoAttendance(organIds,startTime,classGroupIds,tenantId);
-				String courseStartTimeError = sysTenantConfigService.getTenantConfigValue(SysConfigService.COURSE_START_TIME_ERROR, tenantId);
-				String courseEndTimeError = sysTenantConfigService.getTenantConfigValue(SysConfigService.COURSE_END_TIME_ERROR, tenantId);
-				if(StringUtils.isNotEmpty(courseStartTimeError) && StringUtils.isNotEmpty(courseEndTimeError)){
-					courseTimeError = indexBaseMonthDataDao.getCourseTimeError(organIds,classGroupIds,courseStartTimeError,courseEndTimeError,tenantId);
-				}
+//				String courseStartTimeError = sysTenantConfigService.getTenantConfigValue(SysConfigService.COURSE_START_TIME_ERROR, tenantId);
+//				String courseEndTimeError = sysTenantConfigService.getTenantConfigValue(SysConfigService.COURSE_END_TIME_ERROR, tenantId);
+//				if(StringUtils.isNotEmpty(courseStartTimeError) && StringUtils.isNotEmpty(courseEndTimeError)){
+//					courseTimeError = indexBaseMonthDataDao.getCourseTimeError(organIds,classGroupIds,courseStartTimeError,courseEndTimeError,tenantId);
+//				}
 			}
-			fourChild.add(new IndexErrInfoDto(IndexErrorType.COURSE_TIME_ERROR, IndexErrorType.COURSE_TIME_ERROR.getMsg(), courseTimeError, null));
+//			fourChild.add(new IndexErrInfoDto(IndexErrorType.COURSE_TIME_ERROR, IndexErrorType.COURSE_TIME_ERROR.getMsg(), courseTimeError, null));
 
-			fourChild.add(new IndexErrInfoDto(IndexErrorType.TEACHER_EXCEPTION_ATTENDANCE, IndexErrorType.TEACHER_EXCEPTION_ATTENDANCE.getMsg(), attendanceError, null));
+			fourChild.add(new IndexErrInfoDto(IndexErrorType.TEACHER_EXCEPTION_ATTENDANCE, IndexErrorType.TEACHER_EXCEPTION_ATTENDANCE.getMsg(), teacherAttendanceError, null));
+			fourChild.add(new IndexErrInfoDto(IndexErrorType.STUDENT_EXCEPTION_ATTENDANCE, IndexErrorType.STUDENT_EXCEPTION_ATTENDANCE.getMsg(), studentAttendanceError, null));
 
 			//课程异常
 			fourChild.add(new IndexErrInfoDto(IndexErrorType.TEACHER_NOT_A_CLASS, IndexErrorType.TEACHER_NOT_A_CLASS.getMsg(), noAttendance, null));
@@ -1104,7 +1108,8 @@ public class IndexBaseMonthDataServiceImpl extends BaseServiceImpl<Long, IndexBa
 		List<IndexErrInfoDto> result = new ArrayList<>();
 
 		//未缴费学员数
-		result.add(new IndexErrInfoDto(IndexErrorType.STUDENT_NOT_PAYMENT, IndexErrorType.STUDENT_NOT_PAYMENT.getMsg(), indexBaseMonthDataDao.countNoPaymentStudentNum(organIdSet,educationUserId, 1, tenantId), Arrays.asList(educationUserId)));
+		result.add(new IndexErrInfoDto(IndexErrorType.STUDENT_NOT_PAYMENT, IndexErrorType.STUDENT_NOT_PAYMENT.getMsg(),
+				indexBaseMonthDataDao.countNoPaymentStudentNum(organIdSet,educationUserId,tenantId,false), Arrays.asList(educationUserId)));
 
 		String monthStr = DateUtil.format(date, DateUtil.ISO_YEAR_MONTH_FORMAT);
 		//全职未离职老师
@@ -1125,6 +1130,9 @@ public class IndexBaseMonthDataServiceImpl extends BaseServiceImpl<Long, IndexBa
 		List<String> musicGroupIds = musicGroupDao.queryHasSubCourseTimes(organIds,tenantId);
 		result.add(new IndexErrInfoDto(IndexErrorType.MUSIC_GROUP_HAS_COURSE_TIMES, IndexErrorType.MUSIC_GROUP_HAS_COURSE_TIMES.getMsg(),musicGroupIds.size(), null));
 
+		//基础技能班学员数量异常提醒
+		result.add(new IndexErrInfoDto(IndexErrorType.HIGH_CLASS_STUDENT_LESS_THAN_THREE_INFO, IndexErrorType.HIGH_CLASS_STUDENT_LESS_THAN_THREE_INFO.getMsg(), indexBaseMonthDataDao.countLessThenThreeClassGroupNum(organIdSet,educationUserId,tenantId,true), null));
+
 		//合作单位回款提醒
 		Map<String, Object> params = new HashMap<>();
 		params.put("organId", organIds);
@@ -1137,7 +1145,7 @@ public class IndexBaseMonthDataServiceImpl extends BaseServiceImpl<Long, IndexBa
 	@Override
 	public Map<String,Boolean> hasIndexErrData(String organIdsStr) {
 		Integer tenantId = TenantContextHolder.getTenantId();
-		String startTime = DateUtil.format(DateUtil.getFirstDayOfMonth(DateUtil.addMonths(new Date(), -2)),DateUtil.ISO_EXPANDED_DATE_FORMAT);
+		String startTime = DateUtil.format(DateUtil.getFirstDayOfMonth(DateUtil.addMonths(new Date(), -1)),DateUtil.ISO_EXPANDED_DATE_FORMAT);
 
 		Map<String,Boolean> resultMap = new HashMap<>();
 		Set<Integer> organIds = null;
@@ -1156,7 +1164,7 @@ public class IndexBaseMonthDataServiceImpl extends BaseServiceImpl<Long, IndexBa
 		}
 		Date date = new Date();
 		boolean flag1 = false;
-		int countLessThenThreeClassGroupNum = indexBaseMonthDataDao.countLessThenThreeClassGroupNum(organIds,educationUserId, tenantId);
+		int countLessThenThreeClassGroupNum = indexBaseMonthDataDao.countLessThenThreeClassGroupNum(organIds,educationUserId, tenantId,false);
 		if(countLessThenThreeClassGroupNum > 0){
 			flag1 = true;
 		}
@@ -1194,7 +1202,7 @@ public class IndexBaseMonthDataServiceImpl extends BaseServiceImpl<Long, IndexBa
 		}
 
 		if(!flag2){
-			int countNoPaymentStudentNum = indexBaseMonthDataDao.countNoPaymentStudentNum(organIds,educationUserId, 0, tenantId);
+			int countNoPaymentStudentNum = indexBaseMonthDataDao.countNoPaymentStudentNum(organIds,educationUserId, tenantId,true);
 			if(countNoPaymentStudentNum > 0){
 				flag2 = true;
 			}
@@ -1247,7 +1255,7 @@ public class IndexBaseMonthDataServiceImpl extends BaseServiceImpl<Long, IndexBa
 		resultMap.put("teacherInfo",flag3);
 
 		boolean flag4 = false;
-		if(!flag4){
+/*		if(!flag4){
 			int courseTimeError = 0;
 			if(classGroupIds == null || classGroupIds.size() > 0){
 				String courseStartTimeError = sysTenantConfigService.getTenantConfigValue(SysConfigService.COURSE_START_TIME_ERROR, tenantId);
@@ -1259,11 +1267,20 @@ public class IndexBaseMonthDataServiceImpl extends BaseServiceImpl<Long, IndexBa
 			if(courseTimeError > 0){
 				flag4 = true;
 			}
+		}*/
+		if(!flag4){
+			int attendanceError = 0;
+			if(classGroupIds == null || classGroupIds.size() > 0){
+				attendanceError = indexBaseMonthDataDao.getTeacherAttendanceError(organIds,startTime,classGroupIds, tenantId);
+			}
+			if(attendanceError > 0){
+				flag4 = true;
+			}
 		}
 		if(!flag4){
 			int attendanceError = 0;
 			if(classGroupIds == null || classGroupIds.size() > 0){
-				attendanceError = indexBaseMonthDataDao.getAttendanceError(organIds,startTime,classGroupIds, tenantId);
+				attendanceError = indexBaseMonthDataDao.getStudentAttendanceError(organIds,startTime,classGroupIds, tenantId);
 			}
 			if(attendanceError > 0){
 				flag4 = true;
@@ -1306,7 +1323,7 @@ public class IndexBaseMonthDataServiceImpl extends BaseServiceImpl<Long, IndexBa
 			}
 		}
 		if(!flag5){
-			int countNoPaymentStudentNum = indexBaseMonthDataDao.countNoPaymentStudentNum(organIds,educationUserId, 1, tenantId);
+			int countNoPaymentStudentNum = indexBaseMonthDataDao.countNoPaymentStudentNum(organIds,educationUserId, tenantId,false);
 			if(countNoPaymentStudentNum > 0){
 				flag5 = true;
 			}
@@ -1325,19 +1342,21 @@ public class IndexBaseMonthDataServiceImpl extends BaseServiceImpl<Long, IndexBa
 			}
 		}
 		if(!flag5){
-			if(!flag5){
-				List<StudentRegistration> studentRegistrations = studentRegistrationDao.queryMemberEndAutoQuitMusic(null,educationUserId,organIdsStr);
-				if(studentRegistrations.size() > 0){
-					flag5 = true;
-				}
+			List<StudentRegistration> studentRegistrations = studentRegistrationDao.queryMemberEndAutoQuitMusic(null,educationUserId,organIdsStr);
+			if(studentRegistrations.size() > 0){
+				flag5 = true;
 			}
 		}
 		if(!flag5){
-			if(!flag5){
-				List<String> strings = musicGroupDao.queryHasSubCourseTimes(organIdsStr, tenantId);
-				if(strings.size() > 0){
-					flag5 = true;
-				}
+			List<String> strings = musicGroupDao.queryHasSubCourseTimes(organIdsStr, tenantId);
+			if(strings.size() > 0){
+				flag5 = true;
+			}
+		}
+		if(!flag5){
+			int countLessThenThreeClassGroupNumInfo = indexBaseMonthDataDao.countLessThenThreeClassGroupNum(organIds,educationUserId, tenantId,true);
+			if(countLessThenThreeClassGroupNumInfo > 0){
+				flag5 = true;
 			}
 		}
 		resultMap.put("remindMatterData",flag5);

+ 4 - 2
mec-biz/src/main/java/com/ym/mec/biz/service/impl/MusicGroupPaymentCalenderDetailServiceImpl.java

@@ -1,11 +1,13 @@
 package com.ym.mec.biz.service.impl;
 
 import com.ym.mec.biz.dal.dao.*;
-import com.ym.mec.biz.dal.dto.*;
+import com.ym.mec.biz.dal.dto.FeeStudentDto;
+import com.ym.mec.biz.dal.dto.MusicArrearageStudentDto;
+import com.ym.mec.biz.dal.dto.MusicGroupPaymentCalenderDetailDto;
+import com.ym.mec.biz.dal.dto.SimpleUserDto;
 import com.ym.mec.biz.dal.entity.*;
 import com.ym.mec.biz.dal.entity.MusicGroupPaymentCalender.PaymentCalenderStatusEnum;
 import com.ym.mec.biz.dal.entity.MusicGroupStudentFee.PaymentStatus;
-import com.ym.mec.biz.dal.enums.GroupType;
 import com.ym.mec.biz.dal.enums.MessageTypeEnum;
 import com.ym.mec.biz.dal.enums.OrderDetailTypeEnum;
 import com.ym.mec.biz.dal.page.ArrearageStudentsQueryInfo;

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

@@ -0,0 +1,80 @@
+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.List;
+
+/**
+ * Created by Eric.Shang on 2022/10/31.
+ */
+@Service
+public class WsConnectServiceImpl implements WsConnectService {
+
+    @Autowired
+    private WsConnectRecordInfoMapper wsConnectRecordInfoMapper;
+
+
+    /**
+     * 保存用户ws连接记录信息
+     *
+     * @param roomId     房间ID
+     * @param userid     用户ID
+     * @param clientType 客户端类型
+     * @return int
+     */
+    @Override
+    public int saveWsConnectRecordInfo(String roomId, String userid, String clientType) {
+
+        // 连接记录信息
+        WsConnectRecordInfo record = new WsConnectRecordInfo();
+        record.setRoomId(roomId);
+        record.setUserId(Long.parseLong(userid));
+        record.setClientType(clientType);
+        record.setConnectTime(DateTime.now().toDate());
+
+        // 保存连接记录信息
+        return wsConnectRecordInfoMapper.insertSelective(record);
+    }
+
+    /**
+     * 更新用户ws连接断开时间
+     *
+     * @param roomId     房间ID
+     * @param userId     用户
+     * @param clientType 客户端类型
+     * @return int
+     */
+    @Override
+    public int updateWsDisconnectRecordInfo(String roomId, String userId, String clientType) {
+
+        // 查询连接记录信息
+        WsConnectRecordInfoExample example = new WsConnectRecordInfoExample();
+        example.or().andRoomIdEqualTo(roomId)
+                .andUserIdEqualTo(Long.parseLong(userId))
+                .andClientTypeEqualTo(clientType)
+                .andDisconnectTimeIsNull();
+
+        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);
+    }
+}

+ 8 - 3
mec-biz/src/main/resources/config/mybatis/ClassGroupMapper.xml

@@ -23,6 +23,7 @@
         <result column="total_class_times_" property="totalClassTimes"/>
         <result column="img_" property="img"/>
         <result column="memo_" property="memo"/>
+        <result column="desc_" property="desc"/>
         <result column="current_class_times_" property="currentClassTimes"/>
 		<result column="tenant_id_" property="tenantId" />
     </resultMap>
@@ -121,9 +122,6 @@
             <if test="studentNum != null">
                 student_num_ = #{studentNum},
             </if>
-            <if test="updateTime != null">
-                update_time_ = NOW(),
-            </if>
             <if test="musicGroupId != null">
                 music_group_id_ = #{musicGroupId},
             </if>
@@ -154,6 +152,13 @@
             <if test="delFlag != null">
                 del_flag_ = #{delFlag},
             </if>
+            <if test="desc != null and desc != ''">
+                desc_=#{desc},
+            </if>
+            <if test="desc == null or desc == ''">
+                desc_ = NULL,
+            </if>
+            update_time_ = NOW()
         </set>
         WHERE id_ = #{id} and tenant_id_ = #{tenantId}
     </update>

+ 6 - 0
mec-biz/src/main/resources/config/mybatis/ClassGroupStudentMapperMapper.xml

@@ -657,6 +657,12 @@
         AND cs.del_flag_=0
         AND cs.status_='NOT_START'
         AND ( cs.new_course_id_ IS NULL OR cs.new_course_id_ = cs.id_ )
+        <if test="hasDesc != null and hasDesc == true">
+            AND cg.desc_ IS NOT NULL
+        </if>
+        <if test="hasDesc != null and hasDesc == false">
+            AND cg.desc_ IS NULL
+        </if>
         <if test="organIds!=null and organIds!=''">
             AND FIND_IN_SET(mg.organ_id_, #{organIds})
         </if>

+ 10 - 4
mec-biz/src/main/resources/config/mybatis/CourseScheduleMapper.xml

@@ -2451,11 +2451,11 @@
         COUNT(DISTINCT cs.id_)
         FROM
         course_schedule cs
-        <if test="searchType == 'ERR_ATTENDANCE'">
+        <if test="searchType == 'TEACHER_ERR_ATTENDANCE' or searchType == 'STUDENT_ERR_ATTENDANCE'">
             LEFT JOIN course_schedule_student_payment cssp ON cssp.course_schedule_id_ = cs.id_
         </if>
         LEFT JOIN student_attendance sa ON sa.course_schedule_id_ = cs.id_
-        <if test="searchType == 'ERR_ATTENDANCE'">
+        <if test="searchType == 'ERR_ATTENDANCE' or searchType == 'STUDENT_ERR_ATTENDANCE'">
             AND cssp.user_id_ = sa.user_id_
         </if>
         LEFT JOIN teacher_attendance ta on ta.course_schedule_id_ = cs.id_
@@ -2562,14 +2562,20 @@
         <if test="tenantId != null">
             AND cs.tenant_id_ = #{tenantId}
         </if>
-        <if test="searchType == 'ERR_ATTENDANCE'">
+        <if test="searchType == 'TEACHER_ERR_ATTENDANCE'">
             AND ta.teacher_id_ = cs.actual_teacher_id_
             AND cs.status_ = 'OVER'
-            AND (((ta.sign_in_status_ = 0 OR ta.sign_in_status_ IS NULL OR ta.sign_out_status_ = 0 OR ta.sign_out_status_ IS NULL) AND ta.dispose_content_ IS NULL) OR (sa.id_ IS NULL OR (sa.status_ = 'TRUANT' AND sa.visit_flag_ = 0)))
+            AND ((ta.sign_in_status_ = 0 OR ta.sign_in_status_ IS NULL OR ta.sign_out_status_ = 0 OR ta.sign_out_status_ IS NULL) AND ta.dispose_content_ IS NULL)
             AND ((ta.sign_in_status_ IS NULL AND ta.sign_out_status_ IS NOT NULL) OR (ta.sign_out_status_ IS NULL AND ta.sign_in_status_ IS NOT NULL) OR (ta.sign_out_status_ IS NOT NULL AND ta.sign_in_status_ IS NOT NULL))
             AND EXISTS (SELECT id_ FROM course_schedule_teacher_salary WHERE cs.id_=course_schedule_id_ AND settlement_time_ IS NULL)
             AND (cs.new_course_id_ IS NULL OR cs.new_course_id_=cs.id_) AND cssp.id_ IS NOT NULL
         </if>
+        <if test="searchType == 'STUDENT_ERR_ATTENDANCE'">
+            AND cs.status_ = 'OVER'
+            AND (sa.id_ IS NULL OR (sa.status_ = 'TRUANT' AND sa.visit_flag_ = 0))
+            AND EXISTS (SELECT id_ FROM course_schedule_teacher_salary WHERE cs.id_=course_schedule_id_ AND settlement_time_ IS NULL)
+            AND (cs.new_course_id_ IS NULL OR cs.new_course_id_=cs.id_) AND cssp.id_ IS NOT NULL
+        </if>
         <if test="searchType == 'NO_ATTENDANCE'">
             AND ta.teacher_id_ = cs.actual_teacher_id_
             AND cs.status_ = 'OVER' AND ta.sign_in_time_ IS NULL AND ta.sign_out_time_ IS NULL AND ta.dispose_content_ IS NULL

+ 39 - 11
mec-biz/src/main/resources/config/mybatis/IndexBaseMonthDataMapper.xml

@@ -759,6 +759,12 @@
 		AND cs.del_flag_=0
 		AND cs.status_='NOT_START'
 		AND ( cs.new_course_id_ IS NULL OR cs.new_course_id_ = cs.id_ )
+		<if test="hasDesc != null and hasDesc == true">
+			AND cg.desc_ IS NOT NULL
+		</if>
+		<if test="hasDesc != null and hasDesc == false">
+			AND cg.desc_ IS NULL
+		</if>
 		<if test="educationUserId != null">
 			AND mg.educational_teacher_id_ = #{educationUserId}
 		</if>
@@ -828,20 +834,21 @@
 			COUNT(DISTINCT mgpc.music_group_id_,mgpcd.user_id_)
 		FROM
 		music_group_payment_calender_detail mgpcd
+		LEFT JOIN music_group_payment_student_course_detail mgpsc ON mgpsc.music_group_payment_calender_id_ = mgpcd.music_group_payment_calender_id_
 		LEFT JOIN music_group_payment_calender mgpc ON mgpcd.music_group_payment_calender_id_ = mgpc.id_
 		LEFT JOIN music_group mg ON mgpc.music_group_id_ = mg.id_
 		LEFT JOIN student_registration sr ON sr.music_group_id_ = mgpc.music_group_id_ AND mgpcd.user_id_ = sr.user_id_
 		WHERE
 		mg.status_ = 'PROGRESS' and mg.tenant_id_ = #{tenantId}
 		AND ((mgpc.member_rank_setting_id_ IS NOT NULL AND (sr.membership_end_time_ &lt; NOW() OR sr.membership_end_time_ IS NULL)) OR mgpc.member_rank_setting_id_ IS NULL)
-		<if test="noPaymentType==null or noPaymentType==0">
-			AND DATE_FORMAT(NOW(),'%Y-%m-%d') > DATE_FORMAT(mgpc.deadline_payment_date_,'%Y-%m-%d')
-		</if>
-		<if test="noPaymentType!=null and noPaymentType==1">
-			AND DATE_FORMAT(NOW(),'%Y-%m-%d') BETWEEN DATE_FORMAT(mgpc.start_payment_date_,'%Y-%m-%d') AND DATE_FORMAT(mgpc.deadline_payment_date_,'%Y-%m-%d')
-		</if>
 		AND mgpcd.payment_status_ = 'NON_PAYMENT' AND mgpc.batch_no_ IS NOT NULL AND mgpc.pay_user_type_ = 'STUDENT'
 		AND mgpc.current_total_amount_ > 0
+		<if test="hasCourse != null and hasCourse == 'true'">
+			AND mgpsc.used_course_minutes_ > 0
+		</if>
+		<if test="hasCourse != null and hasCourse == 'false'">
+			AND mgpsc.used_course_minutes_ = 0
+		</if>
 		<if test="educationUserId != null">
 			AND mg.educational_teacher_id_ = #{educationUserId}
 		</if>
@@ -999,14 +1006,13 @@
 		GROUP BY cs.organ_id_
 	</select>
 
-    <select id="getAttendanceError" resultType="int">
+    <select id="getTeacherAttendanceError" resultType="int">
 		SELECT COUNT(DISTINCT c.id_) FROM (SELECT cs.id_ FROM course_schedule cs
 		LEFT JOIN teacher_attendance ta ON ta.course_schedule_id_ = cs.id_
 		LEFT JOIN course_schedule_student_payment cssp ON cssp.course_schedule_id_ = cs.id_
-		LEFT JOIN student_attendance sa ON sa.course_schedule_id_ = cssp.course_schedule_id_ AND cssp.user_id_ = sa.user_id_
 		WHERE ta.teacher_id_ = cs.actual_teacher_id_ AND cs.pre_course_flag_ = 0 and cs.tenant_id_ = #{tenantId}
-		AND cs.status_ = 'OVER' AND cs.del_flag_ = 0 AND cs.class_date_ >= '2021-02-01'
-		AND (((ta.sign_in_status_ = 0 OR ta.sign_in_status_ IS NULL OR ta.sign_out_status_ = 0 OR ta.sign_out_status_ IS NULL) AND ta.dispose_content_ IS NULL) OR (sa.id_ IS NULL OR (sa.status_ = 'TRUANT' AND sa.visit_flag_ = 0)))
+		AND cs.status_ = 'OVER' AND cs.del_flag_ = 0 AND cs.class_date_ >= #{startTime}
+		AND ((ta.sign_in_status_ = 0 OR ta.sign_in_status_ IS NULL OR ta.sign_out_status_ = 0 OR ta.sign_out_status_ IS NULL) AND ta.dispose_content_ IS NULL)
 		AND ((ta.sign_in_status_ IS NULL AND ta.sign_out_status_ IS NOT NULL) OR (ta.sign_out_status_ IS NULL AND ta.sign_in_status_ IS NOT NULL) OR (ta.sign_out_status_ IS NOT NULL AND ta.sign_in_status_ IS NOT NULL))
 		AND (cs.new_course_id_ IS NULL OR cs.new_course_id_=cs.id_) AND cssp.id_ IS NOT NULL
 		AND EXISTS (SELECT id_ FROM course_schedule_teacher_salary WHERE cs.id_=course_schedule_id_ AND settlement_time_ IS NULL)
@@ -1025,6 +1031,28 @@
 		GROUP BY cs.id_) c
 	</select>
 
+    <select id="getStudentAttendanceError" resultType="int">
+		SELECT COUNT(DISTINCT c.id_) FROM (SELECT cs.id_ FROM course_schedule cs
+		LEFT JOIN course_schedule_student_payment cssp ON cssp.course_schedule_id_ = cs.id_
+		LEFT JOIN student_attendance sa ON sa.course_schedule_id_ = cssp.course_schedule_id_ AND cssp.user_id_ = sa.user_id_
+		WHERE cs.pre_course_flag_ = 0 AND cs.status_ = 'OVER' AND cs.del_flag_ = 0 AND cs.class_date_ >= #{startTime}
+		AND (sa.id_ IS NULL OR (sa.status_ = 'TRUANT' AND sa.visit_flag_ = 0)) AND cssp.id_ IS NOT NULL
+		AND EXISTS (SELECT id_ FROM course_schedule_teacher_salary WHERE cs.id_=course_schedule_id_ AND settlement_time_ IS NULL)
+		<if test="classGroupIds != null and classGroupIds.size() > 0">
+			AND cs.class_group_id_ IN
+			<foreach collection="classGroupIds" item="classGroupId" open="(" close=")" separator=",">
+				#{classGroupId}
+			</foreach>
+		</if>
+		<if test="organIds != null and organIds.size()>0">
+			AND cs.organ_id_ IN
+			<foreach collection="organIds" item="organId" open="(" close=")" separator=",">
+				#{organId}
+			</foreach>
+		</if>
+		GROUP BY cs.id_) c
+	</select>
+
 	<select id="getOrganAttendanceError" resultType="map">
 		SELECT
 			c.organ_id_ AS 'key',
@@ -1270,7 +1298,7 @@
 			sr.music_group_status_ = 'NORMAL' and sr.tenant_id_ = #{tenantId}
 			AND mg.status_ = 'PROGRESS'
 			AND cgsm.id_ IS NULL
-			AND sub.parent_subject_id_!=24
+			AND sub.parent_subject_id_ != 24 AND mg.first_course_start_time_ >= NOW()
 			<if test="educationUserId != null">
 				AND mg.educational_teacher_id_ = #{educationUserId}
 			</if>

+ 7 - 0
mec-biz/src/main/resources/config/mybatis/MusicGroupPaymentCalenderDetailMapper.xml

@@ -442,6 +442,12 @@
 		<where>
 			mg.status_ = 'PROGRESS' and mg.tenant_id_ = #{tenantId}
 			AND ((mgpc.member_rank_setting_id_ IS NOT NULL AND (sr.membership_end_time_ &lt; NOW() OR sr.membership_end_time_ IS NULL)) OR mgpc.member_rank_setting_id_ IS NULL)
+			<if test="hasCourse != null and hasCourse == 'true'">
+				AND mgpsc.used_course_minutes_ > 0
+			</if>
+			<if test="hasCourse != null and hasCourse == 'false'">
+				AND mgpsc.used_course_minutes_ = 0
+			</if>
 			<if test="noPaymentType==null or noPaymentType==0">
 				AND DATE_FORMAT(NOW(),'%Y-%m-%d') > DATE_FORMAT(mgpc.deadline_payment_date_,'%Y-%m-%d')
 			</if>
@@ -487,6 +493,7 @@
 			su.phone_ phone_
 		FROM
 			music_group_payment_calender_detail mgpcd
+			LEFT JOIN music_group_payment_student_course_detail mgpsc ON mgpsc.music_group_payment_calender_id_ = mgpcd.music_group_payment_calender_id_
 			LEFT JOIN music_group_payment_calender mgpc ON mgpcd.music_group_payment_calender_id_ = mgpc.id_
 			LEFT JOIN music_group mg ON mgpc.music_group_id_ = mg.id_
 			LEFT JOIN student_registration sr ON sr.music_group_id_ = mgpc.music_group_id_ AND mgpcd.user_id_ = sr.user_id_

+ 6 - 0
mec-biz/src/main/resources/config/mybatis/StudentRegistrationMapper.xml

@@ -1244,6 +1244,12 @@
             <if test="organIds != null and organIds != ''">
                 AND FIND_IN_SET(mg.organ_id_,#{organIds})
             </if>
+            <if test="hasCourse != null and hasCourse == 'true'">
+                AND mg.first_course_start_time_ >= NOW()
+            </if>
+            <if test="hasCourse != null and hasCourse == 'false'">
+                AND (mg.first_course_start_time_ &lt; NOW() OR mg.first_course_start_time_ IS NULL)
+            </if>
             <if test="educationUserId != null">
                 AND mg.educational_teacher_id_ = #{educationUserId}
             </if>

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

@@ -0,0 +1,334 @@
+<?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 Mon Oct 31 11:58:02 CST 2022.
+    -->
+    <id column="id_" jdbcType="BIGINT" property="id" />
+    <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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 CST 2022.
+    -->
+    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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 CST 2022.
+    -->
+    insert into ws_connect_record_info (id_, room_id_, user_id_, 
+      client_type_, connect_time_, disconnect_time_, 
+      online_time_, created_time_)
+    values (#{id,jdbcType=BIGINT}, #{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 Mon Oct 31 11:58:02 CST 2022.
+    -->
+    insert into ws_connect_record_info
+    <trim prefix="(" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        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="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 Mon Oct 31 11:58:02 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 Mon Oct 31 11:58:02 CST 2022.
+    -->
+    update ws_connect_record_info
+    <set>
+      <if test="record.id != null">
+        id_ = #{record.id,jdbcType=BIGINT},
+      </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 Mon Oct 31 11:58:02 CST 2022.
+    -->
+    update ws_connect_record_info
+    set id_ = #{record.id,jdbcType=BIGINT},
+      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 Mon Oct 31 11:58:02 CST 2022.
+    -->
+    update ws_connect_record_info
+    <set>
+      <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 Mon Oct 31 11:58:02 CST 2022.
+    -->
+    update ws_connect_record_info
+    set 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());
+    }
+
+}

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

@@ -0,0 +1,235 @@
+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.DispatchMessage;
+import com.corundumstudio.socketio.store.pubsub.PubSubStore;
+import com.corundumstudio.socketio.store.pubsub.PubSubType;
+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.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_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.info("onConnect client={}, ns={}, roomId={}", client.getSessionId(), client.getNamespace().getName(), roomId);
+        //发送初始化房间事件
+        client.sendEvent(EVENT_INIT_ROOM);
+
+        if (StringUtils.isNoneBlank(roomId, userId, clientType)) {
+
+            wsConnectService.saveWsConnectRecordInfo(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.info("onDisconnect client={}, ns={}, roomId={}", client.getSessionId(), client.getNamespace().getName(), roomId);
+        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(roomId, userId, clientType);
+        }
+    }
+
+
+    /**
+     * 加入房间事件
+     * @param client SocketIOClient
+     * @param roomId 房间ID
+     */
+    @OnEvent(value = EVENT_JOIN_ROOM)
+    public void joinRoom(SocketIOClient client, String roomId) {
+        log.info("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));
+    }
+}

+ 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;
+    }
+}

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

@@ -0,0 +1,51 @@
+package com.ym.mec.web.support.socket;
+
+import com.corundumstudio.socketio.SocketIOServer;
+import com.corundumstudio.socketio.namespace.Namespace;
+import com.corundumstudio.socketio.namespace.NamespacesHub;
+import com.corundumstudio.socketio.store.pubsub.DispatchMessage;
+import com.corundumstudio.socketio.store.pubsub.PubSubStore;
+import com.corundumstudio.socketio.store.pubsub.PubSubType;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.stereotype.Component;
+
+import java.util.Objects;
+
+@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


+ 28 - 1
pom.xml

@@ -19,6 +19,8 @@
 		<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
 		<zipkin.version>2.12.2</zipkin.version>
 		<google.zxing.version>3.4.0</google.zxing.version>
+
+		<maven.test.skip>true</maven.test.skip>
 	</properties>
 
 	<dependencyManagement>
@@ -281,7 +283,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 +296,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>