Преглед на файлове

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

zouxuan преди 3 години
родител
ревизия
eea58c02f9
променени са 21 файла, в които са добавени 1446 реда и са изтрити 19 реда
  1. 14 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/dao/StudentBasicInfoDao.java
  2. 40 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/dao/StudentStatisticsDao.java
  3. 16 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/dto/StudentStatisticsDto.java
  4. 160 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/dto/StudentStatisticsSumDto.java
  5. 147 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/entity/StudentBasicInfo.java
  6. 281 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/entity/StudentStatistics.java
  7. 46 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/entity/StudentVisit.java
  8. 2 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/enums/OrderTypeEnum.java
  9. 154 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/page/StudentStatisticsQueryInfo.java
  10. 18 0
      mec-biz/src/main/java/com/ym/mec/biz/service/StudentStatisticsService.java
  11. 5 3
      mec-biz/src/main/java/com/ym/mec/biz/service/impl/ImLiveBroadcastRoomServiceImpl.java
  12. 80 0
      mec-biz/src/main/java/com/ym/mec/biz/service/impl/StudentStatisticsServiceImpl.java
  13. 114 0
      mec-biz/src/main/resources/config/mybatis/StudentBasicInfoMapper.xml
  14. 269 0
      mec-biz/src/main/resources/config/mybatis/StudentStatisticsMapper.xml
  15. 5 3
      mec-biz/src/main/resources/config/mybatis/StudentVisitMapper.xml
  16. 6 0
      mec-client-api/src/main/java/com/ym/mec/task/TaskRemoteService.java
  17. 5 0
      mec-client-api/src/main/java/com/ym/mec/task/fallback/TaskRemoteServiceFallback.java
  18. 17 13
      mec-im/src/main/java/com/ym/controller/RoomController.java
  19. 19 0
      mec-task/src/main/java/com/ym/mec/task/jobs/StudentSmallClassStatisticsTask.java
  20. 40 0
      mec-web/src/main/java/com/ym/mec/web/controller/StudentStatisticsController.java
  21. 8 0
      mec-web/src/main/java/com/ym/mec/web/controller/TaskController.java

+ 14 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/dao/StudentBasicInfoDao.java

@@ -0,0 +1,14 @@
+package com.ym.mec.biz.dal.dao;
+
+import com.ym.mec.common.dal.BaseDAO;
+import com.ym.mec.biz.dal.entity.StudentBasicInfo;
+import org.apache.ibatis.annotations.Param;
+
+public interface StudentBasicInfoDao extends BaseDAO<Integer, StudentBasicInfo> {
+
+
+    Integer getMaxId();
+
+    //更新声部班老师
+    void updateSubjectTeacher();
+}

+ 40 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/dao/StudentStatisticsDao.java

@@ -0,0 +1,40 @@
+package com.ym.mec.biz.dal.dao;
+
+import com.ym.mec.biz.dal.dto.StudentStatisticsDto;
+import com.ym.mec.biz.dal.dto.StudentStatisticsSumDto;
+import com.ym.mec.common.dal.BaseDAO;
+import com.ym.mec.biz.dal.entity.StudentStatistics;
+
+import java.util.List;
+import java.util.Map;
+
+public interface StudentStatisticsDao extends BaseDAO<Integer, StudentStatistics> {
+
+    //更新乐团主管、指导老师
+    void updateTeacherAndEdu();
+
+    //更新总课时数、已完成、剩余课时数、最近30天课耗
+    void updateCourseNum();
+
+    //更新未排课总数
+    void updateNoCourseNum();
+
+    //更新未开始价值
+    void updateNotStartCourseFee();
+
+    //更新未排课课程价值
+    void updateNoCourseFee();
+
+    //更新第一次课的时间\最近一次课时间
+    void updateFirstAndLastCourseTime();
+
+    int countStatistics(Map<String, Object> params);
+
+    List<StudentStatisticsDto> queryStatistics(Map<String, Object> params);
+
+    //更新进行中课程组数量
+    void updateNormalGroupNum();
+
+    //汇总小课数据
+    StudentStatisticsSumDto studentSmallClassStatisticsSum(String groupType);
+}

+ 16 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/dto/StudentStatisticsDto.java

@@ -0,0 +1,16 @@
+package com.ym.mec.biz.dal.dto;
+
+import com.ym.mec.biz.dal.entity.StudentBasicInfo;
+import com.ym.mec.biz.dal.entity.StudentStatistics;
+
+public class StudentStatisticsDto extends StudentStatistics {
+    private StudentBasicInfo studentBasicInfo;
+
+    public StudentBasicInfo getStudentBasicInfo() {
+        return studentBasicInfo;
+    }
+
+    public void setStudentBasicInfo(StudentBasicInfo studentBasicInfo) {
+        this.studentBasicInfo = studentBasicInfo;
+    }
+}

+ 160 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/dto/StudentStatisticsSumDto.java

@@ -0,0 +1,160 @@
+package com.ym.mec.biz.dal.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+
+public class StudentStatisticsSumDto {
+
+    @ApiModelProperty(value = "沉睡学员总数:有课耗且有【未开始】课程的学生中,近一年无课耗学生总数",required = false)
+    private Integer sleepStudentNum;
+
+    @ApiModelProperty(value = "有未排课:沉睡学员中,有未排课资格的学数量",required = false)
+    private Integer sleepStudentHasNotSchedule;
+
+    @ApiModelProperty(value = "无未排课:沉睡学员中,无未排课资格的学数量",required = false)
+    private Integer sleepStudentNoNotSchedule;
+
+    @ApiModelProperty(value = "在读学员总数:有【未开始】+【未排课】+ 近一年有课耗学生总数",required = false)
+    private Integer normalStudentNum;
+
+    @ApiModelProperty(value = "进行中:【在读学员总数】中有【进行中】状态课程组的学生数量",required = false)
+    private Integer normalStudentHasNormalGroupNum;
+
+    @ApiModelProperty(value = "暂停:有课程余额且没有剩余课时",required = false)
+    private Integer hasCourseBalanceAndNotSubCourseNum;
+
+    @ApiModelProperty(value = "未排课:【在读学员总数】中有未排课资格的学员数量",required = false)
+    private Integer normalStudentHasNoScheduleNum;
+
+    @ApiModelProperty(value = "待续费人数:【未开始】+【未排课】课时数≤3节",required = false)
+    private Integer waitRenewNum;
+
+    @ApiModelProperty(value = "未开始课时:【未开始】课程总数",required = false)
+    private Integer subCourseNum;
+
+    @ApiModelProperty(value = "未排课课时:活动资格【未排课】课程总数(1v多不去重)",required = false)
+    private Integer noScheduleNum;
+
+    @ApiModelProperty(value = "流失人数",required = false)
+    private Integer lostNum;
+
+    @ApiModelProperty(value = "新增人数:以前没课,所选时间段内新增课程或排课资格的学员",required = false)
+    private Integer addNum;
+
+    @ApiModelProperty(value = "续费人数:所选结束时间之前有多笔大于0元的付费订单(活动赠送不算)",required = false)
+    private Integer renewNum;
+
+    @ApiModelProperty(value = "回访人数",required = false)
+    private Integer visitNum;
+
+    public Integer getSleepStudentNum() {
+        return sleepStudentNum;
+    }
+
+    public void setSleepStudentNum(Integer sleepStudentNum) {
+        this.sleepStudentNum = sleepStudentNum;
+    }
+
+    public Integer getSleepStudentHasNotSchedule() {
+        return sleepStudentHasNotSchedule;
+    }
+
+    public void setSleepStudentHasNotSchedule(Integer sleepStudentHasNotSchedule) {
+        this.sleepStudentHasNotSchedule = sleepStudentHasNotSchedule;
+    }
+
+    public Integer getSleepStudentNoNotSchedule() {
+        return sleepStudentNoNotSchedule;
+    }
+
+    public void setSleepStudentNoNotSchedule(Integer sleepStudentNoNotSchedule) {
+        this.sleepStudentNoNotSchedule = sleepStudentNoNotSchedule;
+    }
+
+    public Integer getNormalStudentNum() {
+        return normalStudentNum;
+    }
+
+    public void setNormalStudentNum(Integer normalStudentNum) {
+        this.normalStudentNum = normalStudentNum;
+    }
+
+    public Integer getNormalStudentHasNormalGroupNum() {
+        return normalStudentHasNormalGroupNum;
+    }
+
+    public void setNormalStudentHasNormalGroupNum(Integer normalStudentHasNormalGroupNum) {
+        this.normalStudentHasNormalGroupNum = normalStudentHasNormalGroupNum;
+    }
+
+    public Integer getHasCourseBalanceAndNotSubCourseNum() {
+        return hasCourseBalanceAndNotSubCourseNum;
+    }
+
+    public void setHasCourseBalanceAndNotSubCourseNum(Integer hasCourseBalanceAndNotSubCourseNum) {
+        this.hasCourseBalanceAndNotSubCourseNum = hasCourseBalanceAndNotSubCourseNum;
+    }
+
+    public Integer getNormalStudentHasNoScheduleNum() {
+        return normalStudentHasNoScheduleNum;
+    }
+
+    public void setNormalStudentHasNoScheduleNum(Integer normalStudentHasNoScheduleNum) {
+        this.normalStudentHasNoScheduleNum = normalStudentHasNoScheduleNum;
+    }
+
+    public Integer getWaitRenewNum() {
+        return waitRenewNum;
+    }
+
+    public void setWaitRenewNum(Integer waitRenewNum) {
+        this.waitRenewNum = waitRenewNum;
+    }
+
+    public Integer getSubCourseNum() {
+        return subCourseNum;
+    }
+
+    public void setSubCourseNum(Integer subCourseNum) {
+        this.subCourseNum = subCourseNum;
+    }
+
+    public Integer getNoScheduleNum() {
+        return noScheduleNum;
+    }
+
+    public void setNoScheduleNum(Integer noScheduleNum) {
+        this.noScheduleNum = noScheduleNum;
+    }
+
+    public Integer getLostNum() {
+        return lostNum;
+    }
+
+    public void setLostNum(Integer lostNum) {
+        this.lostNum = lostNum;
+    }
+
+    public Integer getAddNum() {
+        return addNum;
+    }
+
+    public void setAddNum(Integer addNum) {
+        this.addNum = addNum;
+    }
+
+    public Integer getRenewNum() {
+        return renewNum;
+    }
+
+    public void setRenewNum(Integer renewNum) {
+        this.renewNum = renewNum;
+    }
+
+    public Integer getVisitNum() {
+        return visitNum;
+    }
+
+    public void setVisitNum(Integer visitNum) {
+        this.visitNum = visitNum;
+    }
+}

+ 147 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/entity/StudentBasicInfo.java

@@ -0,0 +1,147 @@
+package com.ym.mec.biz.dal.entity;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+/**
+ * 对应数据库表(student_basic_info):
+ */
+public class StudentBasicInfo {
+
+	/** 学员编号 */
+	private Integer userId;
+	
+	/** 学员姓名 */
+	private String userName;
+	
+	/** 手机号 */
+	private String phone;
+	
+	/** 声部编号 */
+	private Integer subjectId;
+	
+	/** 声部 */
+	private String subjectName;
+	
+	/** 年级 */
+	private String grade;
+	
+	/** 分部编号 */
+	private Integer organId;
+	
+	/** 分部名称 */
+	private String organName;
+	
+	/** 学校编号 */
+	private Integer cooperationOrganId;
+	
+	/** 学校名称 */
+	private String cooperationOrganName;
+	
+	/** 声部课老师(学员所在乐团声部班老师,如果有多个,取id最大的) */
+	private Integer subjectTeacherId;
+	
+	/** 声部课老师(学员所在乐团声部班老师) */
+	private String subjectTeacherName;
+	
+	public void setUserId(Integer userId){
+		this.userId = userId;
+	}
+	
+	public Integer getUserId(){
+		return this.userId;
+	}
+			
+	public void setUserName(String userName){
+		this.userName = userName;
+	}
+	
+	public String getUserName(){
+		return this.userName;
+	}
+			
+	public void setPhone(String phone){
+		this.phone = phone;
+	}
+	
+	public String getPhone(){
+		return this.phone;
+	}
+			
+	public void setSubjectId(Integer subjectId){
+		this.subjectId = subjectId;
+	}
+	
+	public Integer getSubjectId(){
+		return this.subjectId;
+	}
+			
+	public void setSubjectName(String subjectName){
+		this.subjectName = subjectName;
+	}
+	
+	public String getSubjectName(){
+		return this.subjectName;
+	}
+			
+	public void setGrade(String grade){
+		this.grade = grade;
+	}
+	
+	public String getGrade(){
+		return this.grade;
+	}
+			
+	public void setOrganId(Integer organId){
+		this.organId = organId;
+	}
+	
+	public Integer getOrganId(){
+		return this.organId;
+	}
+			
+	public void setOrganName(String organName){
+		this.organName = organName;
+	}
+	
+	public String getOrganName(){
+		return this.organName;
+	}
+			
+	public void setCooperationOrganId(Integer cooperationOrganId){
+		this.cooperationOrganId = cooperationOrganId;
+	}
+	
+	public Integer getCooperationOrganId(){
+		return this.cooperationOrganId;
+	}
+			
+	public void setCooperationOrganName(String cooperationOrganName){
+		this.cooperationOrganName = cooperationOrganName;
+	}
+	
+	public String getCooperationOrganName(){
+		return this.cooperationOrganName;
+	}
+			
+	public void setSubjectTeacherId(Integer subjectTeacherId){
+		this.subjectTeacherId = subjectTeacherId;
+	}
+	
+	public Integer getSubjectTeacherId(){
+		return this.subjectTeacherId;
+	}
+			
+	public void setSubjectTeacherName(String subjectTeacherName){
+		this.subjectTeacherName = subjectTeacherName;
+	}
+	
+	public String getSubjectTeacherName(){
+		return this.subjectTeacherName;
+	}
+			
+	@Override
+	public String toString() {
+		return ToStringBuilder.reflectionToString(this);
+	}
+
+}

+ 281 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/entity/StudentStatistics.java

@@ -0,0 +1,281 @@
+package com.ym.mec.biz.dal.entity;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+import java.math.BigDecimal;
+
+/**
+ * 对应数据库表(student_statistics):
+ */
+public class StudentStatistics {
+
+	/** 主键 */
+	private Integer id;
+	
+	/** 学员编号 */
+	private Integer userId;
+	
+	/** 指导老师编号(最近一节已结束课程上的老师) */
+	private Integer teacherId;
+	
+	/** 指导老师(最近一节已结束课程上的老师) */
+	private String teacherName;
+	
+	/** 乐团主管(最近一节已结束课程的课程组老师) */
+	private Integer musicDirectorId;
+	
+	/** 乐团主管(最近一节已结束课程的课程组老师) */
+	private String musicDirectorName;
+	
+	/** 总课时(课时总数) */
+	private Integer totalCourseNum;
+	
+	/** 已完成课时(已结束课时数) */
+	private Integer overCourseNum;
+	
+	/** 剩余课时(未开始课时数) */
+	private Integer subCourseNum;
+	
+	/** 未排课时(未排课时总数) */
+	private Integer noScheduleNum;
+	
+	/** 第一次课时间 */
+	private String firstCourseTime;
+	
+	/** 最近一次上课时间 */
+	private String lastCourseTime;
+	
+	/** 最近30天课耗(已结束的课程数量) */
+	private Integer latelyCourseConsumer;
+
+	/** 最近1年课耗(已结束的课程数量) */
+	private Integer latelyYearCourseConsumer;
+	
+	/** 回访次数 */
+	private Integer visitNum;
+	
+	/** 最近一次回访状态 */
+	private String lastVisitStatus;
+	
+	/** 最近一次回访原因 */
+	private String visitReason;
+	
+	/** 最近一次回访时间 */
+	private String lastVisitTime;
+	
+	/** 预收款(未开始课程价值总和) */
+	private java.math.BigDecimal notStartCourseFee;
+
+	/** 预收款(未排课 价值总和) */
+	private java.math.BigDecimal noCourseFee;
+	
+	/** 首次付费订单时间 */
+	private String firstOrderTime;
+	
+	/** 最近一次付费订单时间 */
+	private String lastOrderTime;
+	
+	/** 付费订单总数 */
+	private Integer orderNum;
+	
+	/** 课程组类型(VIP乐理网管) */
+	private String groupType;
+
+	public Integer getLatelyYearCourseConsumer() {
+		return latelyYearCourseConsumer;
+	}
+
+	public void setLatelyYearCourseConsumer(Integer latelyYearCourseConsumer) {
+		this.latelyYearCourseConsumer = latelyYearCourseConsumer;
+	}
+
+	public void setId(Integer id){
+		this.id = id;
+	}
+	
+	public Integer getId(){
+		return this.id;
+	}
+			
+	public void setUserId(Integer userId){
+		this.userId = userId;
+	}
+	
+	public Integer getUserId(){
+		return this.userId;
+	}
+			
+	public void setTeacherId(Integer teacherId){
+		this.teacherId = teacherId;
+	}
+	
+	public Integer getTeacherId(){
+		return this.teacherId;
+	}
+			
+	public void setTeacherName(String teacherName){
+		this.teacherName = teacherName;
+	}
+	
+	public String getTeacherName(){
+		return this.teacherName;
+	}
+			
+	public void setMusicDirectorId(Integer musicDirectorId){
+		this.musicDirectorId = musicDirectorId;
+	}
+	
+	public Integer getMusicDirectorId(){
+		return this.musicDirectorId;
+	}
+			
+	public void setMusicDirectorName(String musicDirectorName){
+		this.musicDirectorName = musicDirectorName;
+	}
+	
+	public String getMusicDirectorName(){
+		return this.musicDirectorName;
+	}
+			
+	public void setTotalCourseNum(Integer totalCourseNum){
+		this.totalCourseNum = totalCourseNum;
+	}
+	
+	public Integer getTotalCourseNum(){
+		return this.totalCourseNum;
+	}
+			
+	public void setOverCourseNum(Integer overCourseNum){
+		this.overCourseNum = overCourseNum;
+	}
+	
+	public Integer getOverCourseNum(){
+		return this.overCourseNum;
+	}
+			
+	public void setSubCourseNum(Integer subCourseNum){
+		this.subCourseNum = subCourseNum;
+	}
+	
+	public Integer getSubCourseNum(){
+		return this.subCourseNum;
+	}
+			
+	public void setNoScheduleNum(Integer noScheduleNum){
+		this.noScheduleNum = noScheduleNum;
+	}
+	
+	public Integer getNoScheduleNum(){
+		return this.noScheduleNum;
+	}
+			
+	public void setFirstCourseTime(String firstCourseTime){
+		this.firstCourseTime = firstCourseTime;
+	}
+	
+	public String getFirstCourseTime(){
+		return this.firstCourseTime;
+	}
+			
+	public void setLastCourseTime(String lastCourseTime){
+		this.lastCourseTime = lastCourseTime;
+	}
+	
+	public String getLastCourseTime(){
+		return this.lastCourseTime;
+	}
+			
+	public void setLatelyCourseConsumer(Integer latelyCourseConsumer){
+		this.latelyCourseConsumer = latelyCourseConsumer;
+	}
+	
+	public Integer getLatelyCourseConsumer(){
+		return this.latelyCourseConsumer;
+	}
+			
+	public void setVisitNum(Integer visitNum){
+		this.visitNum = visitNum;
+	}
+	
+	public Integer getVisitNum(){
+		return this.visitNum;
+	}
+			
+	public void setLastVisitStatus(String lastVisitStatus){
+		this.lastVisitStatus = lastVisitStatus;
+	}
+	
+	public String getLastVisitStatus(){
+		return this.lastVisitStatus;
+	}
+			
+	public void setVisitReason(String visitReason){
+		this.visitReason = visitReason;
+	}
+	
+	public String getVisitReason(){
+		return this.visitReason;
+	}
+			
+	public void setLastVisitTime(String lastVisitTime){
+		this.lastVisitTime = lastVisitTime;
+	}
+	
+	public String getLastVisitTime(){
+		return this.lastVisitTime;
+	}
+
+	public BigDecimal getNotStartCourseFee() {
+		return notStartCourseFee;
+	}
+
+	public void setNotStartCourseFee(BigDecimal notStartCourseFee) {
+		this.notStartCourseFee = notStartCourseFee;
+	}
+
+	public BigDecimal getNoCourseFee() {
+		return noCourseFee;
+	}
+
+	public void setNoCourseFee(BigDecimal noCourseFee) {
+		this.noCourseFee = noCourseFee;
+	}
+
+	public void setFirstOrderTime(String firstOrderTime){
+		this.firstOrderTime = firstOrderTime;
+	}
+	
+	public String getFirstOrderTime(){
+		return this.firstOrderTime;
+	}
+			
+	public void setLastOrderTime(String lastOrderTime){
+		this.lastOrderTime = lastOrderTime;
+	}
+	
+	public String getLastOrderTime(){
+		return this.lastOrderTime;
+	}
+			
+	public void setOrderNum(Integer orderNum){
+		this.orderNum = orderNum;
+	}
+	
+	public Integer getOrderNum(){
+		return this.orderNum;
+	}
+			
+	public void setGroupType(String groupType){
+		this.groupType = groupType;
+	}
+	
+	public String getGroupType(){
+		return this.groupType;
+	}
+			
+	@Override
+	public String toString() {
+		return ToStringBuilder.reflectionToString(this);
+	}
+
+}

+ 46 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/entity/StudentVisit.java

@@ -43,6 +43,38 @@ public class StudentVisit extends BaseEntity {
         }
     }
 
+    public enum FeedbackTypeEnum implements BaseEnum<String,FeedbackTypeEnum> {
+        THINKING("THINKING", "考虑中"), PENDING_PAYMENT("PENDING_PAYMENT", "确认缴费待缴费"),
+        LOST("LOST", "流失"), PAUSE("PAUSE", "暂停"),
+        OTHER("OTHER", "其他");
+
+        private String code;
+
+        private String msg;
+
+        FeedbackTypeEnum(String code, String msg) {
+            this.code = code;
+            this.msg = msg;
+        }
+
+        public void setCode(String code) {
+            this.code = code;
+        }
+
+        public String getMsg() {
+            return msg;
+        }
+
+        public void setMsg(String msg) {
+            this.msg = msg;
+        }
+
+        @Override
+        public String getCode() {
+            return this.code;
+        }
+    }
+
     private Integer id;
 
     /**
@@ -97,6 +129,12 @@ public class StudentVisit extends BaseEntity {
     private String overview;
 
     /**
+     * 家长反馈类型
+     */
+    @ApiModelProperty(value = "家长反馈", required = true)
+    private FeedbackTypeEnum feedbackType;
+
+    /**
      * 家长反馈
      */
     @ApiModelProperty(value = "家长反馈", required = true)
@@ -116,6 +154,14 @@ public class StudentVisit extends BaseEntity {
 
     private Long objectId;
 
+    public FeedbackTypeEnum getFeedbackType() {
+        return feedbackType;
+    }
+
+    public void setFeedbackType(FeedbackTypeEnum feedbackType) {
+        this.feedbackType = feedbackType;
+    }
+
     public Integer getId() {
         return id;
     }

+ 2 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/enums/OrderTypeEnum.java

@@ -15,6 +15,7 @@ public enum OrderTypeEnum implements BaseEnum<String, OrderTypeEnum> {
     PRACTICE_GROUP_BUY("PRACTICE_GROUP_BUY", "网管课报名"),
     PRACTICE_GROUP_RENEW("PRACTICE_GROUP_RENEW", "网管课续费"),
     COURSE_GROUP_BUY("COURSE_GROUP_BUY", "课程购买"),
+    //余额充值活动
     LUCK("LUCK", "福袋活动"),
     DOUBLE_ELEVEN2020("DOUBLE_ELEVEN2020", "2020双十一活动"),
     DOUBLE_ELEVEN2021("DOUBLE_ELEVEN2021", "2021双十一活动"),
@@ -23,6 +24,7 @@ public enum OrderTypeEnum implements BaseEnum<String, OrderTypeEnum> {
     OUTORDER("OUTORDER", "导入订单"),
     REPAIR("REPAIR", "乐器维修"),
     SUBJECT_CHANGE("SUBJECT_CHANGE", "声部更换"),
+    //包含考级费用和乐理课费用,乐理课的费用在detail里面,对于的订单类型是 DEGREE_REGISTRATION
     DEGREE_REGISTRATION("DEGREE_REGISTRATION", "考级报名"),
     MAINTENANCE("MAINTENANCE", "乐器保养"),
     REPLACEMENT("REPLACEMENT", "乐器置换"),

+ 154 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/page/StudentStatisticsQueryInfo.java

@@ -0,0 +1,154 @@
+package com.ym.mec.biz.dal.page;
+
+import com.ym.mec.common.page.QueryInfo;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+* @author zx
+* @date 2021/9/26 15:52
+*/
+public class StudentStatisticsQueryInfo extends QueryInfo {
+
+    @ApiModelProperty(value = "小课类型 VIP、PRACTICE、THEORY 默认VIP",required = true)
+    private String groupType = "VIP";
+
+    @ApiModelProperty(value = "声部编号",required = false)
+    private Integer subjectId;
+
+    @ApiModelProperty(value = "学员状态,在读(NORMAL)、沉睡(SLEEPY)",required = false)
+    private String studentStatus;
+
+    @ApiModelProperty(value = "回访状态,THINKING(考虑中), PENDING_PAYMENT(确认缴费待缴费),LOST(流失), PAUSE(暂停),OTHER(其他)",required = false)
+    private String feedbackType;
+
+    @ApiModelProperty(value = "30天课耗",required = false)
+    private Integer latelyCourseConsumer;
+
+    @ApiModelProperty(value = "一年课耗",required = false)
+    private Integer latelyYearCourseConsumer;
+
+    @ApiModelProperty(value = "回访次数",required = false)
+    private Integer visitNum;
+
+    @ApiModelProperty(value = "指导老师",required = false)
+    private Integer teacherId;
+
+    @ApiModelProperty(value = "声部课老师",required = false)
+    private Integer subjectTeacherId;
+
+    @ApiModelProperty(value = "乐团主管",required = false)
+    private Integer musicDirectorId;
+
+    @ApiModelProperty(value = "是否查询课耗异常",required = false)
+    private boolean courseConsumerError = false;
+
+    @ApiModelProperty(value = "第一次课开始时间(年月日)",required = false)
+    private String firstCourseStartTime;
+
+    @ApiModelProperty(value = "第一次课截止时间(年月日)",required = false)
+    private String firstCourseEndTime;
+
+    public String getGroupType() {
+        return groupType;
+    }
+
+    public void setGroupType(String groupType) {
+        this.groupType = groupType;
+    }
+
+    public Integer getSubjectId() {
+        return subjectId;
+    }
+
+    public void setSubjectId(Integer subjectId) {
+        this.subjectId = subjectId;
+    }
+
+    public String getStudentStatus() {
+        return studentStatus;
+    }
+
+    public void setStudentStatus(String studentStatus) {
+        this.studentStatus = studentStatus;
+    }
+
+    public String getFeedbackType() {
+        return feedbackType;
+    }
+
+    public void setFeedbackType(String feedbackType) {
+        this.feedbackType = feedbackType;
+    }
+
+    public Integer getLatelyCourseConsumer() {
+        return latelyCourseConsumer;
+    }
+
+    public void setLatelyCourseConsumer(Integer latelyCourseConsumer) {
+        this.latelyCourseConsumer = latelyCourseConsumer;
+    }
+
+    public Integer getLatelyYearCourseConsumer() {
+        return latelyYearCourseConsumer;
+    }
+
+    public void setLatelyYearCourseConsumer(Integer latelyYearCourseConsumer) {
+        this.latelyYearCourseConsumer = latelyYearCourseConsumer;
+    }
+
+    public Integer getVisitNum() {
+        return visitNum;
+    }
+
+    public void setVisitNum(Integer visitNum) {
+        this.visitNum = visitNum;
+    }
+
+    public Integer getTeacherId() {
+        return teacherId;
+    }
+
+    public void setTeacherId(Integer teacherId) {
+        this.teacherId = teacherId;
+    }
+
+    public Integer getSubjectTeacherId() {
+        return subjectTeacherId;
+    }
+
+    public void setSubjectTeacherId(Integer subjectTeacherId) {
+        this.subjectTeacherId = subjectTeacherId;
+    }
+
+    public Integer getMusicDirectorId() {
+        return musicDirectorId;
+    }
+
+    public void setMusicDirectorId(Integer musicDirectorId) {
+        this.musicDirectorId = musicDirectorId;
+    }
+
+    public boolean isCourseConsumerError() {
+        return courseConsumerError;
+    }
+
+    public void setCourseConsumerError(boolean courseConsumerError) {
+        this.courseConsumerError = courseConsumerError;
+    }
+
+    public String getFirstCourseStartTime() {
+        return firstCourseStartTime;
+    }
+
+    public void setFirstCourseStartTime(String firstCourseStartTime) {
+        this.firstCourseStartTime = firstCourseStartTime;
+    }
+
+    public String getFirstCourseEndTime() {
+        return firstCourseEndTime;
+    }
+
+    public void setFirstCourseEndTime(String firstCourseEndTime) {
+        this.firstCourseEndTime = firstCourseEndTime;
+    }
+}

+ 18 - 0
mec-biz/src/main/java/com/ym/mec/biz/service/StudentStatisticsService.java

@@ -0,0 +1,18 @@
+package com.ym.mec.biz.service;
+
+import com.ym.mec.biz.dal.dto.StudentStatisticsDto;
+import com.ym.mec.biz.dal.dto.StudentStatisticsSumDto;
+import com.ym.mec.biz.dal.entity.StudentStatistics;
+import com.ym.mec.biz.dal.page.StudentStatisticsQueryInfo;
+import com.ym.mec.common.page.PageInfo;
+import com.ym.mec.common.page.QueryInfo;
+import com.ym.mec.common.service.BaseService;
+
+public interface StudentStatisticsService extends BaseService<Integer, StudentStatistics> {
+
+    void updateStudentStatistics();
+
+    PageInfo<StudentStatisticsDto> queryStatisticsPage(StudentStatisticsQueryInfo queryInfo);
+
+    StudentStatisticsSumDto studentSmallClassStatisticsSum(String groupType);
+}

+ 5 - 3
mec-biz/src/main/java/com/ym/mec/biz/service/impl/ImLiveBroadcastRoomServiceImpl.java

@@ -83,6 +83,8 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
     public static final String LIVE_USER_ROOM = "IM:LIVE_ROOM_USER:" + USER_ID;
     //房间点赞数
     public static final String LIVE_ROOM_LIKE = "IM:LIVE_ROOM_LIKE:" + ROOM_UID;
+    //直播提前开始时间
+    public static final int PRE_LIVE_TIME_MINUTE = 30;
 
     /**
      * 进入直播间检查数据
@@ -111,8 +113,8 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
         optional.filter(r -> r.getRoomState() != 1).orElseThrow(() -> new BizException("直播间不存在"));
         ImLiveBroadcastRoomVo room = optional.get();
         if (room.getLiveState() == 0) {
-            throw new BizException("直播未开始,直播开启的时间是 "
-                    + DateUtil.format(room.getLiveStartTime(), DateUtil.EXPANDED_DATE_TIME_FORMAT));
+            Date liveStartTime = DateUtil.addMinutes(room.getLiveStartTime(), -PRE_LIVE_TIME_MINUTE);
+            throw new BizException(DateUtil.format(liveStartTime, "yyyy年MM月dd日 HH点mm分") + " 可进入直播间准备");
         }
         if (room.getLiveState() == 2) {
             throw new BizException("直播已结束!");
@@ -630,7 +632,7 @@ public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastR
      */
     public void createLiveRoom() {
         Date now = new Date();
-        Date endTime = DateUtil.addMinutes(now, 30);
+        Date endTime = DateUtil.addMinutes(now, PRE_LIVE_TIME_MINUTE);
         List<ImLiveBroadcastRoom> list = this.list(new WrapperUtil<ImLiveBroadcastRoom>()
                 .hasEq("live_state_", 0)
                 .hasEq("room_state_", 0)

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

@@ -0,0 +1,80 @@
+package com.ym.mec.biz.service.impl;
+
+import com.ym.mec.biz.dal.dao.StudentBasicInfoDao;
+import com.ym.mec.biz.dal.dao.StudentStatisticsDao;
+import com.ym.mec.biz.dal.dto.StudentStatisticsDto;
+import com.ym.mec.biz.dal.dto.StudentStatisticsSumDto;
+import com.ym.mec.biz.dal.entity.StudentStatistics;
+import com.ym.mec.biz.dal.page.StudentStatisticsQueryInfo;
+import com.ym.mec.biz.service.StudentStatisticsService;
+import com.ym.mec.common.dal.BaseDAO;
+import com.ym.mec.common.page.PageInfo;
+import com.ym.mec.common.page.QueryInfo;
+import com.ym.mec.common.service.impl.BaseServiceImpl;
+import com.ym.mec.util.collection.MapUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class StudentStatisticsServiceImpl extends BaseServiceImpl<Integer, StudentStatistics>  implements StudentStatisticsService {
+	
+	@Autowired
+	private StudentStatisticsDao studentStatisticsDao;
+	@Autowired
+	private StudentBasicInfoDao studentBasicInfoDao;
+
+	@Override
+	public BaseDAO<Integer, StudentStatistics> getDAO() {
+		return studentStatisticsDao;
+	}
+
+	@Override
+	public void updateStudentStatistics() {
+		//更新声部班老师
+		studentBasicInfoDao.updateSubjectTeacher();
+		//更新乐团主管、指导老师
+		studentStatisticsDao.updateTeacherAndEdu();
+		//更新总课时数、已完成、剩余课时数、最近30天课耗、最近1年课耗
+		studentStatisticsDao.updateCourseNum();
+		//更新未排课总数
+		studentStatisticsDao.updateNoCourseNum();
+		//更新未开始价值总和
+		studentStatisticsDao.updateNotStartCourseFee();
+		//更新未排课价值
+		studentStatisticsDao.updateNoCourseFee();
+		//更新第一次课的时间\最近一次课时间
+		studentStatisticsDao.updateFirstAndLastCourseTime();
+		//更新进行中课程组数量
+		studentStatisticsDao.updateNormalGroupNum();
+	}
+
+	@Override
+	public PageInfo<StudentStatisticsDto> queryStatisticsPage(StudentStatisticsQueryInfo queryInfo) {
+		PageInfo<StudentStatisticsDto> pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
+		Map<String, Object> params = new HashMap<String, Object>();
+		MapUtil.populateMap(params, queryInfo);
+
+		List<StudentStatisticsDto> dataList = null;
+		int count = studentStatisticsDao.countStatistics(params);
+		if (count > 0) {
+			pageInfo.setTotal(count);
+			params.put("offset", pageInfo.getOffset());
+			dataList = studentStatisticsDao.queryStatistics(params);
+		}
+		if (count == 0) {
+			dataList = new ArrayList<>();
+		}
+		pageInfo.setRows(dataList);
+		return pageInfo;
+	}
+
+    @Override
+    public StudentStatisticsSumDto studentSmallClassStatisticsSum(String groupType) {
+		return studentStatisticsDao.studentSmallClassStatisticsSum(groupType);
+    }
+}

+ 114 - 0
mec-biz/src/main/resources/config/mybatis/StudentBasicInfoMapper.xml

@@ -0,0 +1,114 @@
+<?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.StudentBasicInfoDao">
+	
+	<resultMap type="com.ym.mec.biz.dal.entity.StudentBasicInfo" id="StudentBasicInfo">
+		<result column="user_id_" property="userId" />
+		<result column="user_name_" property="userName" />
+		<result column="phone_" property="phone" />
+		<result column="subject_id_" property="subjectId" />
+		<result column="subject_name_" property="subjectName" />
+		<result column="grade_" property="grade" />
+		<result column="organ_id_" property="organId" />
+		<result column="organ_name_" property="organName" />
+		<result column="cooperation_organ_id_" property="cooperationOrganId" />
+		<result column="cooperation_organ_name_" property="cooperationOrganName" />
+		<result column="subject_teacher_id_" property="subjectTeacherId" />
+		<result column="subject_teacher_name_" property="subjectTeacherName" />
+	</resultMap>
+	
+	<!-- 根据主键查询一条记录 -->
+	<select id="get" resultMap="StudentBasicInfo" >
+		SELECT * FROM student_basic_info WHERE user_id_ = #{userId} 
+	</select>
+	
+	<!-- 全查询 -->
+	<select id="findAll" resultMap="StudentBasicInfo">
+		SELECT * FROM student_basic_info ORDER BY user_id_
+	</select>
+	
+	<!-- 向数据库增加一条记录 -->
+	<insert id="insert" parameterType="com.ym.mec.biz.dal.entity.StudentBasicInfo" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
+		<!--
+		<selectKey resultClass="int" keyProperty="id" > 
+		SELECT SEQ_WSDEFINITION_ID.nextval AS ID FROM DUAL 
+		</selectKey>
+		-->
+		INSERT INTO student_basic_info (user_id_,user_name_,phone_,subject_id_,subject_name_,grade_,organ_id_,organ_name_,cooperation_organ_id_,cooperation_organ_name_,subject_teacher_id_,subject_teacher_name_) VALUES(#{userId},#{userName},#{phone},#{subjectId},#{subjectName},#{grade},#{organId},#{organName},#{cooperationOrganId},#{cooperationOrganName},#{subjectTeacherId},#{subjectTeacherName})
+	</insert>
+	
+	<!-- 根据主键查询一条记录 -->
+	<update id="update" parameterType="com.ym.mec.biz.dal.entity.StudentBasicInfo">
+		UPDATE student_basic_info <set>
+			<if test="subjectId != null">
+			subject_id_ = #{subjectId},
+			</if>
+			<if test="subjectName != null">
+			subject_name_ = #{subjectName},
+			</if>
+			<if test="userId != null">
+			user_id_ = #{userId},
+			</if>
+			<if test="organId != null">
+			organ_id_ = #{organId},
+			</if>
+			<if test="subjectTeacherId != null">
+			subject_teacher_id_ = #{subjectTeacherId},
+			</if>
+			<if test="userName != null">
+			user_name_ = #{userName},
+			</if>
+			<if test="cooperationOrganId != null">
+			cooperation_organ_id_ = #{cooperationOrganId},
+			</if>
+			<if test="organName != null">
+			organ_name_ = #{organName},
+			</if>
+			<if test="phone != null">
+			phone_ = #{phone},
+			</if>
+			<if test="cooperationOrganName != null">
+			cooperation_organ_name_ = #{cooperationOrganName},
+			</if>
+			<if test="grade != null">
+			grade_ = #{grade},
+			</if>
+			<if test="subjectTeacherName != null">
+			subject_teacher_name_ = #{subjectTeacherName},
+			</if>
+		</set> WHERE user_id_ = #{userId}
+	</update>
+	<update id="updateSubjectTeacher">
+		UPDATE (
+		SELECT cgsm.user_id_,cgtm.user_id_ teacher_id_ FROM class_group cg
+		LEFT JOIN class_group_student_mapper cgsm ON cgsm.class_group_id_ = cg.id_
+		LEFT JOIN class_group_teacher_mapper cgtm ON cgtm.class_group_id_ = cg.id_
+		WHERE cg.type_ = 'NORMAL' AND cgsm.status_ = 'NORMAL' AND cg.del_flag_ = 0 AND cgtm.teacher_role_ = 'BISHOP'
+		GROUP BY cgsm.user_id_) st
+		LEFT JOIN student_basic_info sbi ON st.user_id_ = sbi.user_id_
+		LEFT JOIN sys_user su ON su.id_ = st.user_id_
+		SET sbi.subject_teacher_id_ = st.teacher_id_,sbi.subject_teacher_name_ = su.real_name_
+	</update>
+
+	<!-- 根据主键删除一条记录 -->
+	<delete id="delete" >
+		DELETE FROM student_basic_info WHERE user_id_ = #{userId} 
+	</delete>
+	
+	<!-- 分页查询 -->
+	<select id="queryPage" resultMap="StudentBasicInfo" parameterType="map">
+		SELECT * FROM student_basic_info ORDER BY user_id_ <include refid="global.limit"/>
+	</select>
+	
+	<!-- 查询当前表的总记录数 -->
+	<select id="queryCount" resultType="int">
+		SELECT COUNT(*) FROM student_basic_info
+	</select>
+    <select id="getMaxId" resultType="java.lang.Integer">
+		SELECT MAX(id_) FROM student_basic_info
+	</select>
+</mapper>

+ 269 - 0
mec-biz/src/main/resources/config/mybatis/StudentStatisticsMapper.xml

@@ -0,0 +1,269 @@
+<?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.StudentStatisticsDao">
+	
+	<resultMap type="com.ym.mec.biz.dal.entity.StudentStatistics" id="StudentStatistics">
+		<result column="id_" property="id" />
+		<result column="user_id_" property="userId" />
+		<result column="teacher_id_" property="teacherId" />
+		<result column="teacher_name_" property="teacherName" />
+		<result column="music_director_id_" property="musicDirectorId" />
+		<result column="music_director_name_" property="musicDirectorName" />
+		<result column="total_course_num_" property="totalCourseNum" />
+		<result column="over_course_num_" property="overCourseNum" />
+		<result column="sub_course_num_" property="subCourseNum" />
+		<result column="no_schedule_num_" property="noScheduleNum" />
+		<result column="first_course_time_" property="firstCourseTime" />
+		<result column="last_course_time_" property="lastCourseTime" />
+		<result column="lately_course_consumer_" property="latelyCourseConsumer" />
+		<result column="lately_year_course_consumer_" property="latelyYearCourseConsumer" />
+		<result column="visit_num_" property="visitNum" />
+		<result column="last_visit_status_" property="lastVisitStatus" />
+		<result column="visit_reason_" property="visitReason" />
+		<result column="last_visit_time_" property="lastVisitTime" />
+		<result column="not_start_course_fee_" property="notStartCourseFee" />
+		<result column="no_course_fee_" property="noCourseFee" />
+		<result column="first_order_time_" property="firstOrderTime" />
+		<result column="last_order_time_" property="lastOrderTime" />
+		<result column="order_num_" property="orderNum" />
+		<result column="group_type_" property="groupType" />
+	</resultMap>
+	
+	<!-- 根据主键查询一条记录 -->
+	<select id="get" resultMap="StudentStatistics" >
+		SELECT * FROM student_statistics WHERE id_ = #{id} 
+	</select>
+	
+	<!-- 全查询 -->
+	<select id="findAll" resultMap="StudentStatistics">
+		SELECT * FROM student_statistics ORDER BY id_
+	</select>
+
+	<!-- 向数据库增加一条记录 -->
+	<insert id="insert" parameterType="com.ym.mec.biz.dal.entity.StudentStatistics" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
+		INSERT INTO student_statistics (id_,user_id_,teacher_id_,teacher_name_,music_director_id_,
+		music_director_name_,total_course_num_,over_course_num_,sub_course_num_,
+		no_schedule_num_,first_course_time_,last_course_time_,lately_course_consumer_,
+		visit_num_,last_visit_status_,visit_reason_,last_visit_time_,not_start_course_fee_,no_course_fee_,first_order_time_,
+		last_order_time_,order_num_,group_type_)
+		VALUES(#{id},#{userId},#{teacherId},#{teacherName},#{musicDirectorId},#{musicDirectorName},
+		       #{totalCourseNum},#{overCourseNum},#{subCourseNum},#{noScheduleNum},#{firstCourseTime},#{lastCourseTime},
+		       #{latelyCourseConsumer},#{visitNum},#{lastVisitStatus},#{visitReason},#{lastVisitTime},#{notStartCourseFee},#{noCourseFee},#{firstOrderTime},
+		       #{lastOrderTime},#{orderNum},#{groupType})
+	</insert>
+
+	<!-- 根据主键查询一条记录 -->
+	<update id="update" parameterType="com.ym.mec.biz.dal.entity.StudentStatistics">
+		UPDATE student_statistics <set>
+		<if test="overCourseNum != null">
+		over_course_num_ = #{overCourseNum},
+		</if>
+		<if test="latelyCourseConsumer != null">
+		lately_course_consumer_ = #{latelyCourseConsumer},
+		</if>
+		<if test="lastCourseTime != null">
+		last_course_time_ = #{lastCourseTime},
+		</if>
+		<if test="totalCourseNum != null">
+		total_course_num_ = #{totalCourseNum},
+		</if>
+		<if test="musicDirectorName != null">
+		music_director_name_ = #{musicDirectorName},
+		</if>
+		<if test="visitNum != null">
+		visit_num_ = #{visitNum},
+		</if>
+		<if test="teacherName != null">
+		teacher_name_ = #{teacherName},
+		</if>
+		<if test="subCourseNum != null">
+		sub_course_num_ = #{subCourseNum},
+		</if>
+		<if test="lastVisitStatus != null">
+		last_visit_status_ = #{lastVisitStatus},
+		</if>
+		<if test="lastVisitTime != null">
+		last_visit_time_ = #{lastVisitTime},
+		</if>
+		<if test="teacherId != null">
+		teacher_id_ = #{teacherId},
+		</if>
+		<if test="musicDirectorId != null">
+		music_director_id_ = #{musicDirectorId},
+		</if>
+		<if test="lastOrderTime != null">
+		last_order_time_ = #{lastOrderTime},
+		</if>
+		<if test="firstOrderTime != null">
+		first_order_time_ = #{firstOrderTime},
+		</if>
+		<if test="orderNum != null">
+		order_num_ = #{orderNum},
+		</if>
+		<if test="groupType != null">
+		group_type_ = #{groupType},
+		</if>
+		<if test="visitReason != null">
+		visit_reason_ = #{visitReason},
+		</if>
+		<if test="notStartCourseFee != null">
+			not_start_course_fee_ = #{notStartCourseFee},
+		</if>
+		<if test="noCourseFee != null">
+			no_course_fee_ = #{noCourseFee},
+		</if>
+		<if test="noScheduleNum != null">
+		no_schedule_num_ = #{noScheduleNum},
+		</if>
+		<if test="firstCourseTime != null">
+		first_course_time_ = #{firstCourseTime},
+		</if>
+		</set> WHERE id_ = #{id}
+	</update>
+	<update id="updateTeacherAndEdu">
+		SELECT updateTeacherAndEdu()
+	</update>
+	<update id="updateCourseNum">
+		SELECT updateCourseNum()
+	</update>
+	<update id="updateNoCourseNum">
+		SELECT updateNoCourseNum()
+	</update>
+	<update id="updateNotStartCourseFee">
+		SELECT updateNotStartCourseFee()
+	</update>
+	<update id="updateNoCourseFee">
+		SELECT updateNoCourseFee()
+	</update>
+	<update id="updateFirstAndLastCourseTime">
+		SELECT updateFirstAndLastCourseTime()
+	</update>
+	<update id="updateNormalGroupNum">
+		SELECT updateNormalGroupNum()
+	</update>
+
+	<!-- 根据主键删除一条记录 -->
+	<delete id="delete" >
+		DELETE FROM student_statistics WHERE id_ = #{id} 
+	</delete>
+	
+	<!-- 分页查询 -->
+	<select id="queryPage" resultMap="StudentStatistics" parameterType="map">
+		SELECT * FROM student_statistics ORDER BY id_ <include refid="global.limit"/>
+	</select>
+	
+	<!-- 查询当前表的总记录数 -->
+	<select id="queryCount" resultType="int">
+		SELECT COUNT(*) FROM student_statistics
+	</select>
+	<resultMap id="StudentStatisticsDto" type="com.ym.mec.biz.dal.dto.StudentStatisticsDto" extends="StudentStatistics">
+		<association property="studentBasicInfo" javaType="com.ym.mec.biz.dal.entity.StudentBasicInfo">
+			<result column="user_id_" property="userId" />
+			<result column="user_name_" property="userName" />
+			<result column="phone_" property="phone" />
+			<result column="subject_id_" property="subjectId" />
+			<result column="subject_name_" property="subjectName" />
+			<result column="grade_" property="grade" />
+			<result column="organ_id_" property="organId" />
+			<result column="organ_name_" property="organName" />
+			<result column="cooperation_organ_id_" property="cooperationOrganId" />
+			<result column="cooperation_organ_name_" property="cooperationOrganName" />
+			<result column="subject_teacher_id_" property="subjectTeacherId" />
+			<result column="subject_teacher_name_" property="subjectTeacherName" />
+		</association>
+	</resultMap>
+	<sql id="queryStatisticsSql">
+		<where>
+			<if test="search != null and search != ''">
+				AND (sbi.user_id_ = #{search} OR sbi.user_name_ LIKE CONCAT('%',#{search},'%'))
+			</if>
+			<if test="subjectId != null">
+				AND sbi.subject_id_ = #{subjectId}
+			</if>
+			<if test="studentStatus != null and studentStatus != ''">
+				<if test="studentStatus == 'NORMAL'">
+					AND ss.over_course_num_ > 0 AND ss.sub_course_num_ > 0 AND ss.lately_year_course_consumer_ > 0
+				</if>
+				<if test="studentStatus == 'SLEEPY'">
+					AND ss.over_course_num_ > 0 AND ss.sub_course_num_ > 0 AND ss.lately_year_course_consumer_ &lt;= 0
+				</if>
+			</if>
+			<if test="feedbackType != null and feedbackType != ''">
+				AND ss.last_visit_status_ = #{feedbackType}
+			</if>
+			<if test="latelyCourseConsumer != null">
+				AND ss.lately_course_consumer_ = #{latelyCourseConsumer}
+			</if>
+			<if test="latelyYearCourseConsumer != null">
+				AND ss.lately_year_course_consumer_ = #{latelyYearCourseConsumer}
+			</if>
+			<if test="visitNum != null">
+				AND ss.visit_num_ = #{visitNum}
+			</if>
+			<if test="teacherId != null">
+				AND ss.teacher_id_ = #{teacherId}
+			</if>
+			<if test="subjectTeacherId != null">
+				AND sbi.subject_teacher_id_ = #{subjectTeacherId}
+			</if>
+			<if test="musicDirectorId != null">
+				AND ss.music_director_id_ = #{musicDirectorId}
+			</if>
+			<if test="courseConsumerError != null and courseConsumerError == 'true'">
+				AND ss.lately_course_consumer_ &lt; 4
+			</if>
+			<if test="firstCourseStartTime != null">
+				AND ss.first_course_time_ >= #{firstCourseStartTime}
+			</if>
+			<if test="firstCourseEndTime != null">
+				AND ss.first_course_time_ &lt;= #{firstCourseEndTime}
+			</if>
+			<if test="groupType != null and groupType != ''">
+				AND ss.group_type_ = #{groupType}
+			</if>
+		</where>
+	</sql>
+	<select id="countStatistics" resultType="java.lang.Integer">
+		SELECT COUNT(sbi.id_) FROM student_basic_info sbi
+		LEFT JOIN student_statistics ss ON ss.user_id_ = sbi.user_id_
+		<include refid="queryStatisticsSql"/>
+	</select>
+	<select id="queryStatistics" resultMap="StudentStatisticsDto">
+		SELECT * FROM student_basic_info sbi
+		LEFT JOIN student_statistics ss ON ss.user_id_ = sbi.user_id_
+		<include refid="queryStatisticsSql"/>
+		<include refid="global.limit"/>
+	</select>
+	<resultMap id="StudentStatisticsSumDto" type="com.ym.mec.biz.dal.dto.StudentStatisticsSumDto">
+		<result property="sleepStudentNum" column="sleepStudentNum"/>
+		<result property="sleepStudentHasNotSchedule" column="sleepStudentHasNotSchedule"/>
+		<result property="sleepStudentNoNotSchedule" column="sleepStudentNoNotSchedule"/>
+		<result property="normalStudentNum" column="normalStudentNum"/>
+		<result property="normalStudentHasNormalGroupNum" column="normalStudentHasNormalGroupNum"/>
+		<result property="hasCourseBalanceAndNotSubCourseNum" column="hasCourseBalanceAndNotSubCourseNum"/>
+		<result property="normalStudentHasNoScheduleNum" column="normalStudentHasNoScheduleNum"/>
+		<result property="waitRenewNum" column="waitRenewNum"/>
+		<result property="subCourseNum" column="subCourseNum"/>
+		<result property="noScheduleNum" column="noScheduleNum"/>
+	</resultMap>
+	<select id="studentSmallClassStatisticsSum" resultMap="StudentStatisticsSumDto">
+		SELECT
+		COUNT(CASE WHEN ss.over_course_num_ > 0 AND ss.sub_course_num_ > 0 AND ss.lately_year_course_consumer_ &lt;= 0 THEN 1 ELSE NULL END) 'sleepStudentNum',
+		COUNT(CASE WHEN ss.over_course_num_ > 0 AND ss.sub_course_num_ > 0 AND ss.lately_year_course_consumer_ &lt;= 0 AND ss.no_schedule_num_ > 0 THEN 1 ELSE NULL END) 'sleepStudentHasNotSchedule',
+		COUNT(CASE WHEN ss.over_course_num_ > 0 AND ss.sub_course_num_ > 0 AND ss.lately_year_course_consumer_ &lt;= 0 AND ss.no_schedule_num_ &lt;= 0 THEN 1 ELSE NULL END) 'sleepStudentNoNotSchedule',
+		COUNT(CASE WHEN ss.over_course_num_ > 0 AND ss.sub_course_num_ > 0 AND ss.lately_year_course_consumer_ > 0 THEN 1 ELSE NULL END) 'normalStudentNum',
+		COUNT(CASE WHEN ss.over_course_num_ > 0 AND ss.sub_course_num_ > 0 AND ss.lately_year_course_consumer_ > 0 AND ss.normal_group_num_ > 0 THEN 1 ELSE NULL END) 'normalStudentHasNormalGroupNum',
+		COUNT(CASE WHEN suca.course_balance_ > 0 AND ss.sub_course_num_ &lt;= 0 THEN 1 ELSE NULL END) 'hasCourseBalanceAndNotSubCourseNum',
+		COUNT(CASE WHEN ss.over_course_num_ > 0 AND ss.sub_course_num_ > 0 AND ss.lately_year_course_consumer_ > 0 AND ss.no_schedule_num_ > 0 THEN 1 ELSE NULL END) 'normalStudentHasNoScheduleNum',
+		COUNT(CASE WHEN (ss.no_schedule_num_ + ss.no_schedule_num_) &lt; 4 THEN 1 ELSE NULL END) 'waitRenewNum',
+		SUM(ss.sub_course_num_) 'subCourseNum',
+		SUM(ss.no_schedule_num_) 'noScheduleNum'
+		FROM student_statistics ss
+		LEFT JOIN sys_user_cash_account suca ON ss.user_id_ = suca.user_id_
+		WHERE ss.group_type_ = #{groupType}
+	</select>
+</mapper>

+ 5 - 3
mec-biz/src/main/resources/config/mybatis/StudentVisitMapper.xml

@@ -11,7 +11,7 @@
         <result column="studentName" property="studentName"/>
         <result column="teacher_id_" property="teacherId"/>
         <result column="teacherName" property="teacherName"/>
-        <result column="visiter_type_" property="visiterType"/>
+        <result column="visiter_type_" property="visiterType" typeHandler="com.ym.mec.common.dal.CustomEnumTypeHandler"/>
         <result column="type_" property="type"/>
         <result column="purpose_" property="purpose"/>
         <result column="overview_" property="overview"/>
@@ -20,11 +20,12 @@
         <result column="create_time_" property="createTime"/>
         <result column="object_id_" property="objectId"/>
         <result column="tenant_id_" property="tenantId"/>
+        <result column="feedback_type_" property="feedbackType" typeHandler="com.ym.mec.common.dal.CustomEnumTypeHandler"/>
     </resultMap>
     <sql id="Base_Column_List">
         <!--@mbg.generated-->
         id_, music_group_id_, organ_id_, student_id_, teacher_id_, visiter_type_, type_, purpose_, overview_, feedback_,
-        visit_time_, create_time_,tenant_id_
+        visit_time_, create_time_,tenant_id_,feedback_type_
     </sql>
     <select id="get" parameterType="java.lang.Integer" resultMap="StudentVisit">
         select
@@ -40,11 +41,12 @@
     <insert id="insert" keyColumn="id_" keyProperty="id" parameterType="com.ym.mec.biz.dal.entity.StudentVisit"
             useGeneratedKeys="true">
         <!--@mbg.generated-->
-        insert into student_visit (music_group_id_, organ_id_, student_id_, teacher_id_, visiter_type_,
+        insert into student_visit (music_group_id_, organ_id_, student_id_, teacher_id_, visiter_type_, feedback_type_,
         type_, purpose_, overview_,
         feedback_, visit_time_, create_time_,object_id_,tenant_id_)
         values (#{musicGroupId,jdbcType=VARCHAR}, #{organId,jdbcType=INTEGER}, #{studentId,jdbcType=INTEGER},
         #{teacherId,jdbcType=INTEGER},#{visiterType,typeHandler=com.ym.mec.common.dal.CustomEnumTypeHandler},
+                #{feedbackType,typeHandler=com.ym.mec.common.dal.CustomEnumTypeHandler},
         #{type,jdbcType=VARCHAR}, #{purpose,jdbcType=VARCHAR}, #{overview,jdbcType=VARCHAR},
         #{feedback,jdbcType=VARCHAR}, #{visitTime}, #{createTime},#{objectId},#{tenantId})
     </insert>

+ 6 - 0
mec-client-api/src/main/java/com/ym/mec/task/TaskRemoteService.java

@@ -254,4 +254,10 @@ public interface TaskRemoteService {
      */
     @GetMapping("task/destroyExpiredLiveRoom")
     void destroyExpiredLiveRoom();
+
+    /**
+     * 学员小课统计
+     */
+    @GetMapping("task/studentSmallClassStatistics")
+    void studentSmallClassStatistics();
 }

+ 5 - 0
mec-client-api/src/main/java/com/ym/mec/task/fallback/TaskRemoteServiceFallback.java

@@ -304,4 +304,9 @@ public class TaskRemoteServiceFallback implements TaskRemoteService {
     public void destroyExpiredLiveRoom() {
         logger.error("销毁直播间失败");
     }
+
+    @Override
+    public void studentSmallClassStatistics() {
+        logger.error("学员小课统计失败");
+    }
 }

+ 17 - 13
mec-im/src/main/java/com/ym/controller/RoomController.java

@@ -84,19 +84,23 @@ public class RoomController{
 
     @RequestMapping(value = "/statusSync")
     public void statusSync(@RequestBody String body) throws Exception {
-        ChannelStateNotify notify = JSONObject.parseObject(body, ChannelStateNotify.class);
-        log.info("statusSyncParam: {}",JSONObject.toJSON(notify));
-        String roomId = notify.getChannelId();
-        String userId = notify.getUserId();
-        switch (notify.getEvent()){
-            case 11:
-                //成员加入
-                roomService.joinRoomSuccess(roomId, userId,null);
-                break;
-            case 12:
-                //成员退出
-                roomService.leaveRoomSuccess(roomId, userId,null);
-                break;
+        try {
+            ChannelStateNotify notify = JSONObject.parseObject(body, ChannelStateNotify.class);
+            log.info("statusSyncParam: {}",JSONObject.toJSON(notify));
+            String roomId = notify.getChannelId();
+            String userId = notify.getUserId();
+            switch (notify.getEvent()){
+                case 11:
+                    //成员加入
+                    roomService.joinRoomSuccess(roomId, userId,null);
+                    break;
+                case 12:
+                    //成员退出
+                    roomService.leaveRoomSuccess(roomId, userId,null);
+                    break;
+            }
+        }catch (Exception e){
+            log.error(e.getLocalizedMessage());
         }
     }
 

+ 19 - 0
mec-task/src/main/java/com/ym/mec/task/jobs/StudentSmallClassStatisticsTask.java

@@ -0,0 +1,19 @@
+package com.ym.mec.task.jobs;
+
+import com.ym.mec.task.TaskRemoteService;
+import com.ym.mec.task.core.BaseTask;
+import com.ym.mec.task.core.TaskException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class StudentSmallClassStatisticsTask extends BaseTask {
+
+	@Autowired
+	private TaskRemoteService taskRemoteService;
+
+	@Override
+	public void execute() throws TaskException {
+		taskRemoteService.studentSmallClassStatistics();
+	}
+}

+ 40 - 0
mec-web/src/main/java/com/ym/mec/web/controller/StudentStatisticsController.java

@@ -0,0 +1,40 @@
+package com.ym.mec.web.controller;
+
+import com.ym.mec.biz.dal.dto.StudentStatisticsDto;
+import com.ym.mec.biz.dal.dto.StudentStatisticsSumDto;
+import com.ym.mec.biz.dal.page.StudentStatisticsQueryInfo;
+import com.ym.mec.biz.service.StudentStatisticsService;
+import com.ym.mec.common.controller.BaseController;
+import com.ym.mec.common.entity.HttpResponseResult;
+import com.ym.mec.common.page.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RequestMapping("studentStatistics")
+@Api(tags = "学员小课统计")
+@RestController
+public class StudentStatisticsController extends BaseController {
+
+    @Autowired
+    private StudentStatisticsService studentStatisticsService;
+
+    @ApiOperation(value = "分页查询收费类型列表")
+    @PostMapping("/queryPage")
+    @PreAuthorize("@pcs.hasPermissions('studentStatistics/queryPage')")
+    public HttpResponseResult<PageInfo<StudentStatisticsDto>> queryPage(StudentStatisticsQueryInfo queryInfo) {
+        return succeed(studentStatisticsService.queryStatisticsPage(queryInfo));
+    }
+
+    @ApiOperation(value = "小课学员管理汇总")
+    @GetMapping("/studentSmallClassStatisticsSum")
+    @PreAuthorize("@pcs.hasPermissions('studentStatistics/studentSmallClassStatisticsSum')")
+    public HttpResponseResult<StudentStatisticsSumDto> studentSmallClassStatisticsSum(String groupType) {
+        return succeed(studentStatisticsService.studentSmallClassStatisticsSum(groupType));
+    }
+}

+ 8 - 0
mec-web/src/main/java/com/ym/mec/web/controller/TaskController.java

@@ -118,6 +118,8 @@ public class TaskController extends BaseController {
 	private TeacherContractsService teacherContractsService;
     @Autowired
     private ImLiveBroadcastRoomService imLiveBroadcastRoomService;
+	@Autowired
+	private StudentStatisticsService studentStatisticsService;
 
 	@GetMapping(value = "/syncImHistoryMessageTask")
 	// 同步即时通讯聊天记录
@@ -577,4 +579,10 @@ public class TaskController extends BaseController {
     public void destroyExpiredLiveRoom(){
         imLiveBroadcastRoomService.destroyExpiredLiveRoom();
     }
+
+    @ApiOperation("学员小课统计")
+    @GetMapping(value = "/studentSmallClassStatistics")
+    public void studentSmallClassStatistics(){
+		studentStatisticsService.updateStudentStatistics();
+    }
 }