liujunchi 2 tahun lalu
induk
melakukan
d108831569
44 mengubah file dengan 1819 tambahan dan 122 penghapusan
  1. 1 1
      audio-analysis/src/main/java/com/yonge/audio/AudioAnalysisServerApplication.java
  2. 1 0
      cooleshow-common/src/main/java/com/yonge/cooleshow/common/enums/CacheNameEnum.java
  3. 32 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/ActivityEvaluationController.java
  4. 48 2
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/TeacherController.java
  5. 21 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/open/ImUserFriendController.java
  6. 52 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/io/request/im/IMNotifyMessageVO.java
  7. 102 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/io/request/teacher/TeacherVO.java
  8. 15 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/task/TaskController.java
  9. 6 0
      cooleshow-user/user-biz/pom.xml
  10. 9 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/ActivityEvaluationDao.java
  11. 22 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/ActivityPlanDao.java
  12. 48 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/ActivityEvaluationDto.java
  13. 11 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/search/ActivityRegistrationSearch.java
  14. 12 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ActivityEvaluation.java
  15. 24 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ActivityEvaluationRecord.java
  16. 25 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ActivityPlan.java
  17. 34 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/activity/ActivityRankingMethodEnum.java
  18. 1 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/activity/ActivityRankingRuleEnum.java
  19. 8 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ActivityEvaluationService.java
  20. 8 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ImUserFriendService.java
  21. 8 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/TeacherService.java
  22. 199 19
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ActivityEvaluationRecordServiceImpl.java
  23. 100 10
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ActivityEvaluationServiceImpl.java
  24. 93 28
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ActivityPlanEvaluationServiceImpl.java
  25. 183 34
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ActivityPlanServiceImpl.java
  26. 59 11
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ActivityRegistrationServiceImpl.java
  27. 5 3
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ActivityRewardServiceImpl.java
  28. 122 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImUserFriendServiceImpl.java
  29. 42 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/TeacherServiceImpl.java
  30. 11 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/ActivityEvaluationVo.java
  31. 70 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/ActivityMusicVo.java
  32. 11 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/ActivityRankingVo.java
  33. 38 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/ActivityRegistrationVo.java
  34. 79 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/MusicActivityVo.java
  35. 39 3
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/MyFens.java
  36. 1 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/StatGroupWrapper.java
  37. 39 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/im/CustomerService.java
  38. 77 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/teacher/TeacherWrapper.java
  39. 29 11
      cooleshow-user/user-biz/src/main/resources/config/mybatis/ActivityEvaluationMapper.xml
  40. 48 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/ActivityPlanMapper.xml
  41. 3 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/ActivityRegistrationMapper.xml
  42. 1 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/TeacherMapper.xml
  43. 13 0
      cooleshow-websocket/src/main/resources/bootstrap-dev.yml
  44. 69 0
      cooleshow-websocket/src/main/resources/logback-spring.xml

+ 1 - 1
audio-analysis/src/main/java/com/yonge/audio/AudioAnalysisServerApplication.java

@@ -24,7 +24,7 @@ import com.yonge.audio.config.LocalFastJsonHttpMessageConverter;
 @SpringBootApplication
 @EnableDiscoveryClient
 @EnableFeignClients("com.yonge.cooleshow")
-@MapperScan(basePackages = {"com.yonge.cooleshow.biz.dal.dao", "com.yonge.toolset.payment.core.dao"})
+@MapperScan(basePackages = {"com.yonge.cooleshow.biz.dal.dao", "com.yonge.cooleshow.biz.dal.mapper", "com.yonge.toolset.payment.core.dao"})
 @ComponentScan(basePackages = {
         "com.yonge.netty", "com.yonge.cooleshow", "com.yonge.toolset"
 })

+ 1 - 0
cooleshow-common/src/main/java/com/yonge/cooleshow/common/enums/CacheNameEnum.java

@@ -30,6 +30,7 @@ public enum CacheNameEnum implements BaseEnum<String, CacheNameEnum> {
     LOCK_ACTIVITY_REWARD_STOCK("锁活动奖品变更"),
     LOCK_ACTIVITY_STOCK("锁活动变更"),
     LOCK_STANDARD_GIFT_CRON("达标活动定时任务锁"),
+    LOCK_EVALUATION_CRON("评测活动定时任务锁"),
     ;
     /***
      * 缓存描述

+ 32 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/ActivityEvaluationController.java

@@ -23,6 +23,8 @@ import com.yonge.toolset.mybatis.support.PageUtil;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
+import org.apache.commons.collections.CollectionUtils;
+import org.joda.time.DateTime;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -36,6 +38,7 @@ import javax.annotation.Resource;
 import javax.validation.Valid;
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
 
 @RestController
 @RequestMapping("/activityEvaluation")
@@ -80,6 +83,35 @@ public class ActivityEvaluationController extends BaseController {
     	return status(activityEvaluationService.addBatch(param,user));
 	}
 
+	@ApiOperation(value = "批量更新曲目信息")
+	@PostMapping("/batchUpdate")
+	public HttpResponseResult<Boolean> batchUpdateEvaluationInfo(ActivityEvaluationDto info) {
+
+		SysUser user = sysUserFeignService.queryUserInfo();
+		if (user == null || null == user.getId()) {
+			return failed(HttpStatus.FORBIDDEN, "请登录");
+		}
+
+		if (CollectionUtils.isEmpty(info.getEvaluationInfos())) {
+			return failed("评测曲目信息为空");
+		}
+
+		for (ActivityEvaluationDto.EvaluationInfo item : info.getEvaluationInfos()) {
+
+			if (Objects.isNull(item.getId())
+					|| Objects.isNull(item.getEvaluationDifficulty())) {
+				return failed("无效的请求参数");
+			}
+
+			item.updateBy(user.getId()).updateTime(DateTime.now().toDate());
+		}
+
+		// 批量更新曲目信息
+		int ret = activityEvaluationService.batchUpdateEvaluationInfo(user, info);
+
+		return status(ret > 0);
+	}
+
  	/**
 	 * 删除
 	 */

+ 48 - 2
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/TeacherController.java

@@ -7,6 +7,10 @@ import com.yonge.cooleshow.biz.dal.queryInfo.TeacherBindingUserQueryInfo;
 import com.yonge.cooleshow.biz.dal.service.UserBindingTeacherService;
 import com.yonge.cooleshow.biz.dal.vo.userBindingTeacher.UserBindingCourseWrapper;
 import com.yonge.cooleshow.biz.dal.vo.userBindingTeacher.UserBindingTeacherWrapper;
+import com.yonge.cooleshow.admin.io.request.teacher.TeacherVO;
+import com.yonge.cooleshow.biz.dal.vo.MyFens;
+import com.yonge.cooleshow.biz.dal.wrapper.teacher.TeacherWrapper;
+import com.yonge.toolset.base.page.QueryInfo;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiImplicitParams;
@@ -16,6 +20,8 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.util.Date;
 import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
 import java.util.stream.Collectors;
 
 import javax.servlet.http.HttpServletResponse;
@@ -24,6 +30,7 @@ import javax.validation.Valid;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.util.CollectionUtils;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -282,8 +289,47 @@ public class TeacherController extends BaseController {
                                                                                                              TeacherBindingUserQueryInfo.BindingStudentCourseQuery.from(query.jsonString()));
         // 数据转换
         List<TeacherBindingUserVo.BindingStudentCourseList> pageInfos = JSON.parseArray(JSON.toJSONString(wrapperIPage.getRecords()),
-                                                                               TeacherBindingUserVo.BindingStudentCourseList.class);
+                                                                                        TeacherBindingUserVo.BindingStudentCourseList.class);
 
-        return succeed(PageUtil.getPageInfo(wrapperIPage,pageInfos));
+        return succeed(PageUtil.getPageInfo(wrapperIPage, pageInfos));
+    }
+    /**
+     * 查询老师统计指标
+     * @param userId 老师ID
+     * @return HttpResponseResult<TeacherVO.TeacherStat>
+     */
+    @ApiOperation(value = "老师统计指标", notes = "传入id")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "id", paramType = "path", dataType = "long", required = true),
+    })
+    @GetMapping("/stat/{id}")
+    public HttpResponseResult<TeacherVO.TeacherStat> teacherStatInfo(@PathVariable("id") Long userId) {
+
+        // 老师统计指标
+        TeacherWrapper.TeacherStatInfo statInfo = teacherService.findTeacherStatInfoById(userId);
+
+        return succeed(TeacherVO.TeacherStat.from(statInfo.jsonString()));
+    }
+
+    /**
+     * 老师粉丝信息查询
+     * @param query TeacherVO.TeacherFansQuery
+     * @return HttpResponseResult<PageInfo<MyFens>>
+     */
+    @ApiOperation(value = "我的粉丝")
+    @PostMapping(value = "/myFans")
+    public HttpResponseResult<PageInfo<TeacherVO.TeacherFans>> queryMyFans(@RequestBody TeacherVO.TeacherFansQuery query) {
+
+        if (Optional.ofNullable(query.getTeacherId()).orElse(0L) <= 0) {
+            return failed("无效的请求参数");
+        }
+
+        IPage<MyFens> pages = teacherService.queryMyFans(PageUtil.getPage(query), query.getTeacherId());
+
+        // 数据转换
+        List<TeacherVO.TeacherFans> responses = JSON.parseArray(JSON.toJSONString(pages.getRecords()),
+                TeacherVO.TeacherFans.class);
+
+        return succeed(PageUtil.getPageInfo(pages, responses));
     }
 }

+ 21 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/open/ImUserFriendController.java

@@ -1,7 +1,9 @@
 package com.yonge.cooleshow.admin.controller.open;
 
+import com.yonge.cooleshow.admin.io.request.im.IMNotifyMessageVO;
 import com.yonge.cooleshow.admin.io.request.im.UserFriendInfoVO;
 import com.yonge.cooleshow.biz.dal.service.ImUserFriendService;
+import com.yonge.cooleshow.biz.dal.wrapper.im.CustomerService;
 import com.yonge.cooleshow.common.controller.BaseController;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
 import io.swagger.annotations.Api;
@@ -44,5 +46,24 @@ public class ImUserFriendController extends BaseController {
 
         return succeed(ret > 0);
     }
+
+    /**
+     * 发送系统客服消息
+     * @param info IMNotifyMessageVO
+     * @return HttpResponseResult<Boolean>
+     */
+    @PostMapping(value = "/message")
+    public HttpResponseResult<Boolean> sendSysCustomerServiceMessage(@RequestBody IMNotifyMessageVO info) {
+
+        if (info.invalidRequestParam()) {
+            return failed("无效的请求参数");
+        }
+
+        // 发送客服通知消息
+        imUserFriendService.sendCustomerServiceNotifyMessage(info.getSender(),
+                CustomerService.NotifyMessage.from(info.jsonString()));
+
+        return succeed();
+    }
 }
 

+ 52 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/io/request/im/IMNotifyMessageVO.java

@@ -0,0 +1,52 @@
+package com.yonge.cooleshow.admin.io.request.im;
+
+import com.alibaba.fastjson.JSON;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 系统IM通知信息
+ * Created by Eric.Shang on 2022/9/20.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class IMNotifyMessageVO implements Serializable {
+
+    @ApiModelProperty(value = "发送者ID")
+    private String sender;
+
+    @ApiModelProperty(value = "接收ID")
+    private List<String> receives;
+
+    @ApiModelProperty(value = "通知标题")
+    private String title;
+
+    @ApiModelProperty(value = "文件通知消息")
+    private String txtMessage;
+
+    @ApiModelProperty(value = "图片通知消息")
+    private String imgMessage;
+
+    @ApiModelProperty(value = "图片文件地址")
+    private String imgUrl;
+
+    public String jsonString() {
+
+        return JSON.toJSONString(this);
+    }
+
+    public boolean invalidRequestParam() {
+
+        return StringUtils.isEmpty(getSender())
+                || StringUtils.isAllBlank(getTxtMessage(), getImgMessage());
+    }
+}

+ 102 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/io/request/teacher/TeacherVO.java

@@ -0,0 +1,102 @@
+package com.yonge.cooleshow.admin.io.request.teacher;
+
+import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.yonge.cooleshow.biz.dal.enums.GenderEnum;
+import com.yonge.cooleshow.biz.dal.enums.MK;
+import com.yonge.cooleshow.common.enums.YesOrNoEnum;
+import com.yonge.toolset.base.page.QueryInfo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 老师端统计信息
+ * Created by Eric.Shang on 2022/10/8.
+ */
+public class TeacherVO {
+
+    /**
+     * 老师统计指标信息
+     */
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel("老师统计响应信息")
+    public static class TeacherStat implements Serializable {
+
+        @ApiModelProperty("星级")
+        private Integer starGrade;
+        @ApiModelProperty("粉丝数")
+        private Integer fansNum;
+        @ApiModelProperty("已上课时")
+        private Integer expTime;
+        @ApiModelProperty("未上课时")
+        private Integer unExpTime;
+        @ApiModelProperty("专辑数 ")
+        private Integer musicAlbumNum;
+        @ApiModelProperty("曲谱数 ")
+        private Integer musicSheetNum;
+        @ApiModelProperty("学生数")
+        private Integer studentNums;
+
+        public static TeacherStat from(String recv) {
+
+            return JSON.parseObject(recv, TeacherStat.class);
+        }
+    }
+
+    /**
+     * 老师粉丝数
+     */
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class TeacherFansQuery extends QueryInfo {
+
+        @ApiModelProperty("老师ID")
+        private Long teacherId;
+    }
+
+    /**
+     * 老师粉丝
+     */
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class TeacherFans implements Serializable {
+
+        @ApiModelProperty("用户编号")
+        private String userId;
+        @ApiModelProperty("昵称")
+        private String userName;
+        @ApiModelProperty(value = "真实姓名")
+        private String realName;
+        @ApiModelProperty("头像地址")
+        private String avatar;
+        @ApiModelProperty("声部")
+        private String subjectName;
+        @ApiModelProperty(value = "性别 0女 1男")
+        private GenderEnum gender;
+        @ApiModelProperty(value = "是否会员 0否 1是")
+        private YesOrNoEnum isVip;
+
+        @ApiModelProperty("手机号")
+        private String phone;
+
+        @ApiModelProperty("关注时间")
+        @JsonFormat(pattern = MK.TIME_PATTERN, timezone = MK.TIME_ZONE)
+        private Date starTime;
+
+        @ApiModelProperty("出生日期")
+        private Date birthdate;
+
+        @ApiModelProperty("出生日期")
+        private Integer age;
+    }
+}

+ 15 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/task/TaskController.java

@@ -12,6 +12,7 @@ import org.springframework.beans.factory.annotation.Value;
 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.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 import springfox.documentation.annotations.ApiIgnore;
 
@@ -39,6 +40,8 @@ public class TaskController extends BaseController {
     private PlatformCashAccountRecordService platformCashAccountRecordService;
     @Autowired
     private ActivityPlanService activityPlanService;
+    @Autowired
+    private ActivityPlanEvaluationService activityPlanEvaluationService;
 
     @Value("${app.blacklist:}")
     private String blacklistFilePath;
@@ -111,4 +114,16 @@ public class TaskController extends BaseController {
         activityPlanService.activityIng();
         return HttpResponseResult.succeed();
     }
+
+    /**
+     * 活动测试
+     * @param activityId 活动ID
+     * @return HttpResponseResult<Object>
+     */
+    @GetMapping("/test")
+    public HttpResponseResult<Object> test(@RequestParam("activityId") Long activityId) {
+
+        activityPlanEvaluationService.successActivity(activityId);
+        return HttpResponseResult.succeed();
+    }
 }

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

@@ -43,6 +43,12 @@
             <version>${rongcloud.im.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>net.coobird</groupId>
+            <artifactId>thumbnailator</artifactId>
+            <version>0.4.11</version>
+        </dependency>
+
         <!-- redisson -->
         <dependency>
             <groupId>org.redisson</groupId>

+ 9 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/ActivityEvaluationDao.java

@@ -9,6 +9,7 @@ import com.yonge.cooleshow.biz.dal.dto.search.MusicSheetSearch;
 import com.yonge.cooleshow.biz.dal.vo.ActivityMusicVo;
 import com.yonge.cooleshow.biz.dal.vo.MusicSheetVo;
 import com.yonge.cooleshow.biz.dal.vo.activity.ActivityTeacherWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.StatGroupWrapper;
 import org.apache.ibatis.annotations.Param;
 import com.yonge.cooleshow.biz.dal.entity.ActivityEvaluation;
 import com.yonge.cooleshow.biz.dal.vo.ActivityEvaluationVo;
@@ -85,4 +86,12 @@ public interface ActivityEvaluationDao extends BaseMapper<ActivityEvaluation> {
 	 * @return List<ActivityTeacherWrapper>
 	 */
 	List<ActivityTeacherWrapper> selectActivityTeacherByTimePageInfo(@Param("page") IPage<ActivityTeacherWrapper> page, @Param("record") ActivityTeacherQuery query);
+
+	/**
+	 * 评测活动声部曲目最高分
+	 * @param activityId 活动ID
+	 * @param userId 用户ID
+	 * @return List<StatGroupWrapper>
+	 */
+    List<StatGroupWrapper> selectSubjectMusicHighestScoreStat(@Param("activityId") Long activityId, @Param("userId") Long userId);
 }

+ 22 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/ActivityPlanDao.java

@@ -4,6 +4,8 @@ import java.util.List;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.yonge.cooleshow.biz.dal.entity.ActivityEvaluationRecord;
+import com.yonge.cooleshow.biz.dal.vo.ActivityRankingVo;
 import com.yonge.cooleshow.biz.dal.wrapper.StatGroupWrapper;
 import org.apache.ibatis.annotations.Param;
 import com.yonge.cooleshow.biz.dal.entity.ActivityPlan;
@@ -82,4 +84,24 @@ public interface ActivityPlanDao extends BaseMapper<ActivityPlan> {
 	 * @return List<StatGroupWrapper>
 	 */
 	List<StatGroupWrapper> selectActivityWinnerStatInfo(@Param("activityIds") List<Long> activityIds);
+
+	/**
+	 * 活动总分用户排名
+	 * @param activityId 活动ID
+	 * @param subjectId 声部ID
+	 * @return List<ActivityRankingVo>
+	 */
+    List<ActivityRankingVo> selectActivityHighestScoreRankingInfo(@Param("activityId") Long activityId, @Param("subjectId") Long subjectId,
+																  @Param("rankingScore") Double rankingScore, @Param("limit") Integer limit);
+
+	/**
+	 * 总分用户排名信息
+	 * @param activityPlanId 活动ID
+	 * @param subjectId 声部ID
+	 * @param rankingScore 分数
+	 * @param userId 用户ID
+	 * @return ActivityRankingVo
+	 */
+	ActivityRankingVo selectActivityHighestScoreUserRanking(@Param("activityId") Long activityPlanId, @Param("subjectId") Long subjectId,
+															@Param("rankingScore") Double rankingScore, @Param("userId") Long userId);
 }

+ 48 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/ActivityEvaluationDto.java

@@ -1,10 +1,17 @@
 package com.yonge.cooleshow.biz.dal.dto;
 
 import com.yonge.cooleshow.common.entity.BaseEntity;
+import com.yonge.cooleshow.common.enums.HardLevelEnum;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
 
 import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
 
 /**
  * @Author: liweifan
@@ -17,12 +24,16 @@ public class ActivityEvaluationDto extends BaseEntity {
 	@ApiModelProperty("活动id ")
 	@NotNull(message = "活动id不能为空")
 	private Long activityId;
+
 	@ApiModelProperty("曲目id 多个用,分割 ")
 	//@NotBlank(message = "曲子id不能为空")
 	private String musicSheetIds; // 评测活动曲目ID
 	@ApiModelProperty("活动老师id 多个用,分割 ")
 	private String teacherIds; // 分享活动老师ID
 
+	@ApiModelProperty("评测内空 ")
+	private List<EvaluationInfo> evaluationInfos;
+
 	public String getTeacherIds() {
 		return teacherIds;
 	}
@@ -47,4 +58,41 @@ public class ActivityEvaluationDto extends BaseEntity {
 	public void setMusicSheetIds(String musicSheetIds) {
 		this.musicSheetIds = musicSheetIds;
 	}
+
+	public List<EvaluationInfo> getEvaluationInfos() {
+		return evaluationInfos;
+	}
+
+	public void setEvaluationInfos(List<EvaluationInfo> evaluationInfos) {
+		this.evaluationInfos = evaluationInfos;
+	}
+
+	/**
+	 * 评测活动内容
+	 */
+	@Data
+	@NoArgsConstructor
+	@AllArgsConstructor
+	public static class EvaluationInfo implements Serializable {
+
+		@ApiModelProperty("主键 ")
+		private Long id;
+
+		@ApiModelProperty("评测难度 BEGINNER 入门级 ADVANCED 进阶级 PERFORMER 大师级 ")
+		private HardLevelEnum evaluationDifficulty;
+
+		private Date updateTime;
+		private Long updateBy;
+
+
+		public EvaluationInfo updateTime(Date updateTime) {
+			this.updateTime = updateTime;
+			return this;
+		}
+
+		public EvaluationInfo updateBy(Long updateBy) {
+			this.updateBy = updateBy;
+			return this;
+		}
+	}
 }

+ 11 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/search/ActivityRegistrationSearch.java

@@ -34,6 +34,9 @@ public class ActivityRegistrationSearch extends QueryInfo {
     @ApiModelProperty(value = "是否获奖 0 否 1 是")
     private YesOrNoEnum rewardFlag;
 
+    @ApiModelProperty(value = "声部ID集合")
+    private List<Long> subjectIds;
+
     public ActivityTypeEnum getActivityType() {
         return activityType;
     }
@@ -65,4 +68,12 @@ public class ActivityRegistrationSearch extends QueryInfo {
     public void setRewardFlag(YesOrNoEnum rewardFlag) {
         this.rewardFlag = rewardFlag;
     }
+
+    public List<Long> getSubjectIds() {
+        return subjectIds;
+    }
+
+    public void setSubjectIds(List<Long> subjectIds) {
+        this.subjectIds = subjectIds;
+    }
 }

+ 12 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ActivityEvaluation.java

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.yonge.cooleshow.common.enums.ActivityResourceEnum;
+import com.yonge.cooleshow.common.enums.HardLevelEnum;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -30,6 +31,10 @@ public class ActivityEvaluation implements Serializable {
     @TableField(value = "music_sheet_id_")
     private Long musicSheetId;
 
+    @ApiModelProperty("评测难度 BEGINNER 入门级 ADVANCED 进阶级 PERFORMER 大师级 ")
+    @TableField(value = "evaluation_difficulty_")
+    private HardLevelEnum evaluationDifficulty;
+
     @ApiModelProperty("资源类型 ")
     @TableField(value = "resource_type_")
     private ActivityResourceEnum resourceType;
@@ -91,4 +96,11 @@ public class ActivityEvaluation implements Serializable {
         this.createBy = createBy;
     }
 
+    public HardLevelEnum getEvaluationDifficulty() {
+        return evaluationDifficulty;
+    }
+
+    public void setEvaluationDifficulty(HardLevelEnum evaluationDifficulty) {
+        this.evaluationDifficulty = evaluationDifficulty;
+    }
 }

+ 24 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ActivityEvaluationRecord.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
+import com.yonge.cooleshow.biz.dal.enums.activity.ActivityRankingMethodEnum;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -43,6 +44,14 @@ public class ActivityEvaluationRecord implements Serializable {
     @ApiModelProperty("次数")
     @TableField(value = "times_")
     private int times;
+
+    @ApiModelProperty("评份方式")
+    @TableField(value = "ranking_method_")
+    private ActivityRankingMethodEnum rankingMethod;
+
+    @ApiModelProperty("活动报名时间 ")
+    @TableField(value = "registration_time_")
+    private Long registrationTime;
     
     @ApiModelProperty("创建时间 ")
     @TableField(value = "create_time_")
@@ -114,4 +123,19 @@ public class ActivityEvaluationRecord implements Serializable {
         this.createTime = createTime;
     }
 
+    public ActivityRankingMethodEnum getRankingMethod() {
+        return rankingMethod;
+    }
+
+    public void setRankingMethod(ActivityRankingMethodEnum rankingMethod) {
+        this.rankingMethod = rankingMethod;
+    }
+
+    public Long getRegistrationTime() {
+        return registrationTime;
+    }
+
+    public void setRegistrationTime(Long registrationTime) {
+        this.registrationTime = registrationTime;
+    }
 }

+ 25 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ActivityPlan.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.enums.activity.ActivityRankingMethodEnum;
 import com.yonge.cooleshow.biz.dal.enums.activity.ActivityRankingRuleEnum;
 import com.yonge.cooleshow.common.enums.ActivityTypeEnum;
 import com.yonge.cooleshow.common.enums.RegistrationMethodEnum;
@@ -118,6 +119,14 @@ public class ActivityPlan implements Serializable {
     @TableField(value = "ranking_rule_")
     private ActivityRankingRuleEnum rankingRule;
 
+    @ApiModelProperty("活动形式: MUSIC_RANKING(单曲排名) TOTAL_SCORE(总分排名) ")
+    @TableField(value = "ranking_method_")
+    private ActivityRankingMethodEnum rankingMethod;
+
+    @ApiModelProperty("获奖分数 ")
+    @TableField(value = "ranking_score_")
+    private Double rankingScore;
+
     public Long getId() {
         return id;
     }
@@ -317,4 +326,20 @@ public class ActivityPlan implements Serializable {
     public void setRankingRule(ActivityRankingRuleEnum rankingRule) {
         this.rankingRule = rankingRule;
     }
+
+    public ActivityRankingMethodEnum getRankingMethod() {
+        return rankingMethod;
+    }
+
+    public void setRankingMethod(ActivityRankingMethodEnum rankingMethod) {
+        this.rankingMethod = rankingMethod;
+    }
+
+    public Double getRankingScore() {
+        return rankingScore;
+    }
+
+    public void setRankingScore(Double rankingScore) {
+        this.rankingScore = rankingScore;
+    }
 }

+ 34 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/activity/ActivityRankingMethodEnum.java

@@ -0,0 +1,34 @@
+package com.yonge.cooleshow.biz.dal.enums.activity;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.yonge.toolset.base.enums.BaseEnum;
+
+/**
+ * 活动获奖排名方式
+ *
+ * @Author: Eric
+ * @Data: 2022/3/16 10:19
+ */
+public enum ActivityRankingMethodEnum implements BaseEnum<String, ActivityRankingMethodEnum> {
+
+    MUSIC_RANKING("单曲排名"),
+    TOTAL_SCORE("总分排名"),
+    ;
+    @EnumValue
+    private String code;
+    private String name;
+
+    ActivityRankingMethodEnum(String name) {
+        this.code = this.name();
+        this.name = name;
+    }
+
+    @Override
+    public String getCode() {
+        return this.code;
+    }
+
+    public String getName() {
+        return name;
+    }
+}

+ 1 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/activity/ActivityRankingRuleEnum.java

@@ -13,6 +13,7 @@ public enum ActivityRankingRuleEnum implements BaseEnum<String, ActivityRankingR
 
     EVALUATIONS_NUMBER("评测次数"),
     EVALUATIONS_TIME("评测时间"),
+    TOTAL_SCORE("评测总分"),
     ;
     @EnumValue
     private String code;

+ 8 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ActivityEvaluationService.java

@@ -77,4 +77,12 @@ public interface ActivityEvaluationService extends IService<ActivityEvaluation>
 	 * @return IPage<ActivityTeacherWrapper>
 	 */
 	IPage<ActivityTeacherWrapper> activityTeacherByTimePageInfo(IPage<ActivityTeacherWrapper> page, ActivityTeacherQuery query);
+
+	/**
+	 * 批量更新评测曲目信息
+	 * @param user SysUser
+	 * @param info ActivityEvaluationDto
+	 * @return int
+	 */
+    int batchUpdateEvaluationInfo(SysUser user, ActivityEvaluationDto info);
 }

+ 8 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ImUserFriendService.java

@@ -3,6 +3,7 @@ package com.yonge.cooleshow.biz.dal.service;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.yonge.cooleshow.biz.dal.dao.ImUserFriendDao;
 import com.yonge.cooleshow.biz.dal.entity.ImUserFriend;
+import com.yonge.cooleshow.biz.dal.wrapper.im.CustomerService;
 
 import java.util.List;
 import java.util.Set;
@@ -43,5 +44,12 @@ public interface ImUserFriendService extends IService<ImUserFriend> {
      * @return Integer
      */
     Integer registerUserBindCustomerService(Long userId, List<Long> friendIds);
+
+    /**
+     * 发送系统客服消息
+     * @param sender 发送者
+     * @param info CustomerService.NotifyMessage
+     */
+    void sendCustomerServiceNotifyMessage(String sender, CustomerService.NotifyMessage info);
 }
 

+ 8 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/TeacherService.java

@@ -10,6 +10,7 @@ import com.yonge.cooleshow.biz.dal.entity.Teacher;
 import com.yonge.cooleshow.biz.dal.entity.TeacherStyleVideo;
 import com.yonge.cooleshow.biz.dal.enums.TeacherTagEnum;
 import com.yonge.cooleshow.biz.dal.vo.*;
+import com.yonge.cooleshow.biz.dal.wrapper.teacher.TeacherWrapper;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
 
 import java.util.List;
@@ -158,4 +159,11 @@ public interface TeacherService extends IService<Teacher> {
      * @return TeacherVo
      */
     TeacherVo findTeacherDetailInfo(Long teacherId);
+
+    /**
+     * 老师统计指标信息
+     * @param userId 用户ID
+     * @return TeacherWrapper.TeacherStatInfo
+     */
+    TeacherWrapper.TeacherStatInfo findTeacherStatInfoById(Long userId);
 }

+ 199 - 19
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ActivityEvaluationRecordServiceImpl.java

@@ -1,43 +1,63 @@
 package com.yonge.cooleshow.biz.dal.service.impl;
 
-import java.math.BigDecimal;
-import java.util.Date;
-import java.util.List;
-import java.util.Optional;
-
-import com.google.common.collect.Lists;
-import com.yonge.cooleshow.biz.dal.entity.ActivityPlan;
-import com.yonge.cooleshow.biz.dal.enums.activity.ActivityRankingRuleEnum;
-import com.yonge.cooleshow.biz.dal.service.ActivityPlanService;
-import org.apache.commons.collections.CollectionUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.google.common.collect.Lists;
 import com.yonge.cooleshow.biz.dal.dao.ActivityEvaluationRecordDao;
+import com.yonge.cooleshow.biz.dal.dao.ActivityPlanDao;
+import com.yonge.cooleshow.biz.dal.dao.StudentDao;
 import com.yonge.cooleshow.biz.dal.dto.search.ActivityEvaluationRecordSearch;
 import com.yonge.cooleshow.biz.dal.entity.ActivityEvaluation;
 import com.yonge.cooleshow.biz.dal.entity.ActivityEvaluationRecord;
+import com.yonge.cooleshow.biz.dal.entity.ActivityPlan;
+import com.yonge.cooleshow.biz.dal.entity.MusicSheet;
+import com.yonge.cooleshow.biz.dal.entity.Student;
+import com.yonge.cooleshow.biz.dal.entity.Subject;
+import com.yonge.cooleshow.biz.dal.enums.activity.ActivityRankingMethodEnum;
 import com.yonge.cooleshow.biz.dal.service.ActivityEvaluationRecordService;
 import com.yonge.cooleshow.biz.dal.service.ActivityEvaluationService;
+import com.yonge.cooleshow.biz.dal.service.ActivityPlanService;
+import com.yonge.cooleshow.biz.dal.service.MusicSheetService;
+import com.yonge.cooleshow.biz.dal.service.SubjectService;
 import com.yonge.cooleshow.biz.dal.vo.ActivityEvaluationRecordVo;
 import com.yonge.cooleshow.biz.dal.vo.ActivityRankingVo;
 import com.yonge.cooleshow.common.enums.ActivityResourceEnum;
 import com.yonge.toolset.base.exception.BizException;
+import com.yonge.toolset.base.util.ThreadPool;
+import lombok.extern.slf4j.Slf4j;
+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.math.BigDecimal;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
 
+@Slf4j
 @Service
 public class ActivityEvaluationRecordServiceImpl extends ServiceImpl<ActivityEvaluationRecordDao, ActivityEvaluationRecord> implements ActivityEvaluationRecordService {
-    private final static Logger log = LoggerFactory.getLogger(ActivityEvaluationRecordServiceImpl.class);
 
     @Autowired
     private ActivityEvaluationService activityEvaluationService;
-
+    @Autowired
+    private ActivityEvaluationRecordService activityEvaluationRecordService;
     @Autowired
     private ActivityPlanService activityPlanService;
+    @Autowired
+    private MusicSheetService musicSheetService;
+    @Autowired
+    private ActivityPlanDao activityPlanMapper;
+    @Autowired
+    private StudentDao studentMapper;
+    @Autowired
+    private SubjectService subjectService;
 
 	@Override
     public ActivityEvaluationRecordVo detail(Long id) {
@@ -63,10 +83,13 @@ public class ActivityEvaluationRecordServiceImpl extends ServiceImpl<ActivityEva
         if (activityEvaluation == null) {
             throw new BizException("未找到评测项目");
         }
+
+        // 记录曲目评测试分数
         ActivityEvaluationRecord activityEvaluationRecord = new ActivityEvaluationRecord();
         activityEvaluationRecord.setActivityId(activityEvaluation.getActivityId());
         activityEvaluationRecord.setUserId(userId);
         activityEvaluationRecord.setEvaluationId(evaluationId);
+        activityEvaluationRecord.setResourceId(activityEvaluation.getMusicSheetId());
         activityEvaluationRecord.setCreateTime(new Date());
         activityEvaluationRecord.setScore(score == null ? 0:score.doubleValue());
         
@@ -77,6 +100,99 @@ public class ActivityEvaluationRecordServiceImpl extends ServiceImpl<ActivityEva
         	activityEvaluationRecord.setTimes(lastestRecord.getTimes() + 1);
         }
         save(activityEvaluationRecord);
+
+        // 活动信息
+        ActivityPlan activity = activityPlanService.getById(activityEvaluation.getActivityId());
+        if (ActivityRankingMethodEnum.TOTAL_SCORE == activity.getRankingMethod()) {
+
+            // 若为总分评测,计算声部分数
+            ThreadPool.getExecutor().submit(() -> {
+
+                List<ActivityEvaluationRecord> records = activityEvaluationRecordService.list(Wrappers.<ActivityEvaluationRecord>lambdaQuery()
+                        .eq(ActivityEvaluationRecord::getActivityId, activityEvaluation.getActivityId())
+                        .eq(ActivityEvaluationRecord::getUserId, userId));
+
+                // 用户当前声部所有评测数据,获取单曲最高进行加和计算为声部最高分
+                Map<Long, Double> highestScoreMap = records.stream()
+                        .filter(x -> Optional.ofNullable(x.getScore()).orElse(0D) > 0)
+                        .filter(x -> Optional.ofNullable(x.getEvaluationId()).orElse(0L) > 0)
+                        .collect(Collectors.groupingBy(ActivityEvaluationRecord::getEvaluationId,
+                                Collectors.mapping(ActivityEvaluationRecord::getScore, Collectors.toSet())))
+                        .entrySet().stream()
+                        .collect(Collectors.toMap(Map.Entry::getKey,
+                                x -> x.getValue().stream().mapToDouble(Double::doubleValue).max().orElse(0D), (o, n) -> n));
+
+                // 最高分
+                double sumScore = highestScoreMap.values().stream().mapToDouble(Double::doubleValue).sum();
+
+                // 获取最高分的时间为每个单曲最高分的记录时间
+                Map<Long, Long> highestScoreTimeMap = records.stream()
+                        .filter(x -> Optional.ofNullable(x.getScore()).orElse(0D) > 0)
+                        .filter(x -> Optional.ofNullable(x.getEvaluationId()).orElse(0L) > 0)
+                        .filter(x -> highestScoreMap.getOrDefault(x.getEvaluationId(), -1D).doubleValue() == x.getScore())
+                        .collect(Collectors.groupingBy(ActivityEvaluationRecord::getEvaluationId, Collectors.mapping(x -> x.getCreateTime().getTime(), Collectors.toSet())))
+                        .entrySet().stream()
+                        .collect(Collectors.toMap(Map.Entry::getKey,
+                                x -> x.getValue().stream().mapToLong(Long::longValue).max().orElse(0L), (o, n) -> n));
+
+                // 最高分时间
+                long highScoreTime = highestScoreTimeMap.values().stream().mapToLong(Long::longValue).max().orElse(DateTime.now().getMillis());
+
+                // 若分总相同,最高分时间相同,则根据用户活动报名时间进行排序
+                Date registrationTime = records.stream()
+                        .filter(x -> Objects.isNull(x.getScore()))
+                        .min(Comparator.comparing(ActivityEvaluationRecord::getId))
+                        .map(ActivityEvaluationRecord::getCreateTime).orElse(DateTime.now().toDate());
+
+                // 查询用户声部评分记录
+                ActivityEvaluationRecord evaluationRecord = activityEvaluationRecordService.getOne(Wrappers.<ActivityEvaluationRecord>lambdaQuery()
+                        .eq(ActivityEvaluationRecord::getActivityId, activity.getId())
+                        .eq(ActivityEvaluationRecord::getUserId, userId)
+                        .eq(ActivityEvaluationRecord::getRankingMethod, activity.getRankingMethod())
+                );
+
+                if (Objects.isNull(evaluationRecord)) {
+
+                    MusicSheet musicSheet = musicSheetService.getById(activityEvaluation.getMusicSheetId());
+
+                    long subjectId = 0;
+                    if (Objects.nonNull(musicSheet)) {
+                        subjectId = Long.parseLong(musicSheet.getMusicSubject().split(",")[0]);
+                    }
+                    // 添加用户声部总分记录
+                    ActivityEvaluationRecord record = new ActivityEvaluationRecord();
+
+                    record.setActivityId(activityEvaluation.getActivityId());
+                    record.setUserId(userId);
+                    record.setEvaluationId(0L);
+                    // 声部ID
+                    record.setResourceId(subjectId);
+                    record.setRegistrationTime(registrationTime.getTime());
+                    record.setRankingMethod(activity.getRankingMethod());
+                    record.setTimes(0);
+                    // 最高分时间
+                    record.setCreateTime(new DateTime(highScoreTime).toDate());
+                    record.setScore(sumScore);
+
+                    save(record);
+                }
+
+                // 更新最高分记录
+                if (Objects.nonNull(evaluationRecord)
+                        && evaluationRecord.getScore() < sumScore) {
+
+                    ActivityEvaluationRecord record = new ActivityEvaluationRecord();
+
+                    record.setId(evaluationRecord.getId());
+                    record.setScore(sumScore);
+                    record.setCreateTime(new DateTime(highScoreTime).toDate());
+
+                    updateById(record);
+                }
+
+            });
+        }
+
     }
 
 
@@ -105,7 +221,41 @@ public class ActivityEvaluationRecordServiceImpl extends ServiceImpl<ActivityEva
         if (activityPlan == null) {
             throw new BizException("活动已结束");
         }
-		return baseMapper.queryRankingList(activityPlanId, activityEvaluationId, limit,activityPlan.getRankingRule());
+
+        List<ActivityRankingVo> activityRankings;
+        // 活动排名
+        if (ActivityRankingMethodEnum.TOTAL_SCORE == activityPlan.getRankingMethod()) {
+
+            // 总分排名, 根据声部ID进行查询
+            activityRankings = activityPlanMapper.selectActivityHighestScoreRankingInfo(activityPlanId, activityEvaluationId,
+                    1D, limit);
+
+            List<Long> userIds = activityRankings.stream()
+                    .map(ActivityRankingVo::getUserId).distinct().collect(Collectors.toList());
+
+            if (CollectionUtils.isNotEmpty(userIds)) {
+
+                Map<Long, Long> userSubjectMap = studentMapper.selectList(Wrappers.<Student>lambdaQuery().in(Student::getUserId, userIds)).stream()
+                        .filter(x -> Objects.nonNull(x.getSubjectId()))
+                        .collect(Collectors.toMap(Student::getUserId, x -> Long.parseLong(Optional.ofNullable(x.getSubjectId()).orElse("0").split(",")[0]), (o, n) -> n));
+
+
+                Map<Long, String> subjectNameMap = subjectService.findBySubjectByIdList(Lists.newArrayList(userSubjectMap.values())).stream()
+                        .collect(Collectors.toMap(Subject::getId, Subject::getName, (o, n) -> n));
+
+                for (ActivityRankingVo item : activityRankings) {
+
+                    item.setUserSubject(subjectNameMap.getOrDefault(userSubjectMap.get(item.getUserId()), ""));
+                }
+            }
+
+        } else {
+
+            // 单曲排名
+            activityRankings = baseMapper.queryRankingList(activityPlanId, activityEvaluationId, limit, activityPlan.getRankingRule());
+        }
+
+        return activityRankings;
 	}
 
 	@Override
@@ -114,7 +264,37 @@ public class ActivityEvaluationRecordServiceImpl extends ServiceImpl<ActivityEva
         if (activityPlan == null) {
             throw new BizException("活动已结束");
         }
-        return baseMapper.queryUserRanking(activityPlanId, activityEvaluationId, userId,activityPlan.getRankingRule());
+
+        // 用户排名信息
+        ActivityRankingVo userRanking;
+
+        if (ActivityRankingMethodEnum.TOTAL_SCORE == activityPlan.getRankingMethod()) {
+
+            // 总分排名
+            userRanking = activityPlanMapper.selectActivityHighestScoreUserRanking(activityPlanId, activityEvaluationId,
+                    1D, userId);
+
+            // 学生声部信息
+            Student student = studentMapper.selectOne(Wrappers.<Student>lambdaQuery().eq(Student::getUserId, userId));
+
+            long subjectId = 0;
+            if (Objects.nonNull(student.getSubjectId())) {
+                subjectId = Long.parseLong(student.getSubjectId().split(",")[0]);
+            }
+            Subject subject = subjectService.get(subjectId);
+
+            if (Objects.nonNull(userRanking)) {
+
+                userRanking.setUserSubject(Optional.ofNullable(subject).map(Subject::getName).orElse(""));
+            }
+
+        } else {
+
+            // 单曲排名
+            userRanking = baseMapper.queryUserRanking(activityPlanId, activityEvaluationId, userId, activityPlan.getRankingRule());
+        }
+
+        return userRanking;
 	}
 
     /**

+ 100 - 10
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ActivityEvaluationServiceImpl.java

@@ -1,5 +1,6 @@
 package com.yonge.cooleshow.biz.dal.service.impl;
 
+import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -15,6 +16,7 @@ import com.yonge.cooleshow.biz.dal.entity.ActivityEvaluation;
 import com.yonge.cooleshow.biz.dal.entity.ActivityPlan;
 import com.yonge.cooleshow.biz.dal.entity.ActivityPlanReward;
 import com.yonge.cooleshow.biz.dal.entity.Subject;
+import com.yonge.cooleshow.biz.dal.enums.activity.ActivityRankingMethodEnum;
 import com.yonge.cooleshow.biz.dal.service.ActivityEvaluationRecordService;
 import com.yonge.cooleshow.biz.dal.service.ActivityEvaluationService;
 import com.yonge.cooleshow.biz.dal.service.ActivityPlanRewardService;
@@ -26,9 +28,11 @@ import com.yonge.cooleshow.biz.dal.vo.ActivityMusicVo;
 import com.yonge.cooleshow.biz.dal.vo.ActivityRankingVo;
 import com.yonge.cooleshow.biz.dal.vo.MusicSheetVo;
 import com.yonge.cooleshow.biz.dal.vo.activity.ActivityTeacherWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.StatGroupWrapper;
 import com.yonge.cooleshow.common.enums.ActivityResourceEnum;
 import com.yonge.cooleshow.common.enums.ActivityTypeEnum;
 import com.yonge.cooleshow.common.enums.EStatus;
+import com.yonge.cooleshow.common.enums.YesOrNoEnum;
 import com.yonge.toolset.base.exception.BizException;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -68,7 +72,28 @@ public class ActivityEvaluationServiceImpl extends ServiceImpl<ActivityEvaluatio
 
     @Override
     public IPage<ActivityEvaluationVo> selectPage(IPage<ActivityEvaluationVo> page, ActivityEvaluationSearch query) {
-        return page.setRecords(baseMapper.selectPage(page, query));
+
+        ActivityPlan activity = activityPlanService.getById(query.getActivityId());
+        if (Objects.isNull(activity)) {
+            throw new BizException("无效的活动ID");
+        }
+
+        List<ActivityEvaluationVo> evaluationInfos = baseMapper.selectPage(page, query);
+        // 排序方式
+        if (ActivityTypeEnum.EVALUATION == activity.getActivityType()
+                && ActivityRankingMethodEnum.TOTAL_SCORE == activity.getRankingMethod()) {
+
+            // 按声部聚合数据返回
+            evaluationInfos = evaluationInfos.stream()
+                    .collect(Collectors.groupingBy(x -> Long.parseLong(x.getSubjectId().split(",")[0])))
+                    .entrySet().stream()
+                    .sorted(Map.Entry.comparingByKey())
+                    .flatMap(x -> x.getValue().stream())
+                    .collect(Collectors.toList());
+
+        }
+
+        return page.setRecords(evaluationInfos);
     }
 
     @Override
@@ -146,8 +171,9 @@ public class ActivityEvaluationServiceImpl extends ServiceImpl<ActivityEvaluatio
         activityContentIds.removeAll(oldMusicSheetIds);
 
         if ((oldMusicSheetIds.size() + activityContentIds.size()) > 10
+                && activity.getRankingMethod() == ActivityRankingMethodEnum.MUSIC_RANKING
                 && activityType == ActivityTypeEnum.EVALUATION) {
-
+            // 评测活动、曲目排名需要限制曲目数量
             throw new BizException("添加的曲目不能多余10首");
         }
         if (CollectionUtils.isNotEmpty(activityContentIds)) {
@@ -179,17 +205,59 @@ public class ActivityEvaluationServiceImpl extends ServiceImpl<ActivityEvaluatio
 
     @Override
     public List<ActivityMusicVo> getActivityMusic(Long activityPlanId, Long userId) {
+
+        // 活动曲目信息
         List<ActivityMusicVo> result = baseMapper.selectActivityMusic(activityPlanId, userId);
-        for (ActivityMusicVo activityMusicVo : result) {
-        	List<ActivityRankingVo> rankingList =  activityEvaluationRecordService.queryRankingList(activityPlanId, activityMusicVo.getEvaluationId(), 1);
-            if (rankingList != null && rankingList.size() > 0) {
-                activityMusicVo.setUserSubject(rankingList.get(0).getUserSubject());
-                activityMusicVo.setScore(rankingList.get(0).getScore());
-                activityMusicVo.setUserAvatar(rankingList.get(0).getUserAvatar());
-                activityMusicVo.setUsername(rankingList.get(0).getUsername());
-                activityMusicVo.setUserId(rankingList.get(0).getUserId());
+
+        // 活动信息
+        ActivityPlan activity = activityPlanService.getById(activityPlanId);
+        if (Objects.isNull(activity)) {
+            throw new BizException("无效的活动ID");
+        }
+
+        // 评测活动
+        if (ActivityTypeEnum.EVALUATION == activity.getActivityType()) {
+
+
+            // 单曲排名,计算每一个曲目的最高分
+            if (ActivityRankingMethodEnum.MUSIC_RANKING == activity.getRankingMethod()) {
+
+                result.parallelStream().forEach(item -> {
+
+                    List<ActivityRankingVo> rankingList =  activityEvaluationRecordService.queryRankingList(activityPlanId, item.getEvaluationId(), 1);
+                    if (rankingList != null && rankingList.size() > 0) {
+                        item.setUserSubject(rankingList.get(0).getUserSubject());
+                        item.setScore(rankingList.get(0).getScore());
+                        item.setUserAvatar(rankingList.get(0).getUserAvatar());
+                        item.setUsername(rankingList.get(0).getUsername());
+                        item.setUserId(rankingList.get(0).getUserId());
+                    }
+
+                });
+
+            }
+
+            // 总分排名
+            if (ActivityRankingMethodEnum.TOTAL_SCORE == activity.getRankingMethod()) {
+
+                Map<Long, Double> collect = Maps.newHashMap();
+                if (result.stream().anyMatch(x -> x.getJoin() == YesOrNoEnum.YES
+                        && Objects.nonNull(userId))) {
+
+                    collect = getBaseMapper().selectSubjectMusicHighestScoreStat(activity.getId(), userId).stream()
+                            .collect(Collectors.toMap(StatGroupWrapper::getId, StatGroupWrapper::getNumber, (o, n) -> n));
+                }
+
+                for (ActivityMusicVo item : result) {
+
+                    item.setScore(collect.getOrDefault(item.getEvaluationId(), 0D));
+                }
+
+
             }
+
         }
+
         return result;
     }
 
@@ -266,4 +334,26 @@ public class ActivityEvaluationServiceImpl extends ServiceImpl<ActivityEvaluatio
         return page.setRecords(wrappers);
     }
 
+    /**
+     * 批量更新评测曲目信息
+     *
+     * @param user SysUser
+     * @param info ActivityEvaluationDto
+     * @return int
+     */
+    @Transactional
+    @Override
+    public int batchUpdateEvaluationInfo(SysUser user, ActivityEvaluationDto info) {
+
+        List<ActivityEvaluationDto.EvaluationInfo> evaluationInfos = info.getEvaluationInfos();
+
+        List<ActivityEvaluation> evaluations = JSON.parseArray(JSON.toJSONString(evaluationInfos),
+                ActivityEvaluation.class);
+
+        // 批量更新
+        activityEvaluationService.updateBatchById(evaluations, 30);
+
+        return evaluationInfos.size();
+    }
+
 }

+ 93 - 28
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ActivityPlanEvaluationServiceImpl.java

@@ -1,6 +1,7 @@
 package com.yonge.cooleshow.biz.dal.service.impl;
 
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.yonge.cooleshow.biz.dal.dao.ActivityPlanDao;
 import com.yonge.cooleshow.biz.dal.dto.ActivityPlanDto;
 import com.yonge.cooleshow.biz.dal.dto.ActivityPlanRewardDto;
 import com.yonge.cooleshow.biz.dal.dto.req.OrderPayReq;
@@ -10,6 +11,7 @@ import com.yonge.cooleshow.biz.dal.entity.ActivityPlanReward;
 import com.yonge.cooleshow.biz.dal.entity.ActivityUserReward;
 import com.yonge.cooleshow.biz.dal.enums.GoodTypeEnum;
 import com.yonge.cooleshow.biz.dal.enums.OrderTypeEnum;
+import com.yonge.cooleshow.biz.dal.enums.activity.ActivityRankingMethodEnum;
 import com.yonge.cooleshow.biz.dal.service.*;
 import com.yonge.cooleshow.biz.dal.vo.ActivityPlanVo;
 import com.yonge.cooleshow.biz.dal.vo.ActivityRankingVo;
@@ -52,6 +54,8 @@ public class ActivityPlanEvaluationServiceImpl extends ServiceImpl<ActivityPlanE
 
     @Autowired
     private ActivityEvaluationService activityEvaluationService;
+    @Autowired
+    private ActivityPlanDao activityPlanMapper;
 
     @Override
     public boolean createOrUpdate(ActivityPlanDto activityPlan) {
@@ -86,7 +90,7 @@ public class ActivityPlanEvaluationServiceImpl extends ServiceImpl<ActivityPlanE
     }
 
     @Override
-    @Transactional(rollbackFor = Exception.class)
+    //@Transactional(rollbackFor = Exception.class)
     public void successActivity(Long activityId) {
         //关闭未付款订单
         closeActivity(activityId);
@@ -110,53 +114,114 @@ public class ActivityPlanEvaluationServiceImpl extends ServiceImpl<ActivityPlanE
         } catch (ParseException e) {
             e.printStackTrace();
         }
-        for (ActivityEvaluation activityEvaluation : activityEvaluationList) {
+        // 总分排名发奖流程
+        if (ActivityRankingMethodEnum.TOTAL_SCORE == detail.getRankingMethod()) {
 
-            List<ActivityRankingVo> activityRankingVos = activityEvaluationRecordService.queryRankingList(activityId,
-                                                              activityEvaluation.getId(),detail.getRanking());
-            for (int i = 0; i < activityRankingVos.size(); i++) {
+            // 查询活动获奖用户信息
+            List<ActivityRankingVo> rankingInfos = activityPlanMapper.selectActivityHighestScoreRankingInfo(detail.getId(), null,
+                    detail.getRankingScore(), 9999);
 
-                ActivityRankingVo activityRankingVo = activityRankingVos.get(i);
+            if (CollectionUtils.isEmpty(detail.getActivityRewardList())) {
+                // 活动未关联奖品,忽略后续流程
+                return;
+            }
 
-                List<ActivityPlanRewardDto> activityPlanRewardDtos = rewardMap.get(i + 1 + "");
-                if (CollectionUtils.isEmpty(activityPlanRewardDtos)) {
-                    continue;
-                }
+            // 活动关联奖品ID
+            List<Long> activityRewardIds = detail.getActivityRewardList().stream()
+                    .map(ActivityPlanReward::getId).collect(Collectors.toList());
 
-                // 活动关联奖品ID
-                List<Long> activityRewardIds = activityPlanRewardDtos.stream()
-                                   .map(ActivityPlanReward::getId).collect(Collectors.toList());
+            int ranking = 0;
+            for (ActivityRankingVo rankingInfo : rankingInfos) {
 
-                //给用户发放奖品 传入用户id和活动id
-                List<Long> collect = activityRewardService.sendReward(activityRankingVo.getUserId(), activityId, activityRewardIds, date);
+                // 推送发奖消息给用户
+                List<Long> collect = activityRewardService.sendReward(rankingInfo.getUserId(), activityId, activityRewardIds, date);
 
-                // 保存奖品发放记录
-                for (ActivityPlanRewardDto item : activityPlanRewardDtos) {
+                // 活动奖品信息
+                for (ActivityPlanRewardDto reward : detail.getActivityRewardList()) {
 
                     ActivityUserReward activityUserReward = new ActivityUserReward();
                     activityUserReward.setActivityId(activityId);
-                    activityUserReward.setBizId(activityEvaluation.getId());
-                    activityUserReward.setUserId(activityRankingVo.getUserId());
+                    activityUserReward.setBizId(rankingInfo.getResourceId());
+                    activityUserReward.setUserId(rankingInfo.getUserId());
                     activityUserReward.setRankingRule(detail.getRankingRule());
-                    activityUserReward.setRanking(i+1);
-                    activityUserReward.setGrantFlag(0);
-                    activityUserReward.setWinningTime(activityRankingVo.getJoinDate());
+                    activityUserReward.setRanking(ranking++);
+                    // 奖品发送状态
+                    activityUserReward.setGrantFlag(collect.contains(reward.getId()) ? 1 : 0);
+                    // 获取最高分时间
+                    activityUserReward.setWinningTime(rankingInfo.getJoinDate());
                     activityUserReward.setCreateTime(date);
                     activityUserReward.setUpdateTime(date);
-                    activityUserReward.setRewardId(item.getRewardId());
+                    activityUserReward.setRewardId(reward.getRewardId());
 
-                    // 奖品发送状态
-                    if (collect.contains(item.getId())) {
-                        activityUserReward.setGrantFlag(1);
-                    }
+                    // 添加发放记录
                     activityUserRewardList.add(activityUserReward);
                 }
 
+                // 若奖品数量超过1000,提交数据刷新缓存
+                if (activityUserRewardList.size() > 1000) {
+
+                    // 提交奖品发放记录
+                    activityUserRewardService.saveBatch(activityUserRewardList);
+
+                    // 清除缓存数据
+                    activityUserRewardList.clear();
+                }
+            }
+
+        } else {
+
+            // 曲目排名发奖流程
+            for (ActivityEvaluation activityEvaluation : activityEvaluationList) {
+
+                List<ActivityRankingVo> activityRankingVos = activityEvaluationRecordService.queryRankingList(activityId,
+                        activityEvaluation.getId(),detail.getRanking());
+                for (int i = 0; i < activityRankingVos.size(); i++) {
+
+                    ActivityRankingVo activityRankingVo = activityRankingVos.get(i);
+
+                    List<ActivityPlanRewardDto> activityPlanRewardDtos = rewardMap.get(i + 1 + "");
+                    if (CollectionUtils.isEmpty(activityPlanRewardDtos)) {
+                        continue;
+                    }
+
+                    // 活动关联奖品ID
+                    List<Long> activityRewardIds = activityPlanRewardDtos.stream()
+                            .map(ActivityPlanReward::getId).collect(Collectors.toList());
+
+                    //给用户发放奖品 传入用户id和活动id
+                    List<Long> collect = activityRewardService.sendReward(activityRankingVo.getUserId(), activityId, activityRewardIds, date);
+
+                    // 保存奖品发放记录
+                    for (ActivityPlanRewardDto item : activityPlanRewardDtos) {
+
+                        ActivityUserReward activityUserReward = new ActivityUserReward();
+                        activityUserReward.setActivityId(activityId);
+                        activityUserReward.setBizId(activityEvaluation.getId());
+                        activityUserReward.setUserId(activityRankingVo.getUserId());
+                        activityUserReward.setRankingRule(detail.getRankingRule());
+                        activityUserReward.setRanking(i+1);
+                        activityUserReward.setGrantFlag(0);
+                        activityUserReward.setWinningTime(activityRankingVo.getJoinDate());
+                        activityUserReward.setCreateTime(date);
+                        activityUserReward.setUpdateTime(date);
+                        activityUserReward.setRewardId(item.getRewardId());
+
+                        // 奖品发送状态
+                        if (collect.contains(item.getId())) {
+                            activityUserReward.setGrantFlag(1);
+                        }
+                        activityUserRewardList.add(activityUserReward);
+                    }
+
+                }
             }
+
         }
-        if (!CollectionUtils.isEmpty(activityUserRewardList)) {
+
+        if (CollectionUtils.isNotEmpty(activityUserRewardList)) {
             activityUserRewardService.saveBatch(activityUserRewardList);
         }
+
         //发完奖后将库存归还
         // activityPlanRewardService.recoveryReward(activityId);
     }

+ 183 - 34
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ActivityPlanServiceImpl.java

@@ -10,6 +10,8 @@ import com.google.common.collect.Maps;
 import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
 import com.yonge.cooleshow.auth.api.entity.SysUser;
 import com.yonge.cooleshow.biz.dal.dao.ActivityPlanDao;
+import com.yonge.cooleshow.biz.dal.dao.StudentDao;
+import com.yonge.cooleshow.biz.dal.dao.SubjectDao;
 import com.yonge.cooleshow.biz.dal.dto.ActivityPlanDto;
 import com.yonge.cooleshow.biz.dal.dto.ActivityPlanPayDto;
 import com.yonge.cooleshow.biz.dal.dto.ActivityPlanRewardDto;
@@ -23,10 +25,13 @@ import com.yonge.cooleshow.biz.dal.entity.ActivityPlan;
 import com.yonge.cooleshow.biz.dal.entity.ActivityPlanReward;
 import com.yonge.cooleshow.biz.dal.entity.ActivityRegistration;
 import com.yonge.cooleshow.biz.dal.entity.ActivityReward;
+import com.yonge.cooleshow.biz.dal.entity.Student;
+import com.yonge.cooleshow.biz.dal.entity.Subject;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.enums.EQueryOp;
 import com.yonge.cooleshow.biz.dal.enums.MK;
 import com.yonge.cooleshow.biz.dal.enums.MessageTypeEnum;
+import com.yonge.cooleshow.biz.dal.enums.activity.ActivityRankingMethodEnum;
 import com.yonge.cooleshow.biz.dal.service.ActivityEvaluationRecordService;
 import com.yonge.cooleshow.biz.dal.service.ActivityEvaluationService;
 import com.yonge.cooleshow.biz.dal.service.ActivityPlanEvaluationService;
@@ -35,9 +40,11 @@ import com.yonge.cooleshow.biz.dal.service.ActivityPlanService;
 import com.yonge.cooleshow.biz.dal.service.ActivityPlanStandardService;
 import com.yonge.cooleshow.biz.dal.service.ActivityRegistrationService;
 import com.yonge.cooleshow.biz.dal.service.ActivityRewardService;
+import com.yonge.cooleshow.biz.dal.service.SubjectService;
 import com.yonge.cooleshow.biz.dal.service.SysMessageService;
 import com.yonge.cooleshow.biz.dal.vo.ActivityMusicVo;
 import com.yonge.cooleshow.biz.dal.vo.ActivityPlanVo;
+import com.yonge.cooleshow.biz.dal.vo.ActivityRankingVo;
 import com.yonge.cooleshow.biz.dal.vo.MusicActivityVo;
 import com.yonge.cooleshow.biz.dal.vo.UserOrderDetailVo;
 import com.yonge.cooleshow.biz.dal.vo.activity.ActivityTeacherWrapper;
@@ -117,6 +124,10 @@ public class ActivityPlanServiceImpl extends ServiceImpl<ActivityPlanDao, Activi
 
     @Autowired
     private RedissonClient redissonClient;
+    @Autowired
+    private StudentDao studentMapper;
+    @Autowired
+    private SubjectService subjectService;
 
     //保存/更新拓展字段
     private static final Map<ActivityTypeEnum, Consumer<ActivityPlanDto>> saveOrUpdateExpand = new HashMap<>();
@@ -488,6 +499,11 @@ public class ActivityPlanServiceImpl extends ServiceImpl<ActivityPlanDao, Activi
     @Override
     public MusicActivityVo getActivityInfo(Long activityPlanId, SysUser user) {
         ActivityPlan activityPlan = this.getById(activityPlanId);
+
+        if (Objects.isNull(activityPlan)) {
+            throw new BizException("无效的活动ID");
+        }
+
         if (activityPlan.getActivityState() != 1) {
             activityPlan.setActivityState(0);
         }
@@ -501,9 +517,127 @@ public class ActivityPlanServiceImpl extends ServiceImpl<ActivityPlanDao, Activi
         }
 
 
-        // 活动曲目
-        List<ActivityMusicVo> activityMusicVoList = activityEvaluationService.getActivityMusic(activityPlanId, userId);
-        activityVo.setActivityMusicVoList(activityMusicVoList);
+        // 评测活动曲目信息
+        if (ActivityTypeEnum.EVALUATION == activityPlan.getActivityType()) {
+
+            // 活动曲目
+            List<ActivityMusicVo> activityMusicVoList = activityEvaluationService.getActivityMusic(activityPlanId, userId);
+            activityVo.setActivityMusicVoList(activityMusicVoList);
+
+            // 评测难度
+            String evaluationDifficulty = baseMapper.selectActivityPlanEvaluation(activityPlanId);
+            activityVo.setEvaluationDifficulty(evaluationDifficulty);
+
+            // 单曲排名
+            if (ActivityRankingMethodEnum.MUSIC_RANKING == activityPlan.getRankingMethod()) {
+
+                for (ActivityMusicVo item : activityVo.getActivityMusicVoList()) {
+
+                    if (StringUtils.isEmpty(item.getEvaluationDifficulty())) {
+
+                        item.setEvaluationDifficulty(evaluationDifficulty);
+                    }
+                }
+            }
+
+            // 计算评测活动声部最高分
+            if (ActivityRankingMethodEnum.TOTAL_SCORE == activityPlan.getRankingMethod()) {
+
+                Map<String, List<ActivityMusicVo>> collect = activityMusicVoList.stream()
+                        .collect(Collectors.groupingBy(x -> x.getSubjectId().split(",")[0]));
+
+                // 曲目难度
+                Map<Integer, String> difficultyMap = Maps.newHashMap();
+                difficultyMap.put(0, "BEGINNER");
+                difficultyMap.put(1, "ADVANCED");
+                difficultyMap.put(2, "PERFORMER");
+                // FIXME:临时增加声部曲目难度字段
+                for (Map.Entry<String, List<ActivityMusicVo>> entry : collect.entrySet()) {
+
+                    int index = 0;
+                    for (ActivityMusicVo item : entry.getValue()) {
+
+                        if (StringUtils.isEmpty(item.getEvaluationDifficulty())) {
+
+                            item.setEvaluationDifficulty(difficultyMap.getOrDefault(index, "PERFORMER"));
+                        }
+
+                        index += 1;
+                    }
+                }
+
+                // 计算声部评测最高分
+                Map<String, ActivityRankingVo> highestScoreMap = Maps.newConcurrentMap();
+                collect.entrySet().parallelStream().forEach(item -> {
+
+                    // 单声部计算用户总分排名用户信息
+                    List<ActivityRankingVo> records = getBaseMapper().selectActivityHighestScoreRankingInfo(activityPlanId, Long.parseLong(item.getKey()),
+                            1D, 1);
+
+                    if (CollectionUtils.isNotEmpty(records)) {
+
+                        highestScoreMap.put(item.getKey(), records.get(0));
+                    }
+                });
+
+                // 用户学生身份声部信息
+                List<Long> userIds = highestScoreMap.values().stream()
+                        .map(ActivityRankingVo::getUserId).distinct().collect(Collectors.toList());
+
+                Map<Long, Long> userSubjectMap = Maps.newHashMap();
+                Map<Long, String> subjectNameMap = Maps.newHashMap();
+                if (CollectionUtils.isNotEmpty(userIds)) {
+
+                    userSubjectMap = studentMapper.selectList(Wrappers.<Student>lambdaQuery().in(Student::getUserId, userIds)).stream()
+                            .filter(x -> Objects.nonNull(x.getSubjectId()))
+                            .collect(Collectors.toMap(Student::getUserId, x -> Long.parseLong(Optional.ofNullable(x.getSubjectId()).orElse("0").split(",")[0]), (o, n) -> n));
+
+
+                    subjectNameMap = subjectService.findBySubjectByIdList(Lists.newArrayList(userSubjectMap.values())).stream()
+                            .collect(Collectors.toMap(Subject::getId, Subject::getName, (o, n) -> n));
+                }
+
+
+                List<MusicActivityVo.SubjectInfo> subjectInfos = Lists.newArrayList();
+                ActivityMusicVo musicVo;
+                ActivityRankingVo ranking;
+                for (Map.Entry<String, List<ActivityMusicVo>> entry : collect.entrySet()) {
+
+                    if (CollectionUtils.isEmpty(entry.getValue())) {
+                        continue;
+                    }
+
+                    musicVo = entry.getValue().get(0);
+
+                    MusicActivityVo.SubjectInfo subjectInfo = MusicActivityVo.SubjectInfo.builder()
+                            .subjectId(musicVo.getSubjectId().split(",")[0])
+                            .subjectName(musicVo.getMusicSubject())
+                            .join(entry.getValue().stream().anyMatch(x -> x.getJoin() == YesOrNoEnum.YES) ? 1 : 0)
+                            .userId(0L)
+                            .username("")
+                            .userAvatar("")
+                            .userSubject("")
+                            .score(0D)
+                            .musicNums(entry.getValue().size())
+                            .build();
+                    subjectInfos.add(subjectInfo);
+
+                    // 最高分记录
+                    if (highestScoreMap.containsKey(entry.getKey())) {
+
+                        ranking = highestScoreMap.get(entry.getKey());
+
+                        subjectInfo.userId(ranking.getUserId())
+                                .username(ranking.getUsername())
+                                .userAvatar(ranking.getUserAvatar())
+                                .userSubject(subjectNameMap.getOrDefault(userSubjectMap.get(ranking.getUserId()), ""))
+                                .score(ranking.getScore());
+                    }
+                }
+
+                activityVo.setSubjectInfos(subjectInfos);
+            }
+        }
 
         // 报名状态
         activityVo.setJoin(activityRegistrationService.getRegistration(userId, activityPlanId));
@@ -512,10 +646,6 @@ public class ActivityPlanServiceImpl extends ServiceImpl<ActivityPlanDao, Activi
         List<ActivityReward> activityRewardList = activityRewardService.getActivityReward(activityPlanId);
         activityVo.setActivityRewardList(activityRewardList);
 
-        // 评测难度
-        String evaluationDifficulty = baseMapper.selectActivityPlanEvaluation(activityPlanId);
-
-        activityVo.setEvaluationDifficulty(evaluationDifficulty);
         return activityVo;
     }
 
@@ -634,34 +764,53 @@ public class ActivityPlanServiceImpl extends ServiceImpl<ActivityPlanDao, Activi
         // 方法调整为异常执行,预防HTTP接口调用返回超时响应
         ThreadPool.getExecutor().submit(() -> {
 
-            List<ActivityPlan> list = baseMapper.activityState();
-            for (ActivityPlan plan : list) {
-                DistributedLock.of(redissonClient)
-                        .runIfLockToFunction(CacheNameEnum.LOCK_ACTIVITY_STOCK.getRedisKey(plan.getId())
-                                , (id) -> {
-                                    ActivityPlan activityPlan = getById(id);
-                                    if (activityPlan.getActivityState() == 0) {
-                                        activityPlan.setActivityState(1);
-                                        baseMapper.updateById(activityPlan);
-
-                                        //开始活动
-                                        Consumer<Long> afterFunction = startActivity.get(activityPlan.getActivityType());
-                                        if (!Objects.isNull(afterFunction)) {
-                                            afterFunction.accept(activityPlan.getId());
-                                        }
-                                    } else {
-                                        activityPlan.setActivityState(0);
-                                        activityPlan.setRewardFlag(1);
-                                        baseMapper.updateById(activityPlan);
-
-                                        //完成活动
-                                        Consumer<Long> afterFunction = successActivity.get(activityPlan.getActivityType());
-                                        if (!Objects.isNull(afterFunction)) {
-                                            afterFunction.accept(activityPlan.getId());
+            RLock lock = redissonClient.getLock(CacheNameEnum.LOCK_EVALUATION_CRON.getCode());
+            try {
+                if (lock.isLocked()) {
+                    log.warn("activityState {}, lockName={}", DateTime.now().toString(MK.TIME_PATTERN), lock.getName());
+                    return;
+                }
+
+                // 增加达标活动计算同步标识,前一个请求未执行完时忽略后续执行请求
+                lock.lock();
+
+                List<ActivityPlan> list = baseMapper.activityState();
+                for (ActivityPlan plan : list) {
+                    DistributedLock.of(redissonClient)
+                            .runIfLockToFunction(CacheNameEnum.LOCK_ACTIVITY_STOCK.getRedisKey(plan.getId())
+                                    , (id) -> {
+                                        ActivityPlan activityPlan = getById(id);
+                                        if (activityPlan.getActivityState() == 0) {
+                                            activityPlan.setActivityState(1);
+                                            baseMapper.updateById(activityPlan);
+
+                                            //开始活动
+                                            Consumer<Long> afterFunction = startActivity.get(activityPlan.getActivityType());
+                                            if (!Objects.isNull(afterFunction)) {
+                                                afterFunction.accept(activityPlan.getId());
+                                            }
+                                        } else {
+                                            activityPlan.setActivityState(0);
+                                            activityPlan.setRewardFlag(1);
+                                            baseMapper.updateById(activityPlan);
+
+                                            //完成活动
+                                            Consumer<Long> afterFunction = successActivity.get(activityPlan.getActivityType());
+                                            if (!Objects.isNull(afterFunction)) {
+                                                afterFunction.accept(activityPlan.getId());
+                                            }
                                         }
-                                    }
-                                    return true;
-                                }, plan.getId(), 10l);
+                                        return true;
+                                    }, plan.getId(), 10l);
+                }
+
+            } catch (Exception e) {
+                log.error("activityState time={}", DateTime.now().toString(MK.TIME_PATTERN), e);
+            } finally {
+                // 释放锁
+                if (lock.isLocked() && lock.isHeldByCurrentThread()) {
+                    lock.unlock(); // 删除同步标识
+                }
             }
 
         });

+ 59 - 11
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ActivityRegistrationServiceImpl.java

@@ -1,13 +1,20 @@
 package com.yonge.cooleshow.biz.dal.service.impl;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.yonge.cooleshow.biz.dal.dao.ActivityRegistrationDao;
+import com.yonge.cooleshow.biz.dal.dao.SubjectDao;
 import com.yonge.cooleshow.biz.dal.dto.search.ActivityRegistrationSearch;
 import com.yonge.cooleshow.biz.dal.entity.ActivityEvaluationRecord;
+import com.yonge.cooleshow.biz.dal.entity.ActivityPlan;
 import com.yonge.cooleshow.biz.dal.entity.ActivityRegistration;
+import com.yonge.cooleshow.biz.dal.entity.Subject;
+import com.yonge.cooleshow.biz.dal.enums.activity.ActivityRankingMethodEnum;
 import com.yonge.cooleshow.biz.dal.service.ActivityEvaluationRecordService;
+import com.yonge.cooleshow.biz.dal.service.ActivityPlanService;
 import com.yonge.cooleshow.biz.dal.service.ActivityRegistrationService;
+import com.yonge.cooleshow.biz.dal.service.SubjectService;
 import com.yonge.cooleshow.biz.dal.vo.ActivityRegistrationVo;
 import com.yonge.cooleshow.common.enums.YesOrNoEnum;
 import lombok.extern.slf4j.Slf4j;
@@ -17,6 +24,7 @@ import org.springframework.stereotype.Service;
 
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -26,6 +34,10 @@ public class ActivityRegistrationServiceImpl extends ServiceImpl<ActivityRegistr
 
     @Autowired
     private ActivityEvaluationRecordService activityEvaluationRecordService;
+    @Autowired
+    private ActivityPlanService activityPlanService;
+    @Autowired
+    private SubjectService subjectService;
 	@Override
     public ActivityRegistrationVo detail(Long id) {
         return baseMapper.detail(id);
@@ -40,22 +52,58 @@ public class ActivityRegistrationServiceImpl extends ServiceImpl<ActivityRegistr
         List<Long> userIds = activityUserInfos.stream()
                 .map(ActivityRegistration::getUserId).distinct().collect(Collectors.toList());
 
+        ActivityPlan activity = activityPlanService.getById(query.getActivityId());
         // 查询用户活动评测获排名最高记录
-        if (CollectionUtils.isNotEmpty(userIds)) {
+        if (CollectionUtils.isNotEmpty(userIds) && Objects.nonNull(activity)) {
+
+            if (ActivityRankingMethodEnum.TOTAL_SCORE == activity.getRankingMethod()) {
+
+                // 总分排名
+                Map<Long, ActivityEvaluationRecord> collect = activityEvaluationRecordService.list(Wrappers.<ActivityEvaluationRecord>lambdaQuery()
+                        .eq(ActivityEvaluationRecord::getActivityId, activity.getId())
+                        .in(ActivityEvaluationRecord::getUserId, userIds)
+                        .eq(ActivityEvaluationRecord::getRankingMethod, activity.getRankingMethod().getCode())).stream()
+                        .collect(Collectors.toMap(ActivityEvaluationRecord::getUserId, Function.identity(), (o, n) -> n));
+
+                // 声部信息
+                List<Long> subjectIds = collect.values().stream()
+                        .map(ActivityEvaluationRecord::getResourceId)
+                        .filter(Objects::nonNull).distinct().collect(Collectors.toList());
+
+                Map<Long, String> subjectNameMap = subjectService.findBySubjectByIdList(subjectIds).stream()
+                        .collect(Collectors.toMap(Subject::getId, Subject::getName, (o, n) -> n));
+
+                ActivityEvaluationRecord record;
+                for (ActivityRegistrationVo item : activityUserInfos) {
+
+                    if (collect.containsKey(item.getUserId())) {
+
+                        record = collect.get(item.getUserId());
+                        // 重置当前时间为用户最高排名时间
+                        item.score(record.getScore())
+                                .subjectName(subjectNameMap.getOrDefault(record.getResourceId(), ""))
+                                .scoreTime(record.getCreateTime());
+
+                    }
+                }
+
+            } else {
 
-            Map<Long, ActivityEvaluationRecord> collect = activityEvaluationRecordService.queryActivityUserHighestRankingInfo(query.getActivityId(), userIds).stream()
-                    .collect(Collectors.toMap(ActivityEvaluationRecord::getUserId, Function.identity(), (o, n) -> n));
+                // 单曲排名
+                Map<Long, ActivityEvaluationRecord> collect = activityEvaluationRecordService.queryActivityUserHighestRankingInfo(query.getActivityId(), userIds).stream()
+                        .collect(Collectors.toMap(ActivityEvaluationRecord::getUserId, Function.identity(), (o, n) -> n));
 
-            ActivityEvaluationRecord record;
-            for (ActivityRegistrationVo item : activityUserInfos) {
+                ActivityEvaluationRecord record;
+                for (ActivityRegistrationVo item : activityUserInfos) {
 
-                if (collect.containsKey(item.getUserId())) {
+                    if (collect.containsKey(item.getUserId())) {
 
-                    record = collect.get(item.getUserId());
-                    // 重置当前时间为用户最高排名时间
-                    item.score(record.getScore())
-                            .times(record.getTimes())
-                            .setCreateTime(record.getCreateTime());
+                        record = collect.get(item.getUserId());
+                        // 重置当前时间为用户最高排名时间
+                        item.score(record.getScore())
+                                .times(record.getTimes())
+                                .setCreateTime(record.getCreateTime());
+                    }
                 }
             }
         }

+ 5 - 3
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ActivityRewardServiceImpl.java

@@ -19,6 +19,7 @@ import com.yonge.cooleshow.biz.dal.entity.ActivityRewardChangeStock;
 import com.yonge.cooleshow.biz.dal.entity.CouponInfo;
 import com.yonge.cooleshow.biz.dal.entity.PianoRoomChangeRecord;
 import com.yonge.cooleshow.biz.dal.enums.*;
+import com.yonge.cooleshow.biz.dal.enums.activity.ActivityRankingMethodEnum;
 import com.yonge.cooleshow.biz.dal.mapper.CouponInfoMapper;
 import com.yonge.cooleshow.biz.dal.service.ActivityPlanRewardService;
 import com.yonge.cooleshow.biz.dal.service.ActivityPlanService;
@@ -39,8 +40,6 @@ import com.yonge.toolset.payment.util.DistributedLock;
 import com.yonge.toolset.thirdparty.message.MessageSenderPluginContext;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.MapUtils;
-import org.redisson.RedissonMultiLock;
-import org.redisson.api.RLock;
 import org.redisson.api.RedissonClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -281,7 +280,10 @@ public class ActivityRewardServiceImpl extends ServiceImpl<ActivityRewardDao, Ac
 
             // 扣减奖品库存
             int update = activityPlanRewardService.reduceStock(activityId, item.getId());
-            if (update == 0) {
+
+            // 排除总分排名用户库存限制
+            if (ActivityRankingMethodEnum.TOTAL_SCORE != activityPlan.getRankingMethod()
+                    && update == 0) {
                 continue;
             }
 

+ 122 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImUserFriendServiceImpl.java

@@ -1,6 +1,9 @@
 package com.yonge.cooleshow.biz.dal.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
 import com.yonge.cooleshow.auth.api.entity.SysUser;
@@ -11,12 +14,21 @@ import com.yonge.cooleshow.biz.dal.dao.TeacherDao;
 import com.yonge.cooleshow.biz.dal.dto.BasicUserInfo;
 import com.yonge.cooleshow.biz.dal.entity.ImUserFriend;
 import com.yonge.cooleshow.biz.dal.enums.MK;
+import com.yonge.cooleshow.biz.dal.mapper.SysUserMapper;
 import com.yonge.cooleshow.biz.dal.service.ImUserFriendService;
+import com.yonge.cooleshow.biz.dal.wrapper.im.CustomerService;
+import com.yonge.toolset.base.exception.BizException;
+import io.rong.messages.BaseMessage;
+import io.rong.messages.FileMessage;
+import io.rong.messages.ImgMessage;
+import io.rong.messages.ImgTextMessage;
 import io.rong.messages.TxtMessage;
 import io.rong.models.message.PrivateMessage;
 import io.rong.models.message.PushExt;
 import io.rong.models.response.ResponseResult;
 import lombok.extern.slf4j.Slf4j;
+import net.coobird.thumbnailator.Thumbnails;
+import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -24,6 +36,11 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.URL;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
@@ -48,6 +65,8 @@ public class ImUserFriendServiceImpl extends ServiceImpl<ImUserFriendDao, ImUser
     private SysUserFeignService sysUserFeignService;
     @Autowired
     private CustomerServiceConfig customerServiceConfig;
+    @Autowired
+    private SysUserMapper sysUserMapper;
 
     @Override
     public ImUserFriendDao getDao() {
@@ -168,5 +187,108 @@ public class ImUserFriendServiceImpl extends ServiceImpl<ImUserFriendDao, ImUser
         return CollectionUtils.size(friendIds);
     }
 
+    /**
+     * 发送系统客服消息
+     *
+     * @param sender 发送者
+     * @param info   CustomerService.NotifyMessage
+     */
+    @Override
+    public void sendCustomerServiceNotifyMessage(String sender, CustomerService.NotifyMessage info) {
+
+        // 发送者信息
+        com.yonge.cooleshow.biz.dal.entity.SysUser senderUser = sysUserMapper.selectOne(Wrappers.<com.yonge.cooleshow.biz.dal.entity.SysUser>lambdaQuery()
+                .eq(com.yonge.cooleshow.biz.dal.entity.SysUser::getPhone, sender));
+
+        if (Objects.isNull(senderUser)) {
+            throw new BizException("无效的客服联系方式");
+        }
+
+        LambdaQueryWrapper<com.yonge.cooleshow.biz.dal.entity.SysUser> wrapper = Wrappers.<com.yonge.cooleshow.biz.dal.entity.SysUser>lambdaQuery()
+                .select(com.yonge.cooleshow.biz.dal.entity.SysUser::getId,
+                        com.yonge.cooleshow.biz.dal.entity.SysUser::getUsername)
+                .in(com.yonge.cooleshow.biz.dal.entity.SysUser::getPhone, info.getReceives());
+
+        wrapper.last("LIMIT 1000");
+        // 接收者信息
+        List<com.yonge.cooleshow.biz.dal.entity.SysUser> sysUsers = sysUserMapper.selectList(wrapper);
+
+        // 发送图片消息
+        ImgMessage imgMessage = new ImgMessage(imageToBase64(info.getImgMessage(), "png"), "", info.getImgUrl());
+
+        // 发送文本消息
+        TxtMessage txtMessage = new TxtMessage(info.getTxtMessage(), "");
+
+        List<BaseMessage> messages = Lists.newArrayList(imgMessage, txtMessage);
+
+        // 拓展消息
+        PushExt pushExt = PushExt.build(info.getTitle(), 1,
+                new PushExt.HW("channelId", "NORMAL"), new PushExt.VIVO("1"),
+                new PushExt.APNs("", ""),
+                new PushExt.OPPO(""));
+
+        // 分批次发送用户消息
+        List<String> receiveUserIds = sysUsers.stream()
+                .map(com.yonge.cooleshow.biz.dal.entity.SysUser::getId)
+                .map(String::valueOf)
+                .collect(Collectors.toList());
+
+        String senderId = String.valueOf(senderUser.getId());
+        PrivateMessage privateMessage;
+        ResponseResult privateResult;
+        for (List<String> item : Lists.partition(receiveUserIds, 100)) {
+
+            try {
+
+                for (BaseMessage message : messages) {
+
+                    // 发送用户IM通知消息
+                    privateMessage = new PrivateMessage()
+                            .setSenderId(senderId)
+                            .setTargetId(item.toArray(new String[0]))
+                            .setObjectName(message.getType())
+                            .setContent(message)
+                            .setPushExt(pushExt)
+                            .setIsIncludeSender(0);
+
+                    privateResult = RongCloudConfig.rongCloud.message.msgPrivate.send(privateMessage);
+                    log.info("sendCustomerServiceNotifyMessage senderId={}, ret={}", senderId, privateResult.getCode());
+                }
+
+            } catch (Exception e) {
+                log.error("sendCustomerServiceNotifyMessage senderId={}", senderId, e);
+            }
+
+        }
+
+    }
+
+    public static String imageToBase64(String imgMessage, String suffix) {
+
+        Base64 encoder = new Base64();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            URL url = new URL(imgMessage);
+            BufferedImage bufferedImage = Thumbnails.of(url).scale(0.1f).outputQuality(0.25f).asBufferedImage();
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            ImageIO.write(bufferedImage, suffix, outputStream);
+
+            byte[] bytes = outputStream.toByteArray();
+            outputStream.close();
+            int length = bytes.length;
+            //文件大小不能超过393216 Bytes
+            if (length > 393216) {
+                throw new BizException("文件过大请调整 像素 或 文件大小");
+            }
+
+            ImageIO.write(bufferedImage, suffix, baos);
+        } catch (IOException e) {
+            log.error("imageToBase64", e);
+        }
+        return new String(encoder.encode((baos.toByteArray())));
+    }
+
+
+
 }
 

+ 42 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/TeacherServiceImpl.java

@@ -15,6 +15,7 @@ import javax.annotation.Resource;
 
 import com.google.common.collect.Lists;
 import com.yonge.cooleshow.biz.dal.wrapper.StatGroupWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.teacher.TeacherWrapper;
 import org.redisson.api.RMap;
 import org.redisson.api.RedissonClient;
 import org.slf4j.Logger;
@@ -722,4 +723,45 @@ public class TeacherServiceImpl extends ServiceImpl<TeacherDao, Teacher> impleme
         return teacherInfo;
     }
 
+    /**
+     * 老师统计指标信息
+     *
+     * @param userId 用户ID
+     * @return TeacherWrapper.TeacherStatInfo
+     */
+    @Override
+    public TeacherWrapper.TeacherStatInfo findTeacherStatInfoById(Long userId) {
+
+        TeacherWrapper.TeacherStatInfo wrapper = TeacherWrapper.TeacherStatInfo.builder()
+                .fansNum(0)
+                .studentNums(0)
+                .starGrade(0)
+                .expTime(0)
+                .unExpTime(0)
+                .musicAlbumNum(0)
+                .musicSheetNum(0)
+                .build();
+
+        // 查询老师统计指标
+        TeacherTotal total = totalService.getTotalById(userId);
+        if (Objects.nonNull(total)) {
+
+            wrapper.starGrade(total.getStarGrade().intValue())
+                    .fansNum(total.getFansNum())
+                    .expTime(total.getExpTime())
+                    .unExpTime(total.getUnExpTime())
+                    .musicAlbumNum(total.getMusicAlbumNum())
+                    .musicSheetNum(total.getMusicSheetNum());
+        }
+
+        // 老师学生端人数统计
+        List<Long> teacherIds = Lists.newArrayList(userId);
+        Map<Long, Integer> studentNumsMap = getBaseMapper().selectTeacherStudentNumberStatInfo(teacherIds).stream()
+                .collect(Collectors.toMap(StatGroupWrapper::getId, StatGroupWrapper::getTotal, (o, n) -> n));
+
+        wrapper.setStudentNums(studentNumsMap.getOrDefault(userId, 0));
+
+        return wrapper;
+    }
+
 }

+ 11 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/ActivityEvaluationVo.java

@@ -26,6 +26,9 @@ public class ActivityEvaluationVo extends ActivityEvaluation{
 	@ApiModelProperty(value = "销售价格")
 	private BigDecimal musicPrice;
 
+	@ApiModelProperty(value = "曲目声部ID")
+	private String subjectId;
+
 	public String getMusicSheetName() {
 		return musicSheetName;
 	}
@@ -65,4 +68,12 @@ public class ActivityEvaluationVo extends ActivityEvaluation{
 	public void setMusicPrice(BigDecimal musicPrice) {
 		this.musicPrice = musicPrice;
 	}
+
+	public String getSubjectId() {
+		return subjectId;
+	}
+
+	public void setSubjectId(String subjectId) {
+		this.subjectId = subjectId;
+	}
 }

+ 70 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/ActivityMusicVo.java

@@ -47,6 +47,18 @@ public class ActivityMusicVo {
     @ApiModelProperty("最高分用户声部")
     private String userSubject;
 
+    @ApiModelProperty("声部ID")
+    private String subjectId;
+
+    @ApiModelProperty("作曲人")
+    private String composer;
+
+    @ApiModelProperty("曲目图片")
+    private String musicImage;
+
+    @ApiModelProperty("评测难度 BEGINNER 入门级 ADVANCED 进阶级 PERFORMER 大师级")
+    private String  evaluationDifficulty;
+
     public Long getUserId() {
         return userId;
     }
@@ -126,4 +138,62 @@ public class ActivityMusicVo {
     public void setUserSubject(String userSubject) {
         this.userSubject = userSubject;
     }
+
+    public String getSubjectId() {
+        return subjectId;
+    }
+
+    public void setSubjectId(String subjectId) {
+        this.subjectId = subjectId;
+    }
+
+    public String getComposer() {
+        return composer;
+    }
+
+    public void setComposer(String composer) {
+        this.composer = composer;
+    }
+
+    public String getEvaluationDifficulty() {
+        return evaluationDifficulty;
+    }
+
+    public void setEvaluationDifficulty(String evaluationDifficulty) {
+        this.evaluationDifficulty = evaluationDifficulty;
+    }
+
+    public String getMusicImage() {
+        return musicImage;
+    }
+
+    public void setMusicImage(String musicImage) {
+        this.musicImage = musicImage;
+    }
+
+
+    public ActivityMusicVo score(double score) {
+        this.score = score;
+        return this;
+    }
+
+    public ActivityMusicVo userId(Long userId) {
+        this.userId = userId;
+        return this;
+    }
+
+    public ActivityMusicVo userAvatar(String userAvatar) {
+        this.userAvatar = userAvatar;
+        return this;
+    }
+
+    public ActivityMusicVo username(String username) {
+        this.username = username;
+        return this;
+    }
+
+    public ActivityMusicVo userSubject(String userSubject) {
+        this.userSubject = userSubject;
+        return this;
+    }
 }

+ 11 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/ActivityRankingVo.java

@@ -27,6 +27,9 @@ public class ActivityRankingVo {
     @ApiModelProperty("次数")
     private int times;
 
+	@ApiModelProperty("资源ID")
+	private Long resourceId;
+
 	public double getScore() {
 		return score;
 	}
@@ -82,4 +85,12 @@ public class ActivityRankingVo {
 	public void setTimes(int times) {
 		this.times = times;
 	}
+
+	public Long getResourceId() {
+		return resourceId;
+	}
+
+	public void setResourceId(Long resourceId) {
+		this.resourceId = resourceId;
+	}
 }

+ 38 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/ActivityRegistrationVo.java

@@ -1,9 +1,13 @@
 package com.yonge.cooleshow.biz.dal.vo;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import com.yonge.cooleshow.biz.dal.entity.ActivityRegistration;
+import com.yonge.cooleshow.biz.dal.enums.MK;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
+import java.util.Date;
+
 /**
  * @Author: liweifan
  * @Data: 2022-08-04 10:19:49
@@ -39,9 +43,17 @@ public class ActivityRegistrationVo extends ActivityRegistration{
 	// 用户高最排名信息
 	@ApiModelProperty("最高排名分数")
 	private Double score;
+	
 	@ApiModelProperty("最高排名次数")
 	private Integer times;
 
+	@ApiModelProperty("曲子声部")
+	private String subjectName;
+
+	@ApiModelProperty("最高分时间")
+	@JsonFormat(pattern = MK.TIME_PATTERN, timezone = MK.TIME_ZONE)
+	private Date scoreTime;
+
 	public String getRealName() {
 		return realName;
 	}
@@ -122,6 +134,22 @@ public class ActivityRegistrationVo extends ActivityRegistration{
 		this.times = times;
 	}
 
+	public String getSubjectName() {
+		return subjectName;
+	}
+
+	public void setSubjectName(String subjectName) {
+		this.subjectName = subjectName;
+	}
+
+	public Date getScoreTime() {
+		return scoreTime;
+	}
+
+	public void setScoreTime(Date scoreTime) {
+		this.scoreTime = scoreTime;
+	}
+
 	public ActivityRegistrationVo score(Double score) {
 		this.score = score;
 		return this;
@@ -131,4 +159,14 @@ public class ActivityRegistrationVo extends ActivityRegistration{
 		this.times = times;
 		return this;
 	}
+
+	public ActivityRegistrationVo subjectName(String subjectName) {
+		this.subjectName = subjectName;
+		return this;
+	}
+
+	public ActivityRegistrationVo scoreTime(Date scoreTime) {
+		this.scoreTime = scoreTime;
+		return this;
+	}
 }

+ 79 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/MusicActivityVo.java

@@ -5,7 +5,12 @@ import com.yonge.cooleshow.biz.dal.entity.ActivityReward;
 import com.yonge.cooleshow.common.enums.YesOrNoEnum;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
 
+import java.io.Serializable;
 import java.util.List;
 
 /**
@@ -29,6 +34,9 @@ public class MusicActivityVo extends ActivityPlan {
     @ApiModelProperty("是否已经报名参与 0 :否 1:是")
     private YesOrNoEnum join;
 
+    @ApiModelProperty("活动声部信息")
+    private List<SubjectInfo> subjectInfos;
+
     public YesOrNoEnum getJoin() {
         return join;
     }
@@ -60,4 +68,75 @@ public class MusicActivityVo extends ActivityPlan {
     public void setEvaluationDifficulty(String evaluationDifficulty) {
         this.evaluationDifficulty = evaluationDifficulty;
     }
+
+    public List<SubjectInfo> getSubjectInfos() {
+        return subjectInfos;
+    }
+
+    public void setSubjectInfos(List<SubjectInfo> subjectInfos) {
+        this.subjectInfos = subjectInfos;
+    }
+
+    /**
+     * 活动曲目声部信息
+     */
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class SubjectInfo implements Serializable {
+
+        @ApiModelProperty("声部ID")
+        private String subjectId;
+
+        @ApiModelProperty("声部名称")
+        private String subjectName;
+
+        @ApiModelProperty("是否已经报名参与 ")
+        private Integer join;
+
+        @ApiModelProperty("最高分")
+        private double score;
+
+        @ApiModelProperty("最高分的用户id")
+        private Long userId;
+
+        @ApiModelProperty("最高分用户头像")
+        private String userAvatar;
+
+        @ApiModelProperty("最高分用户声部")
+        private String userSubject;
+
+        @ApiModelProperty("最高分用户名")
+        private String username;
+
+        @ApiModelProperty("声部曲目数")
+        private Integer musicNums;
+
+
+        public SubjectInfo score(double score) {
+            this.score = score;
+            return this;
+        }
+
+        public SubjectInfo userId(Long userId) {
+            this.userId = userId;
+            return this;
+        }
+
+        public SubjectInfo userAvatar(String userAvatar) {
+            this.userAvatar = userAvatar;
+            return this;
+        }
+
+        public SubjectInfo username(String username) {
+            this.username = username;
+            return this;
+        }
+
+        public SubjectInfo userSubject(String userSubject) {
+            this.userSubject = userSubject;
+            return this;
+        }
+    }
 }

+ 39 - 3
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/MyFens.java

@@ -3,19 +3,25 @@ package com.yonge.cooleshow.biz.dal.vo;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.yonge.cooleshow.biz.dal.enums.GenderEnum;
 import com.yonge.cooleshow.biz.dal.enums.MK;
-import com.yonge.cooleshow.common.entity.BaseEntity;
 import com.yonge.cooleshow.common.enums.YesOrNoEnum;
+import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import org.joda.time.DateTime;
+import org.joda.time.Period;
+import org.joda.time.PeriodType;
 
+import java.io.Serializable;
 import java.util.Date;
+import java.util.Objects;
 
 /**
  * @Author: cy
  * @Date: 2022/5/12
  */
-public class MyFens extends BaseEntity {
+@ApiModel("老师粉丝响应信息")
+public class MyFens implements Serializable {
 
-    @ApiModelProperty("昵称")
+    @ApiModelProperty("用户编号")
     private String userId;
     @ApiModelProperty("昵称")
     private String userName;
@@ -37,6 +43,12 @@ public class MyFens extends BaseEntity {
     @JsonFormat(pattern = MK.TIME_PATTERN, timezone = MK.TIME_ZONE)
     private Date starTime;
 
+    @ApiModelProperty("出生日期")
+    private Date birthdate;
+
+    @ApiModelProperty("出生日期")
+    private Integer age;
+
     public String getSubjectName() {
         return subjectName;
     }
@@ -112,4 +124,28 @@ public class MyFens extends BaseEntity {
     public void setStarTime(Date starTime) {
         this.starTime = starTime;
     }
+
+    public void setGender(GenderEnum gender) {
+        this.gender = gender;
+    }
+
+    public Date getBirthdate() {
+        return birthdate;
+    }
+
+    public void setBirthdate(Date birthdate) {
+        this.birthdate = birthdate;
+    }
+
+    public Integer getAge() {
+        if (Objects.nonNull(getBirthdate())) {
+
+            return new Period(new DateTime(getBirthdate()), DateTime.now(), PeriodType.years()).getYears();
+        }
+        return age;
+    }
+
+    public void setAge(Integer age) {
+        this.age = age;
+    }
 }

+ 1 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/StatGroupWrapper.java

@@ -20,5 +20,6 @@ public class StatGroupWrapper implements Serializable {
     private Long id;
     private Integer total;
     private String gid; // 字符串分组ID
+    private Double number;
 
 }

+ 39 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/im/CustomerService.java

@@ -0,0 +1,39 @@
+package com.yonge.cooleshow.biz.dal.wrapper.im;
+
+import com.alibaba.fastjson.JSON;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 客服消息
+ * Created by Eric.Shang on 2022/9/30.
+ */
+public class CustomerService {
+
+    /**
+     * 客服通知消息
+     */
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class NotifyMessage implements Serializable {
+
+        private List<String> receives;
+
+        private String title;
+        private String txtMessage;
+        private String imgMessage;
+        private String imgUrl;
+
+        public static NotifyMessage from(String recv) {
+
+            return JSON.parseObject(recv, NotifyMessage.class);
+        }
+    }
+}

+ 77 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/teacher/TeacherWrapper.java

@@ -0,0 +1,77 @@
+package com.yonge.cooleshow.biz.dal.wrapper.teacher;
+
+import com.alibaba.fastjson.JSON;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * Created by Eric.Shang on 2022/10/8.
+ */
+public class TeacherWrapper {
+
+    /**
+     * 老师统计指标信息
+     */
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class TeacherStatInfo implements Serializable {
+
+        // 星级
+        private Integer starGrade;
+        // 粉丝数
+        private Integer fansNum;
+        // 已上课时
+        private Integer expTime;
+        // 未上课时
+        private Integer unExpTime;
+        // 专辑数
+        private Integer musicAlbumNum;
+        // 曲谱数
+        private Integer musicSheetNum;
+        // 学生数
+        private Integer studentNums;
+
+        public String jsonString() {
+
+            return JSON.toJSONString(this);
+        }
+
+
+        public TeacherStatInfo starGrade(Integer starGrade) {
+            this.starGrade = starGrade;
+            return this;
+        }
+
+        public TeacherStatInfo fansNum(Integer fansNum) {
+            this.fansNum = fansNum;
+            return this;
+        }
+
+        public TeacherStatInfo expTime(Integer expTime) {
+            this.expTime = expTime;
+            return this;
+        }
+
+        public TeacherStatInfo unExpTime(Integer unExpTime) {
+            this.unExpTime = unExpTime;
+            return this;
+        }
+
+        public TeacherStatInfo musicAlbumNum(Integer musicAlbumNum) {
+            this.musicAlbumNum = musicAlbumNum;
+            return this;
+        }
+
+        public TeacherStatInfo musicSheetNum(Integer musicSheetNum) {
+            this.musicSheetNum = musicSheetNum;
+            return this;
+        }
+
+    }
+}

+ 29 - 11
cooleshow-user/user-biz/src/main/resources/config/mybatis/ActivityEvaluationMapper.xml

@@ -30,14 +30,13 @@
 
     <sql id="selectSql">
         SELECT
-            <include refid="baseColumns" />,
-            ms.music_sheet_name_ as musicSheetName,
-            (select group_concat(mt.name_) from music_tag mt
-                where find_in_set(mt.id_,ms.music_tag_) and mt.del_flag_ = 0  and mt.state_ = 1) as musicTagNames,
-            (select group_concat(s.name_) from subject s
-                where find_in_set(s.id_,ms.music_subject_) and s.del_flag_ = 0 ) as subjectNames,
-            ms.source_type_ as sourceType,
-            ms.music_price_ as musicPrice
+            <include refid="baseColumns" />
+            , ms.music_sheet_name_ as musicSheetName
+            , ms.music_subject_ AS subjectId
+            , (select group_concat(mt.name_) from music_tag mt where find_in_set(mt.id_,ms.music_tag_) and mt.del_flag_ = 0  and mt.state_ = 1) as musicTagNames
+            , (select group_concat(s.name_) from subject s where find_in_set(s.id_,ms.music_subject_) and s.del_flag_ = 0 ) as subjectNames
+            , ms.source_type_ as sourceType
+            , ms.music_price_ as musicPrice
         FROM activity_evaluation t
         left join music_sheet ms on t.music_sheet_id_ = ms.id_
         <where>
@@ -67,11 +66,13 @@
 
         select ms.id_ as musicSheetId
         ,ms.music_sheet_name_ as musicSheetName
+        , ms.music_subject_ AS subjectId
+        , ms.title_img_ AS musicImage
         ,s.name_ as musicSubject
+        , ms.composer_ AS composer
         ,ae.id_ as evaluationId
-        ,(select if(count(1)>0,1,0) as `join` from activity_evaluation_record aer
-            where aer.evaluation_id_ = ae.id_ and aer.user_id_ = #{userId}
-        ) as `join`
+        , ae.evaluation_difficulty_ AS evaluationDifficulty
+        ,(select if(count(1)>0,1,0) as `join` from activity_evaluation_record aer where aer.evaluation_id_ = ae.id_ and aer.user_id_ = #{userId}) as `join`
         from activity_evaluation ae
         left join music_sheet ms on ms.id_ = ae.music_sheet_id_
         left join subject s on s.id_ = ms.music_subject_
@@ -241,4 +242,21 @@
         </where>
     </select>
     <!--时间匹配活动老师信息-->
+
+    <!--评测活动声部曲目最多分-->
+    <select id="selectSubjectMusicHighestScoreStat"
+            resultType="com.yonge.cooleshow.biz.dal.wrapper.StatGroupWrapper">
+        SELECT t1.evaluation_id_ AS id, MAX(t1.score_) AS number FROM activity_evaluation_record t1
+        <where>
+            t1.score_ IS NOT NULL
+            <if test="activityId != null">
+                AND t1.activity_id_ = #{activityId}
+            </if>
+            <if test="userId != null">
+                AND t1.user_id_ = #{userId}
+            </if>
+        </where>
+        GROUP BY t1.evaluation_id_
+    </select>
+    <!--评测活动声部曲目最多分-->
 </mapper>

+ 48 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/ActivityPlanMapper.xml

@@ -17,6 +17,11 @@
 	        <result column="registration_method_" property="registrationMethod" />
 	        <result column="registration_price_" property="registrationPrice" />
             <result column="share_rate_" property="shareRate" />
+            <result column="share_type_" property="shareType" />
+            <result column="ranking_" property="ranking" />
+            <result column="ranking_rule_" property="rankingRule" />
+            <result column="ranking_method_" property="rankingMethod" />
+            <result column="ranking_score_" property="rankingScore" />
 	        <result column="draft_flag_" property="draftFlag" />
 	        <result column="activity_state_" property="activityState" />
 	        <result column="create_time_" property="createTime" />
@@ -45,6 +50,8 @@
         , t.share_type_ as shareType
         , t.ranking_ as ranking
         , t.ranking_rule_ as rankingRule
+        , t.ranking_method_ as rankingMethod
+        , t.ranking_score_ as rankingScore
         , t.draft_flag_ as draftFlag
         , if(t.activity_state_ = 1, 1, 0) as activityState
         , t.create_time_ as createTime
@@ -158,4 +165,45 @@
     </select>
     <!--活动参与人数、获奖人数统计-->
 
+    <!--活动总分用户排名-->
+    <select id="selectActivityHighestScoreRankingInfo"
+            resultType="com.yonge.cooleshow.biz.dal.vo.ActivityRankingVo">
+        SELECT
+            t1.score_, t1.user_id_, t1.resource_id_, t1.create_time_ AS joinDate, t1.times_, t2.avatar_ AS userAvatar, t2.username_ AS username
+        FROM
+            activity_evaluation_record t1 LEFT JOIN sys_user t2 ON t1.user_id_ = t2.id_
+        WHERE
+            t1.activity_id_ = #{activityId} AND t1.ranking_method_ = 'TOTAL_SCORE' AND t1.score_ >= #{rankingScore}
+            <if test="subjectId != null">
+                AND t1.resource_id_ = #{subjectId}
+            </if>
+            <if test="subjectId == null">
+                AND t1.user_id_ NOT IN (SELECT t3.user_id_ FROM activity_user_reward t3 WHERE t3.activity_id_ = #{activityId})
+            </if>
+        ORDER BY
+            t1.score_ DESC,
+            t1.registration_time_ ASC
+        LIMIT #{limit}
+    </select>
+
+    <select id="selectActivityHighestScoreUserRanking"
+            resultType="com.yonge.cooleshow.biz.dal.vo.ActivityRankingVo">
+        SELECT
+            t1.score_, t1.user_id_, t1.resource_id_, t1.create_time_ AS joinDate, t1.times_, t2.avatar_ AS userAvatar, t2.username_ AS username
+        FROM
+            activity_evaluation_record t1 LEFT JOIN sys_user t2 ON t1.user_id_ = t2.id_
+        WHERE
+            t1.activity_id_ = #{activityId} AND t1.ranking_method_ = 'TOTAL_SCORE' AND t1.score_ >= #{rankingScore}
+            <if test="subjectId != null">
+                AND t1.resource_id_ = #{subjectId}
+            </if>
+            <if test="userId != null">
+                AND t1.user_id_ = #{userId}
+            </if>
+        ORDER BY
+            t1.score_ DESC,
+            t1.registration_time_ ASC
+        LIMIT 1
+    </select>
+    <!--活动总分用户排名-->
 </mapper>

+ 3 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/ActivityRegistrationMapper.xml

@@ -63,6 +63,9 @@
                     <if test="param.activityId != null">
                         and aer.activity_id_ = #{param.activityId}
                     </if>
+		            <if test="param.subjectIds != null and param.subjectIds.size() > 0">
+                        AND aer.resource_id_ IN (<foreach collection="param.subjectIds" separator="," item="item">#{item}</foreach>)
+                    </if>
 		            <if test="param.musicSheetIds != null and param.musicSheetIds.size()>0">
                         and ae.music_sheet_id_ IN
                         <foreach collection="param.musicSheetIds" item="item" separator="," open="(" close=")">

+ 1 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/TeacherMapper.xml

@@ -256,6 +256,7 @@
             u.real_name_ AS realName,
             u.gender_ AS gender,
             u.phone_ AS phone,
+            u.birthdate_ AS birthdate,
             (SELECT group_concat(name_) FROM `subject` WHERE find_in_set(id_,sr.subject_id_)) AS subjectName,
             if(sr.membership_start_time_ &lt;= now() and sr.membership_end_time_ &gt;= now(),1,0) AS isVip
         FROM student_star s

+ 13 - 0
cooleshow-websocket/src/main/resources/bootstrap-dev.yml

@@ -0,0 +1,13 @@
+spring:
+  cloud:
+    nacos:
+      config:
+        server-addr: 47.114.1.200:8848
+        namespace: 6f8374a9-598f-4889-bb17-476070ffb8de
+        group: DEFAULT_GROUP
+        prefix: websocket
+        file-extension: yaml
+        refresh:
+          enabled: true
+        enabled: true
+

+ 69 - 0
cooleshow-websocket/src/main/resources/logback-spring.xml

@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration scan="true" scanPeriod="10 seconds">
+
+	<property name="LOG_HOME" value="/mdata/logs/websocket-%d{yyyy-MM-dd_HH}-%i.log" />
+	<property name="CONSOLE_LOG_PATTERN"
+		value="[%X{username} %X{ip} %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}] : %msg%n" />
+
+	<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder charset="UTF-8">
+			<pattern>${CONSOLE_LOG_PATTERN}</pattern>
+		</encoder>
+	</appender>
+
+	<appender name="file"
+		class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+			<FileNamePattern>${LOG_HOME}</FileNamePattern>
+			<MaxHistory>90</MaxHistory>
+			<TimeBasedFileNamingAndTriggeringPolicy
+				class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+				<MaxFileSize>20MB</MaxFileSize>
+			</TimeBasedFileNamingAndTriggeringPolicy>
+		</rollingPolicy>
+
+		<encoder>
+			<pattern>${CONSOLE_LOG_PATTERN}</pattern>
+		</encoder>
+	</appender>
+
+	<logger name="com.yonge" level="INFO" />
+
+	<!--本地环境:打印控制台 -->
+	<springProfile name="local">
+		<root level="INFO">
+			<appender-ref ref="stdout" />
+			<appender-ref ref="file" />
+		</root>
+	</springProfile>
+	<!--开发环境:打印控制台 -->
+	<springProfile name="dev">
+		<root level="INFO">
+			<appender-ref ref="stdout" />
+			<appender-ref ref="file" />
+		</root>
+	</springProfile>
+	
+	<springProfile name="test">
+		<root level="INFO">
+			<appender-ref ref="stdout" />
+			<appender-ref ref="file" />
+		</root>
+	</springProfile>
+
+	<springProfile name="pre">
+		<root level="INFO">
+			<appender-ref ref="stdout" />
+			<appender-ref ref="file" />
+		</root>
+	</springProfile>
+
+	<!--生产环境:输出到文件 -->
+	<springProfile name="prod">
+		<root level="WARN">
+			<appender-ref ref="stdout" />
+			<appender-ref ref="file" />
+		</root>
+	</springProfile>
+
+</configuration>