소스 검색

Merge branch 'feature/0722-kf' into feature/0712_vip

yuanliang 8 달 전
부모
커밋
b961bc1037
35개의 변경된 파일764개의 추가작업 그리고 123개의 파일을 삭제
  1. 2 2
      cooleshow-app/src/main/java/com/yonge/cooleshow/admin/controller/StudentController.java
  2. 15 2
      cooleshow-app/src/main/java/com/yonge/cooleshow/admin/controller/TeacherController.java
  3. 2 2
      cooleshow-app/src/main/java/com/yonge/cooleshow/student/controller/ImGroupController.java
  4. 2 2
      cooleshow-app/src/main/java/com/yonge/cooleshow/student/controller/ImUserFriendController.java
  5. 2 2
      cooleshow-app/src/main/java/com/yonge/cooleshow/teacher/controller/ImGroupController.java
  6. 4 2
      cooleshow-app/src/main/java/com/yonge/cooleshow/teacher/controller/ImUserFriendController.java
  7. 25 0
      cooleshow-app/src/main/java/com/yonge/cooleshow/teacher/controller/StudentController.java
  8. 28 0
      cooleshow-app/src/main/java/com/yonge/cooleshow/teacher/controller/TeacherController.java
  9. 2 2
      cooleshow-auth/auth-api/src/main/java/com/yonge/cooleshow/auth/config/CustomerServiceConfig.java
  10. 3 0
      cooleshow-auth/auth-server/src/main/java/com/yonge/cooleshow/auth/dal/dao/SysUserDao.java
  11. 5 13
      cooleshow-auth/auth-server/src/main/java/com/yonge/cooleshow/auth/service/impl/SysUserServiceImpl.java
  12. 17 0
      cooleshow-auth/auth-server/src/main/resources/config/mybatis/SysUserMapper.xml
  13. 10 0
      cooleshow-common/src/main/java/com/yonge/cooleshow/common/constant/SysConfigConstant.java
  14. 2 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/ImUserFriendDao.java
  15. 5 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/TeacherDao.java
  16. 8 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/BasicUserInfo.java
  17. 3 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/req/TeacherSubmitReq.java
  18. 3 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/search/TeacherSearch.java
  19. 4 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/Teacher.java
  20. 2 2
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/EUserVipType.java
  21. 4 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ImUserFriendService.java
  22. 5 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/TeacherService.java
  23. 9 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CustomerServiceBatchSendingServiceImpl.java
  24. 133 39
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImUserFriendServiceImpl.java
  25. 5 11
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/StudentServiceImpl.java
  26. 248 9
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/TeacherServiceImpl.java
  27. 11 7
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/TenantApplyRecordServiceImpl.java
  28. 95 25
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/VipCardRecordServiceImpl.java
  29. 4 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/TeacherHomeVo.java
  30. 2 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/TeacherVo.java
  31. 13 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/im/ImUserFriendVO.java
  32. 6 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/VipCardRecordWrapper.java
  33. 33 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/teacher/TeacherWrapper.java
  34. 13 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/ImUserFriendMapper.xml
  35. 39 1
      cooleshow-user/user-biz/src/main/resources/config/mybatis/TeacherMapper.xml

+ 2 - 2
cooleshow-app/src/main/java/com/yonge/cooleshow/admin/controller/StudentController.java

@@ -251,9 +251,9 @@ public class StudentController extends BaseController {
         OutputStream outputStream = response.getOutputStream();
         try {
             HSSFWorkbook workbook = POIUtil.exportExcel(new String[]{"学生编号", "学生姓名", "真实姓名", "性别", "出生日期",
-                    "年龄", "专业", "手机号码", "是否是会员", "注册时间", "用户状态", "学生来源","小组"}, new String[]{
+                    "年龄", "专业", "手机号码", "会员类型", "会员结束时间", "注册时间", "用户状态", "学生来源","小组"}, new String[]{
                     "userId", "username", "realName", "gender.msg", "birthdate", "age", "subjectName", "phone",
-                    "isVip.msg", "createTime", "userStatus.msg", "tenantName", "tenantGroupName"}, rows);
+                    "vipType.name", "membershipEndTime", "createTime", "userStatus.msg", "tenantName", "tenantGroupName"}, rows);
             response.setContentType("application/octet-stream");
             response.setHeader("Content-Disposition", "attac:wq" +
                     "hment;filename=学生列表-" + DateUtil.getDate(new Date()) + ".xls");

+ 15 - 2
cooleshow-app/src/main/java/com/yonge/cooleshow/admin/controller/TeacherController.java

@@ -218,9 +218,9 @@ public class TeacherController extends BaseController {
         OutputStream outputStream = response.getOutputStream();
         try {
             HSSFWorkbook workbook = POIUtil.exportExcel(new String[]{"老师编号", "昵称", "姓名", "手机号", "老师类型",
-                    "注册时间", "认证时间", "状态", "是否是会员", "徽章", "机构", "小组"}, new String[]{
+                    "注册时间", "认证时间", "状态", "会员类型", "会员结束时间", "徽章", "机构", "小组"}, new String[]{
                     "userId", "username", "realName", "phone", "entryFlag.code == 1 ? '达人' : '游客'", "createTime",
-                    "entryAuthDate","userStatus.msg", "isVip.code == 1 ? '是' : '否'", "tag", "tenantName",
+                    "entryAuthDate","userStatus.msg", "vipType.name", "membershipEndTime", "tag", "tenantName",
                     "tenantGroupName"}, rows);
             response.setContentType("application/octet-stream");
             response.setHeader("Content-Disposition", "attac:wq" +
@@ -355,4 +355,17 @@ public class TeacherController extends BaseController {
         imGroupService.setTeacherFansGroup();
         return succeed();
     }
+
+    /**
+     * 老师账号冻结/解冻
+     */
+    @PostMapping("/updateLock/{teacherId}")
+    @ApiOperation(value = "老师账号冻结/解冻")
+    @PreAuthorize("@pcs.hasPermissions('teacher/updateLock')")
+    public HttpResponseResult updateLock(@PathVariable("teacherId") Long teacherId) {
+        SysUser sysUser = sysUserFeignService.queryUserInfo();
+        teacherService.updateLock(sysUser, teacherId);
+        return succeed();
+    }
+
 }

+ 2 - 2
cooleshow-app/src/main/java/com/yonge/cooleshow/student/controller/ImGroupController.java

@@ -136,7 +136,7 @@ public class ImGroupController extends BaseController {
             return failed("无效的用户ID");
         }
 
-        ImUserFriend userFriend = imUserFriendService.getDetail(userId, ClientEnum.TEACHER);
+        ImUserFriendVO.ImUserFriend userFriend = imUserFriendService.getDetail(userId, ClientEnum.TEACHER);
         if (Objects.isNull(userFriend)) {
             return failed("当前好友不存在");
         }
@@ -145,7 +145,7 @@ public class ImGroupController extends BaseController {
             userFriend.setFriendType(ClientEnum.STUDENT);
         }
 
-        return succeed(ImUserFriendVO.ImUserFriend.from(JSON.toJSONString(userFriend)));
+        return succeed(userFriend);
     }
 }
 

+ 2 - 2
cooleshow-app/src/main/java/com/yonge/cooleshow/student/controller/ImUserFriendController.java

@@ -91,7 +91,7 @@ public class ImUserFriendController extends BaseController {
             return failed("无效的用户ID");
         }
 
-        ImUserFriend userFriend = imUserFriendService.getDetail(userId, ClientEnum.STUDENT);
+        ImUserFriendVO.ImUserFriend userFriend = imUserFriendService.getDetail(userId, ClientEnum.STUDENT);
         if (Objects.isNull(userFriend)) {
             return failed("当前好友不存在");
         }
@@ -100,7 +100,7 @@ public class ImUserFriendController extends BaseController {
             userFriend.setFriendType(ClientEnum.TEACHER);
         }
 
-        return succeed(ImUserFriendVO.ImUserFriend.from(JSON.toJSONString(userFriend)));
+        return succeed(userFriend);
     }
 }
 

+ 2 - 2
cooleshow-app/src/main/java/com/yonge/cooleshow/teacher/controller/ImGroupController.java

@@ -195,7 +195,7 @@ public class ImGroupController extends BaseController {
             return failed("无效的用户ID");
         }
 
-        ImUserFriend userFriend = imUserFriendService.getDetail(userId, ClientEnum.TEACHER);
+        ImUserFriendVO.ImUserFriend userFriend = imUserFriendService.getDetail(userId, ClientEnum.TEACHER);
         if (Objects.isNull(userFriend)) {
             return failed("当前好友不存在");
         }
@@ -204,7 +204,7 @@ public class ImGroupController extends BaseController {
             userFriend.setFriendType(ClientEnum.STUDENT);
         }
 
-        return succeed(ImUserFriendVO.ImUserFriend.from(JSON.toJSONString(userFriend)));
+        return succeed(userFriend);
     }
 
     @ApiOperation(value = "转让群主", notes = "转让群主- 传入 ImGroupVo.ImGroupOwner")

+ 4 - 2
cooleshow-app/src/main/java/com/yonge/cooleshow/teacher/controller/ImUserFriendController.java

@@ -9,6 +9,7 @@ import com.yonge.cooleshow.biz.dal.service.ImGroupService;
 import com.yonge.cooleshow.biz.dal.service.ImUserFriendService;
 import com.yonge.cooleshow.biz.dal.service.SysUserService;
 import com.yonge.cooleshow.biz.dal.vo.im.ImUserFriendVO;
+import com.yonge.cooleshow.biz.dal.wrapper.VipCardRecordWrapper;
 import com.yonge.cooleshow.biz.dal.wrapper.im.ImUserWrapper;
 import com.yonge.cooleshow.common.controller.BaseController;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
@@ -90,7 +91,7 @@ public class ImUserFriendController extends BaseController {
             return failed("无效的用户ID");
         }
 
-        ImUserFriend userFriend = imUserFriendService.getDetail(userId, ClientEnum.TEACHER);
+        ImUserFriendVO.ImUserFriend userFriend = imUserFriendService.getDetail(userId, ClientEnum.TEACHER);
         if (Objects.isNull(userFriend)) {
             return failed("当前好友不存在");
         }
@@ -99,7 +100,8 @@ public class ImUserFriendController extends BaseController {
             userFriend.setFriendType(ClientEnum.STUDENT);
         }
 
-        return succeed(ImUserFriendVO.ImUserFriend.from(JSON.toJSONString(userFriend)));
+
+        return succeed(userFriend);
     }
 
 }

+ 25 - 0
cooleshow-app/src/main/java/com/yonge/cooleshow/teacher/controller/StudentController.java

@@ -5,10 +5,12 @@ import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
 import com.yonge.cooleshow.auth.api.entity.SysUser;
 import com.yonge.cooleshow.biz.dal.dto.search.StudentSearch;
 import com.yonge.cooleshow.biz.dal.entity.Student;
+import com.yonge.cooleshow.biz.dal.entity.Teacher;
 import com.yonge.cooleshow.biz.dal.entity.TenantGroup;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.service.ImGroupService;
 import com.yonge.cooleshow.biz.dal.service.StudentService;
+import com.yonge.cooleshow.biz.dal.service.TeacherService;
 import com.yonge.cooleshow.biz.dal.service.TenantGroupService;
 import com.yonge.cooleshow.biz.dal.vo.StudentVo;
 import com.yonge.cooleshow.common.controller.BaseController;
@@ -16,15 +18,20 @@ import com.yonge.cooleshow.common.entity.HttpResponseResult;
 import com.yonge.cooleshow.common.enums.UserLockFlag;
 import com.yonge.cooleshow.common.enums.UserStatusEnum;
 import com.yonge.cooleshow.common.enums.YesOrNoEnum;
+import com.yonge.toolset.base.exception.BizException;
 import com.yonge.toolset.base.page.PageInfo;
 import com.yonge.toolset.base.util.StringUtil;
 import com.yonge.toolset.mybatis.support.PageUtil;
 import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
 import io.swagger.annotations.ApiOperation;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -53,6 +60,9 @@ public class StudentController extends BaseController {
     @Autowired
     private TenantGroupService tenantGroupService;
 
+    @Autowired
+    private TeacherService teacherService;
+
 
     @ApiOperation(value = "查询指定学员信息-融云token")
     @GetMapping("/queryUserById")
@@ -75,6 +85,21 @@ public class StudentController extends BaseController {
         return succeed(studentService.detail(userId));
     }
 
+    @GetMapping("/detail/{id}")
+    public HttpResponseResult<StudentVo> detail(@PathVariable("id") Long id) {
+        SysUser user = sysUserFeignService.queryUserInfo();
+        Teacher teacher = teacherService.getById(user.getId());
+        if (Boolean.FALSE.equals(teacher.getCustomerService())) {
+            throw new BizException("权限不足");
+        }
+        StudentVo detail = studentService.detail(id);
+        if (detail != null && detail.getTenantGroupId() != null) {
+            TenantGroup group = tenantGroupService.getById(detail.getTenantGroupId());
+            detail.setTenantGroupName(group == null ? "" : group.getName());
+        }
+        return succeed(detail);
+    }
+
     /**
      * 查询老师所属机构下所有的学生
      *

+ 28 - 0
cooleshow-app/src/main/java/com/yonge/cooleshow/teacher/controller/TeacherController.java

@@ -7,8 +7,10 @@ import com.yonge.cooleshow.auth.api.entity.SysUser;
 import com.yonge.cooleshow.biz.dal.dto.TeacherDto;
 import com.yonge.cooleshow.biz.dal.entity.Subject;
 import com.yonge.cooleshow.biz.dal.entity.Teacher;
+import com.yonge.cooleshow.biz.dal.entity.TeacherStyleVideo;
 import com.yonge.cooleshow.biz.dal.entity.TenantInfo;
 import com.yonge.cooleshow.biz.dal.entity.TenantUnbindRecord;
+import com.yonge.cooleshow.biz.dal.enums.AuthStatusEnum;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.service.SmsCodeService;
 import com.yonge.cooleshow.biz.dal.service.SubjectService;
@@ -30,13 +32,18 @@ import com.yonge.toolset.base.util.StringUtil;
 import com.yonge.toolset.mybatis.support.PageUtil;
 import com.yonge.toolset.utils.idcard.IdcardInfoExtractor;
 import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
 import org.apache.commons.lang3.StringUtils;
 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.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -83,6 +90,27 @@ public class TeacherController extends BaseController {
         return teacherService.queryUserInfo(user.getId());
     }
 
+
+    /**
+     * 查询单条
+     */
+    @GetMapping("/detail/{id}")
+    public HttpResponseResult<TeacherVo> detail(@PathVariable("id") Long userId) {
+        SysUser user = sysUserFeignService.queryUserInfo();
+        Teacher teacher = teacherService.getById(user.getId());
+        if (Boolean.FALSE.equals(teacher.getCustomerService())) {
+            throw new BizException("权限不足");
+        }
+
+        TeacherVo detail = teacherService.findTeacherDetailInfo(userId);
+        if (null != detail && !CollectionUtils.isEmpty(detail.getStyleVideo())) {
+            List<TeacherStyleVideo> styleVideo = detail.getStyleVideo();
+            List<TeacherStyleVideo> collect = styleVideo.stream().filter(o -> AuthStatusEnum.PASS.equals(o.getAuthStatus())).collect(Collectors.toList());
+            detail.setStyleVideo(collect);
+        }
+        return succeed(detail);
+    }
+
     @ApiOperation(value = "开通直播")
     @GetMapping("/openLive")
     public HttpResponseResult<Boolean> openLive() {

+ 2 - 2
cooleshow-auth/auth-api/src/main/java/com/yonge/cooleshow/auth/config/CustomerServiceConfig.java

@@ -16,8 +16,8 @@ import java.io.Serializable;
 @Component
 public class CustomerServiceConfig implements Serializable {
 
-    @Value("${app.customer.service:17740683946}")
-    private String customerService;
+//    @Value("${app.customer.service:17740683946}")
+//    private String customerService;
     @Value("${app.customer.message:}")
     private String customerMessage;
     @Value("${app.customer.title:}")

+ 3 - 0
cooleshow-auth/auth-server/src/main/java/com/yonge/cooleshow/auth/dal/dao/SysUserDao.java

@@ -185,4 +185,7 @@ public interface SysUserDao extends BaseDAO<Long, SysUser> {
     void updateStudentHideFlag(@Param("userId") Long userId, @Param("hideFlag") int hideFlag);
 
     void updateAvatar(@Param("clientId") String clientId, @Param("avatar") String avatar,@Param("id") Long id);
+
+    SysUser getCustomerServiceByFriendLeast();
+
 }

+ 5 - 13
cooleshow-auth/auth-server/src/main/java/com/yonge/cooleshow/auth/service/impl/SysUserServiceImpl.java

@@ -3,6 +3,7 @@ package com.yonge.cooleshow.auth.service.impl;
 import com.alibaba.fastjson.JSON;
 import com.google.common.collect.Lists;
 import com.yonge.cooleshow.api.feign.AdminFeignService;
+import com.yonge.cooleshow.api.feign.TeacherFeignService;
 import com.yonge.cooleshow.api.feign.dto.UserFriendInfoVO;
 import com.yonge.cooleshow.auth.api.dto.QRLoginDto;
 import com.yonge.cooleshow.auth.api.dto.RealnameAuthReq;
@@ -218,24 +219,15 @@ public class SysUserServiceImpl extends BaseServiceImpl<Long, SysUser> implement
 
             ThreadPool.getExecutor().submit(() -> {
 
-                String customerService = customerServiceConfig.getCustomerService();
-                if (StringUtils.isNotEmpty(customerService)) {
-
-                    List<String> collect = Arrays.stream(customerService.split(",")).collect(Collectors.toList());
-
-                    Random rand = new Random();
-                    String mobile = collect.get(rand.nextInt(collect.size()));
-
-                    // 系统客服好友
-                    SysUser friend = sysUserDao.queryByPhone(mobile);
-
+                SysUser customerService = sysUserDao.getCustomerServiceByFriendLeast();
+                if (customerService != null) {
                     // 发送添加系统客服好友消息
                     HttpResponseResult<Boolean> result = adminFeignService.customerService(UserFriendInfoVO.builder()
                             .userId(sysUser.getId())
                             .clientType(clientType)
-                            .friendIds(Lists.newArrayList(friend.getId()))
+                            .friendIds(Lists.newArrayList(customerService.getId()))
                             .build());
-                    log.info("sendSysCustomerServiceFriendMessage mobile={}, ret={}", mobile, JSON.toJSONString(result));
+                    log.info("sendSysCustomerServiceFriendMessage mobile={}, ret={}", customerService.getPhone(), JSON.toJSONString(result));
                 }
 
             });

+ 17 - 0
cooleshow-auth/auth-server/src/main/resources/config/mybatis/SysUserMapper.xml

@@ -382,4 +382,21 @@
             update sys_user set avatar_ = #{avatar} where id_ = #{id}
         </if>
     </update>
+
+    <select id="getCustomerServiceByFriendLeast" resultMap="SysUser">
+        select m.*
+        from (SELECT te.user_id_,
+                     count(distinct iuf.friend_id_) friends
+              from teacher te
+                       left join sys_user su on te.user_id_ = su.id_
+                       left join im_user_friend iuf on te.user_id_ = iuf.user_id_
+              where te.lock_flag_ = 0
+                and te.customer_service_ = 1
+                and su.del_flag_ = 0
+                and su.lock_flag_ = 0
+              group by te.user_id_
+              order by friends
+              limit 1) t
+                 left join sys_user m on m.id_ = t.user_id_
+    </select>
 </mapper>

+ 10 - 0
cooleshow-common/src/main/java/com/yonge/cooleshow/common/constant/SysConfigConstant.java

@@ -430,4 +430,14 @@ public interface SysConfigConstant {
      * 草稿保存时长
      */
     String USER_MUSIC_DRAFT_TIME = "user_music_draft_time";
+
+    /**
+     * 添加客服好友时,客服发送消息
+     */
+    String CUSTOMER_SERVICE_ADD_MSG = "customer_service_add_msg";
+
+    /**
+     * 添加客服好友时,客服发送消息标题
+     */
+    String CUSTOMER_SERVICE_ADD_MSG_TITLE = "customer_service_add_msg_title";
 }

+ 2 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/ImUserFriendDao.java

@@ -23,5 +23,7 @@ public interface ImUserFriendDao extends BaseMapper<ImUserFriend> {
 
     void delStudentFriendByTenantId(@Param("tenantId") Long tenantId,@Param("userId") Long userId ,
                                     @Param("clientType") String clientType);
+
+    List<ImUserFriend> queryExistCustomerServiceFriend(@Param("userIds") String userIds, @Param("clientType") String clientType);
 }
 

+ 5 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/TeacherDao.java

@@ -16,6 +16,7 @@ import com.yonge.cooleshow.biz.dal.vo.TeacherVo;
 
 import com.yonge.cooleshow.biz.dal.wrapper.StatGroupWrapper;
 import com.yonge.cooleshow.biz.dal.wrapper.TenantInfoWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.teacher.TeacherWrapper;
 import org.apache.ibatis.annotations.Param;
 
 public interface TeacherDao extends BaseMapper<Teacher> {
@@ -115,4 +116,8 @@ public interface TeacherDao extends BaseMapper<Teacher> {
     List<TenantInfoWrapper.UserCount> countTeacherByTenantIds(@Param("tenantIdList") List<Long> tenantIdList);
 
     Integer queryTeacherCounts(@Param("id") Long id);
+
+    Teacher getCustomerServiceByFriendLeast();
+
+    List<TeacherWrapper.TeacherFriend> getCustomerServiceFriendNums();
 }

+ 8 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/BasicUserInfo.java

@@ -3,6 +3,8 @@ package com.yonge.cooleshow.biz.dal.dto;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
+import java.util.Date;
+
 /**
 * @description: 用户基本信息
 * @author zx
@@ -27,4 +29,10 @@ public class BasicUserInfo {
 
     @ApiModelProperty("手机号")
     private String phone;
+
+    @ApiModelProperty("性别")
+    private Integer gender;
+
+    @ApiModelProperty("生日")
+    private Date birthdate;
 }

+ 3 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/req/TeacherSubmitReq.java

@@ -65,6 +65,9 @@ public class TeacherSubmitReq implements Serializable {
     @ApiModelProperty(value = "后台修改人",hidden = true)
     private Long updateBy;
 
+    @ApiModelProperty("是否客服")
+    private Boolean customerService;
+
     public Long getUserId() {
         return userId;
     }

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

@@ -82,6 +82,9 @@ public class TeacherSearch extends QueryInfo{
 	@ApiModelProperty("用户会员类型")
 	private EUserVipType vipType;
 
+	@ApiModelProperty("是否客服")
+	private Boolean customerService;
+
 	@ApiModelProperty(value = "排序方式, 默认 create_time_ desc", hidden = true)
 	private String orderBy;
 	public YesOrNoEnum getTrainFlag() {

+ 4 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/Teacher.java

@@ -251,6 +251,10 @@ public class Teacher implements Serializable {
     @TableField(value = "im_device_id_")
     private String imDeviceId;
 
+    @ApiModelProperty("是否是客服")
+    @TableField(value = "customer_service_")
+    private Boolean customerService;
+
     public ESettlementFrom getSettlementFrom() {
         return settlementFrom;
     }

+ 2 - 2
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/EUserVipType.java

@@ -9,8 +9,8 @@ import com.yonge.toolset.base.enums.BaseEnum;
 public enum EUserVipType implements BaseEnum<String, EUserVipType> {
 
     NORMAL("普通用户"),
-    VIP("会员"),
-    SVIP("SVIP"),
+    VIP("VIP会员"),
+    SVIP("SVIP会员"),
 
     ;
     @EnumValue

+ 4 - 1
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ImUserFriendService.java

@@ -4,6 +4,7 @@ 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.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.vo.im.ImUserFriendVO;
 import com.yonge.cooleshow.biz.dal.wrapper.im.CustomerService;
 import com.yonge.cooleshow.biz.dal.wrapper.im.ImUserWrapper;
 import org.springframework.transaction.annotation.Transactional;
@@ -41,7 +42,7 @@ public interface ImUserFriendService extends IService<ImUserFriend> {
     * @author zx
     * @date 2022/3/24 12:03
     */
-    ImUserFriend getDetail(String userId, ClientEnum clientType);
+    ImUserFriendVO.ImUserFriend getDetail(String userId, ClientEnum clientType);
 
     /**
      * 新用户自动添加客服
@@ -52,6 +53,8 @@ public interface ImUserFriendService extends IService<ImUserFriend> {
      */
     Integer registerUserBindCustomerService(Long userId, List<Long> friendIds, ClientEnum clientType);
 
+    void sendCustomerServiceAddFriendMessage(Long customerServiceId, String sendTitle, String sendMessage, List<Long> friendIds, ClientEnum friendType);
+
     /**
      * 发送系统客服消息
      * @param sender 发送者

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

@@ -2,6 +2,7 @@ package com.yonge.cooleshow.biz.dal.service;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.yonge.cooleshow.auth.api.entity.SysUser;
 import com.yonge.cooleshow.biz.dal.dao.TeacherDao;
 import com.yonge.cooleshow.biz.dal.dto.TeacherDto;
 import com.yonge.cooleshow.biz.dal.dto.req.TeacherSubmitReq;
@@ -186,4 +187,8 @@ public interface TeacherService extends IService<Teacher> {
     UserPaymentOrderWrapper.AccountTenantTo teacherSettlementFrom(Long teacherId,Long recomUserId);
 
     Map<Long, Teacher> getMapByIds(List<Long> teacherIds);
+
+    List<Teacher> getCustomerService();
+
+    void updateLock(SysUser sysUser, Long teacherId);
 }

+ 9 - 1
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CustomerServiceBatchSendingServiceImpl.java

@@ -1,6 +1,7 @@
 package com.yonge.cooleshow.biz.dal.service.impl;
 
 import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -211,7 +212,14 @@ public class CustomerServiceBatchSendingServiceImpl extends ServiceImpl<Customer
         // 发送客服ID
         String customerService = info.getMobile();
         if (StringUtils.isBlank(customerService)) {
-            customerService = customerServiceConfig.getCustomerService();
+            List<Long> teacherIds = teacherService.getCustomerService().stream().map(Teacher::getUserId).collect(Collectors.toList());
+            if (!teacherIds.isEmpty()) {
+                customerService = sysUserMapper.selectList(new QueryWrapper<SysUser>().lambda()
+                                .in(SysUser::getId, teacherIds))
+                        .stream()
+                        .map(SysUser::getPhone)
+                        .collect(Collectors.joining(","));
+            }
         }
         if (StringUtils.isNotEmpty(customerService)) {
 

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

@@ -15,24 +15,32 @@ import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
 import com.yonge.cooleshow.auth.api.entity.SysUser;
 import com.yonge.cooleshow.auth.config.CustomerServiceConfig;
 import com.yonge.cooleshow.biz.dal.dao.ImUserFriendDao;
+import com.yonge.cooleshow.biz.dal.dao.StudentDao;
 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.entity.Subject;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.enums.MK;
 import com.yonge.cooleshow.biz.dal.mapper.SysUserMapper;
 import com.yonge.cooleshow.biz.dal.service.ImGroupService;
 import com.yonge.cooleshow.biz.dal.service.ImUserFriendService;
+import com.yonge.cooleshow.biz.dal.service.SubjectService;
+import com.yonge.cooleshow.biz.dal.service.SysConfigService;
+import com.yonge.cooleshow.biz.dal.service.VipCardRecordService;
+import com.yonge.cooleshow.biz.dal.vo.StudentVo;
+import com.yonge.cooleshow.biz.dal.vo.TeacherVo;
+import com.yonge.cooleshow.biz.dal.vo.im.ImUserFriendVO;
+import com.yonge.cooleshow.biz.dal.wrapper.VipCardRecordWrapper;
 import com.yonge.cooleshow.biz.dal.wrapper.im.CustomerService;
 import com.yonge.cooleshow.biz.dal.wrapper.im.ImUserWrapper;
+import com.yonge.cooleshow.common.constant.SysConfigConstant;
 import com.yonge.toolset.base.exception.BizException;
 import com.yonge.toolset.base.util.ImUtil;
 import io.rong.messages.BaseMessage;
 import io.rong.messages.ImgMessage;
 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 org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -41,13 +49,12 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
-import java.text.MessageFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -78,6 +85,14 @@ public class ImUserFriendServiceImpl extends ServiceImpl<ImUserFriendDao, ImUser
 
     @Autowired
     private ImPluginContext imPluginContext;
+    @Autowired
+    private StudentDao studentDao;
+    @Autowired
+    private SubjectService subjectService;
+    @Autowired
+    private VipCardRecordService vipCardRecordService;
+    @Autowired
+    private SysConfigService sysConfigService;
     @Override
     public ImUserFriendDao getDao() {
         return this.baseMapper;
@@ -183,49 +198,71 @@ public class ImUserFriendServiceImpl extends ServiceImpl<ImUserFriendDao, ImUser
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public ImUserFriend getDetail(String imUserId, ClientEnum clientType) {
+    public ImUserFriendVO.ImUserFriend getDetail(String imUserId, ClientEnum clientType) {
 
         // 用户ID
         long userId = Long.parseLong(imGroupService.analysisImUserId(imUserId));
 
         SysUser sysUser = sysUserFeignService.queryUserInfo();
         BasicUserInfo basicUserInfo = teacherDao.getBasicUserInfo(userId);
-        if (sysUser != null && sysUser.getId() != null) {
-
-            ImUserFriend imUserFriend = lambdaQuery()
-                    .eq(ImUserFriend::getUserId, sysUser.getId())
-                    .eq(ImUserFriend::getClientType, clientType)
-                    .eq(ImUserFriend::getFriendId, userId)
-                    .one();
-            if (Objects.nonNull(imUserFriend)) {
-                //更新当前用户关联的该用户的详情信息
-                imUserFriend.setFriendAvatar(basicUserInfo.getAvatar());
-                imUserFriend.setFriendNickname(basicUserInfo.getUsername());
-                imUserFriend.setUpdateTime(new Date());
-                baseMapper.updateById(imUserFriend);
-
-                // 设置融云IM好友ID
-                imUserFriend.setImFriendId(imUserId);
-
-                return imUserFriend;
+        if (basicUserInfo == null) {
+            throw new BizException("无效的用户");
+        }
+
+        String friendClientType = imGroupService.analysisImUserClient(imUserId);
+        ImUserFriend imUserFriend = lambdaQuery()
+                .eq(ImUserFriend::getUserId, sysUser.getId())
+                .eq(ImUserFriend::getClientType, clientType)
+                .eq(ImUserFriend::getFriendId, userId)
+                .eq(ImUserFriend::getFriendType, ClientEnum.valueOf(friendClientType))
+                .one();
+        if (Objects.nonNull(imUserFriend)) {
+            //更新当前用户关联的该用户的详情信息
+            imUserFriend.setFriendAvatar(basicUserInfo.getAvatar());
+            imUserFriend.setFriendNickname(basicUserInfo.getUsername());
+            imUserFriend.setUpdateTime(new Date());
+            baseMapper.updateById(imUserFriend);
+
+            // 设置融云IM好友ID
+            imUserFriend.setImFriendId(imUserId);
+        } else {
+            // 好友身份类型
+            ClientEnum friendType = ClientEnum.TEACHER;
+            if (ClientEnum.STUDENT.match(imGroupService.analysisImUserClient(imUserId))) {
+                friendType = ClientEnum.STUDENT;
             }
+            // 返回当前登录用户信息
+            imUserFriend = new ImUserFriend();
+            imUserFriend.setFriendAvatar(basicUserInfo.getAvatar());
+            imUserFriend.setFriendNickname(basicUserInfo.getUsername());
+            imUserFriend.setFriendId(userId);
+            imUserFriend.setFriendType(friendType);
+            // 设置融云IM好友ID
+            imUserFriend.setImFriendId(imUserId);
         }
 
-        // 好友身份类型
-        ClientEnum friendType = ClientEnum.TEACHER;
-        if (ClientEnum.STUDENT.match(imGroupService.analysisImUserClient(imUserId))) {
-            friendType = ClientEnum.STUDENT;
+        ImUserFriendVO.ImUserFriend result = JSON.parseObject(JSON.toJSONString(imUserFriend), ImUserFriendVO.ImUserFriend.class);
+        result.setFriendGender(basicUserInfo.getGender());
+
+        if (ClientEnum.STUDENT.getCode().equals(friendClientType)) {
+            StudentVo detail = studentDao.detail(userId);
+            if (detail != null && StringUtils.isNotEmpty(detail.getSubjectId())) {
+                List<Subject> subject = subjectService.findBySubjectByIdList(detail.getSubjectId());
+                String subjectName = subject.stream().map(Subject::getName).collect(Collectors.joining("、"));
+                result.setFriendSubjectName(subjectName);
+            }
+        } else if (ClientEnum.TEACHER.getCode().equals(friendClientType)) {
+            TeacherVo detail = teacherDao.detail(userId);
+            if (detail != null && StringUtils.isNotEmpty(detail.getSubjectId())) {
+                List<Subject> subject = subjectService.findBySubjectByIdList(detail.getSubjectId());
+                String subjectName = subject.stream().map(Subject::getName).collect(Collectors.joining("、"));
+                result.setFriendSubjectName(subjectName);
+            }
         }
-        // 返回当前登录用户信息
-        ImUserFriend imUserFriend = new ImUserFriend();
-        imUserFriend.setFriendAvatar(basicUserInfo.getAvatar());
-        imUserFriend.setFriendNickname(basicUserInfo.getUsername());
-        imUserFriend.setFriendId(userId);
-        imUserFriend.setFriendType(friendType);
-        // 设置融云IM好友ID
-        imUserFriend.setImFriendId(imUserId);
-
-        return imUserFriend;
+        VipCardRecordWrapper.UserVip userVip = vipCardRecordService.UserVipInfo(userId, ClientEnum.valueOf(friendClientType));
+        result.setUserVip(userVip);
+        result.setFriendBirthdate(basicUserInfo.getBirthdate());
+        return result;
     }
 
     /**
@@ -280,12 +317,12 @@ public class ImUserFriendServiceImpl extends ServiceImpl<ImUserFriendDao, ImUser
         try {
 
             // 设置默认值
-            String customerMessage = customerServiceConfig.getCustomerMessage();
+            String customerMessage = sysConfigService.findConfigValue(SysConfigConstant.CUSTOMER_SERVICE_ADD_MSG);
             if (StringUtils.isEmpty(customerMessage)) {
                 customerMessage = MK.IM_SYS_FRIEND;
             }
 
-            String customerTitle = customerServiceConfig.getCustomerTitle();
+            String customerTitle = sysConfigService.findConfigValue(SysConfigConstant.CUSTOMER_SERVICE_ADD_MSG_TITLE);
             if (StringUtils.isEmpty(customerTitle)) {
                 customerTitle = MK.IM_SYS_TITLE;
             }
@@ -332,6 +369,63 @@ public class ImUserFriendServiceImpl extends ServiceImpl<ImUserFriendDao, ImUser
     }
 
     /**
+     * 与客服建立好友关系时,客服给好友发送消息
+     */
+    @Override
+    public void sendCustomerServiceAddFriendMessage(Long customerServiceId, String sendTitle, String sendMessage, List<Long> friendIds, ClientEnum friendType) {
+
+        if (customerServiceId == null || CollectionUtils.isEmpty(friendIds)) {
+            return;
+        }
+
+        try {
+            if (StringUtils.isEmpty(sendMessage)) {
+                sendMessage = MK.IM_SYS_FRIEND;
+            }
+
+            if (StringUtils.isEmpty(sendTitle)) {
+                sendTitle = MK.IM_SYS_TITLE;
+            }
+
+            // 拓展消息
+            PushExt pushExt = PushExt.build(sendTitle, 1,
+                    new PushExt.HW("channelId", "NORMAL"), new PushExt.VIVO("1"),
+                    new PushExt.APNs("", ""),
+                    new PushExt.OPPO(""));
+
+
+            List<String> targetIds = friendIds.stream().map(n -> imGroupService.getImUserId(n, friendType)).collect(Collectors.toList());
+            MessageWrapper.PrivateMessage build = MessageWrapper.PrivateMessage.builder()
+                    .senderId(imGroupService.getImUserId(customerServiceId.toString(), ClientEnum.TEACHER.getCode()))
+                    .targetIds(targetIds)
+                    //.objectName(txtMessage.getType())
+                    //.rongCloueMessage(txtMessage)
+                    .pushExt(JSON.toJSONString(pushExt))
+                    .includeSender(0)
+                    .build();
+
+
+            if (TencentCloudImPlugin.PLUGIN_NAME.equals(imPluginContext.defaultService())) {
+                // 腾讯IM消息
+                TencentRequest.MessageBody message = TencentRequest.MessageBody.builder()
+                        .msgType(ETencentMessage.TIMTextElem.name())
+                        .msgContent(TencentRequest.TextMessageBody.builder().text(sendMessage).build())
+                        .build();
+
+                Boolean ret = imPluginContext.getPluginService().sendPrivateMessage(build.objectName(message.getMsgType()).tencentMessage(message));
+                log.info("registerUserBindCustomerService GROUP tencentCloud senderId={}, ret={}", build.getSenderId(), ret);
+            } else {
+                // 融云IM消息
+                TxtMessage message = new TxtMessage(sendMessage, "");
+                Boolean ret = imPluginContext.getPluginService().sendPrivateMessage(build.objectName(message.getType()).rongCloueMessage(message));
+                log.info("registerUserBindCustomerService GROUP rongCloud senderId={}, ret={}", build.getSenderId(), ret);
+            }
+        } catch (Exception e) {
+            log.error("registerUserBindCustomerService userId={}", customerServiceId, e);
+        }
+    }
+
+    /**
      * 发送系统客服消息
      *
      * @param sender 发送者

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

@@ -902,17 +902,11 @@ public class StudentServiceImpl extends ServiceImpl<StudentDao, Student> impleme
             addBindUnBindRecord(student.getUserId(), student.getTenantId(), true);
         }
 
-        //  与随机一个客服建立好友
-        String customerService = customerServiceConfig.getCustomerService();
-        if (StringUtils.isNotBlank(customerService)) {
-            List<String> phones = Arrays.stream(customerService.split(",")).collect(Collectors.toList());
-            Random rand = new Random();
-            String mobile = phones.get(rand.nextInt(phones.size()));
-            SysUser friend = sysUserMapper.findUserByPhone(mobile);
-            if (friend != null) {
-                imUserFriendService.registerUserBindCustomerService(student.getUserId(),
-                        Collections.singletonList(friend.getId()), ClientEnum.STUDENT);
-            }
+        //  与好友数量最少的客服建立好友关系
+        Teacher customerService = teacherDao.getCustomerServiceByFriendLeast();
+        if (customerService != null) {
+            imUserFriendService.registerUserBindCustomerService(student.getUserId(),
+                    Collections.singletonList(customerService.getUserId()), ClientEnum.STUDENT);
         }
 
         // 加入机构小组群

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

@@ -26,6 +26,7 @@ import com.yonge.cooleshow.biz.dal.enums.ImGroupType;
 import com.yonge.cooleshow.biz.dal.enums.MessageTypeEnum;
 import com.yonge.cooleshow.biz.dal.enums.TeacherTagEnum;
 import com.yonge.cooleshow.biz.dal.enums.TeacherTypeEnum;
+import com.yonge.cooleshow.biz.dal.enums.im.EImGroupMemberRoleType;
 import com.yonge.cooleshow.biz.dal.mapper.TenantGroupMapper;
 import com.yonge.cooleshow.biz.dal.mapper.UserTenantBindRecordMapper;
 import com.yonge.cooleshow.biz.dal.queryInfo.TeacherQueryInfo;
@@ -66,6 +67,7 @@ import com.yonge.cooleshow.common.enums.CacheNameEnum;
 import com.yonge.cooleshow.common.enums.ESettlementFrom;
 import com.yonge.cooleshow.common.enums.ETenantUnBindAuditStatus;
 import com.yonge.cooleshow.common.enums.UserFirstTimeTypeEnum;
+import com.yonge.cooleshow.common.enums.UserLockFlag;
 import com.yonge.cooleshow.common.enums.YesOrNoEnum;
 import com.yonge.toolset.base.exception.BizException;
 import com.yonge.toolset.base.util.StringUtil;
@@ -367,6 +369,7 @@ public class TeacherServiceImpl extends ServiceImpl<TeacherDao, Teacher> impleme
 
         teacherHomeVo.setDegreeDate(teacher.getDegreeDate());
         teacherHomeVo.setTeacherDate(teacher.getTeacherDate());
+        teacherHomeVo.setCustomerService(teacher.getCustomerService());
         //身份证号、手机号脱敏
         teacherHomeVo.setIdCardNo(ValueUtil.fuzzyIdCard(teacherHomeVo.getIdCardNo()));
 //        teacherHomeVo.setPhone(ValueUtil.fuzzyMobile(teacherHomeVo.getPhone()));
@@ -620,18 +623,20 @@ public class TeacherServiceImpl extends ServiceImpl<TeacherDao, Teacher> impleme
             sendBindUnBindSMS(teacher.getUserId(), teacherSubmitReq.getPhone(), MessageTypeEnum.TEACHER_BIND_TENANT, teacher.getTenantId());
 
             //  与客服建立好友
-            String customerService = customerServiceConfig.getCustomerService();
-            if (StringUtils.isNotBlank(customerService)) {
-                List<String> phones = Arrays.stream(customerService.split(",")).collect(Collectors.toList());
-                Random rand = new Random();
-                String mobile = phones.get(rand.nextInt(phones.size()));
-                SysUser friend = sysUserMapper.findUserByPhone(mobile);
-                if (friend != null) {
+            if (!Boolean.TRUE.equals(teacher.getCustomerService())) {
+                Teacher customerServiceTeacher = getCustomerServiceByFriendLeast();
+                if (customerServiceTeacher != null) {
                     imUserFriendService.registerUserBindCustomerService(teacher.getUserId(),
-                            Collections.singletonList(friend.getId()), ClientEnum.TEACHER);
+                            Collections.singletonList(customerServiceTeacher.getUserId()), ClientEnum.TEACHER);
                 }
             }
         } else {
+            // 客服状态变更,移交好友信息
+            Boolean customerService = teacher.getCustomerService();
+            List<TeacherWrapper.CustomerServiceSendMsg2User> customerServiceSendMsg2User = new ArrayList<>();
+            if (Boolean.TRUE.equals(customerService) && Boolean.FALSE.equals(teacherSubmitReq.getCustomerService())) {
+                customerServiceSendMsg2User.addAll(transferFriend(teacher.getUserId(), true));
+            }
             // 如果机构解绑,更新机构ID为-1
             if (Boolean.TRUE.equals(teacherSubmitReq.getBindTenant())) {
                 teacherSubmitReq.setTenantId(-1L);
@@ -664,11 +669,200 @@ public class TeacherServiceImpl extends ServiceImpl<TeacherDao, Teacher> impleme
             // 老师头像
             teacher.setAvatar(Optional.ofNullable(teacherSubmitReq.getAvatar()).orElse(teacher.getAvatar()));
             baseMapper.updateById(teacher);
+
+
+            // 交接后的客服发送消息
+            if (Boolean.TRUE.equals(customerService) && Boolean.FALSE.equals(teacherSubmitReq.getCustomerService())) {
+                String customerMessage = sysConfigService.findConfigValue(SysConfigConstant.CUSTOMER_SERVICE_ADD_MSG);
+                String customerTitle = sysConfigService.findConfigValue(SysConfigConstant.CUSTOMER_SERVICE_ADD_MSG_TITLE);
+                for (TeacherWrapper.CustomerServiceSendMsg2User serviceSendMsg2User : customerServiceSendMsg2User) {
+                    imUserFriendService.sendCustomerServiceAddFriendMessage(serviceSendMsg2User.getCustomerId(), customerTitle, customerMessage, serviceSendMsg2User.getTeacherIds(),
+                            ClientEnum.TEACHER);
+                    imUserFriendService.sendCustomerServiceAddFriendMessage(serviceSendMsg2User.getCustomerId(), customerTitle, customerMessage, serviceSendMsg2User.getStudentIds(),
+                            ClientEnum.STUDENT);
+                }
+            }
+
         }
 
         return teacher;
     }
 
+    // 客服好友移交给其他客服
+    private List<TeacherWrapper.CustomerServiceSendMsg2User> transferFriend(Long userId, boolean saveGroupFriend) {
+        List<TeacherWrapper.CustomerServiceSendMsg2User> result = new ArrayList<>();
+        // 所有的好友
+        List<ImUserFriend> userFriendList = imUserFriendService.lambdaQuery()
+                .eq(ImUserFriend::getUserId, userId)
+                .eq(ImUserFriend::getClientType, ClientEnum.TEACHER)
+                .list();
+
+        // 其他客服
+        List<TeacherWrapper.TeacherFriend> customerServiceFriendNums = this.getBaseMapper().getCustomerServiceFriendNums();
+        customerServiceFriendNums.removeIf(n -> n.getTeacherId().equals(userId));
+        List<Long> customerIds = customerServiceFriendNums.stream().map(TeacherWrapper.TeacherFriend::getTeacherId).collect(Collectors.toList());
+        userFriendList.removeIf(n -> customerIds.contains(n.getFriendId()) && ClientEnum.TEACHER.equals(n.getFriendType()));
+        if (userFriendList.isEmpty()) { // 没有好友
+            return result;
+        }
+
+        List<ImUserFriend> removeFriendList = userFriendList;
+        if (saveGroupFriend) {
+            // 群里的好友保留
+            List<String> groupIdList = imGroupMemberService.lambdaQuery()
+                    .eq(ImGroupMember::getUserId, userId)
+                    .eq(ImGroupMember::getIsAdmin, true)
+                    .eq(ImGroupMember::getGroupRoleType, EImGroupMemberRoleType.Owner)
+                    .list()
+                    .stream()
+                    .map(ImGroupMember::getGroupId).collect(Collectors.toList());
+
+            List<Long> savedTeacherIdList = new ArrayList<>();
+            List<Long> savedStudentIdList = new ArrayList<>();
+
+            if (!groupIdList.isEmpty()) {
+                List<ImGroupMember> savedMemberList = imGroupMemberService.lambdaQuery()
+                        .in(ImGroupMember::getGroupId, groupIdList)
+                        .eq(ImGroupMember::getIsAdmin, false)
+                        .ne(ImGroupMember::getGroupRoleType, EImGroupMemberRoleType.Owner)
+                        .list();
+
+                savedTeacherIdList.addAll(savedMemberList.stream()
+                        .filter(n -> ImGroupMemberRoleType.TEACHER.equals(n.getRoleType()))
+                        .map(ImGroupMember::getUserId)
+                        .collect(Collectors.toList()));
+
+                savedStudentIdList.addAll(savedMemberList.stream()
+                        .filter(n -> ImGroupMemberRoleType.STUDENT.equals(n.getRoleType()))
+                        .map(ImGroupMember::getUserId)
+                        .collect(Collectors.toList()));
+            }
+
+            // 机构好友保留
+            Teacher teacher = this.getById(userId);
+            List<Long> tenantTeacherIds = new ArrayList<>();
+            List<Long> tenantStudentIds = new ArrayList<>();
+            if (teacher.getTenantId() > 0) {
+                List<Long> teacherIdList = userFriendList.stream().filter(n -> ClientEnum.TEACHER.equals(n.getFriendType())).map(ImUserFriend::getFriendId).collect(Collectors.toList());
+                List<Long> studentIdList = userFriendList.stream().filter(n -> ClientEnum.STUDENT.equals(n.getFriendType())).map(ImUserFriend::getFriendId).collect(Collectors.toList());
+                if (!teacherIdList.isEmpty()) {
+                    List<Long> tenantTeacherIdsTemp = this.lambdaQuery().in(Teacher::getUserId, teacherIdList)
+                            .eq(Teacher::getTenantId, teacher.getTenantId())
+                            .list()
+                            .stream().filter(n -> n.getTenantId() > 0).map(Teacher::getUserId).collect(Collectors.toList());
+                    tenantTeacherIds.addAll(tenantTeacherIdsTemp);
+                }
+                if (!studentIdList.isEmpty()) {
+                    List<Long> tenantStudentIdsTemp = studentService.lambdaQuery().in(Student::getUserId, studentIdList)
+                            .eq(Student::getTenantId, teacher.getTenantId())
+                            .list()
+                            .stream().filter(n -> n.getTenantId() > 0).map(Student::getUserId).collect(Collectors.toList());
+                    tenantStudentIds.addAll(tenantStudentIdsTemp);
+                }
+            }
+
+            // 删除的好友
+            removeFriendList = userFriendList.stream().filter(n -> {
+                if (ClientEnum.STUDENT.equals(n.getFriendType())) {
+                    if (tenantStudentIds.contains(n.getFriendId())) {
+                        return false;
+                    }
+                    return !savedStudentIdList.contains(n.getFriendId());
+                }
+                if (ClientEnum.TEACHER.equals(n.getFriendType())) {
+                    if (tenantTeacherIds.contains(n.getFriendId())) {
+                        return false;
+                    }
+                    return !savedTeacherIdList.contains(n.getFriendId());
+                }
+                return false;
+            }).collect(Collectors.toList());
+        }
+
+        // 删除好友
+        removeFriendList.stream().collect(Collectors.groupingBy(ImUserFriend::getFriendType)).forEach((client, friends) -> {
+            List<Long> friendIds = friends.stream().map(ImUserFriend::getFriendId).collect(Collectors.toList());
+            imUserFriendService.lambdaUpdate()
+                    .eq(ImUserFriend::getUserId, userId)
+                    .eq(ImUserFriend::getClientType, ClientEnum.TEACHER)
+                    .in(ImUserFriend::getFriendId, friendIds)
+                    .eq(ImUserFriend::getFriendType, client)
+                    .remove();
+
+            imUserFriendService.lambdaUpdate()
+                    .eq(ImUserFriend::getFriendId, userId)
+                    .eq(ImUserFriend::getFriendType, ClientEnum.TEACHER)
+                    .in(ImUserFriend::getUserId, friendIds)
+                    .eq(ImUserFriend::getClientType, client)
+                    .remove();
+        });
+
+        if (customerIds.isEmpty()) {
+            return result;
+        }
+        // 去除好友里面存在其他客服关系的好友,只有没有客服好友的好友,才需要与客服建立好友关系
+        List<ImUserFriend> teacherFriends = userFriendList.stream().filter(n -> ClientEnum.TEACHER.equals(n.getFriendType())).collect(Collectors.toList());
+        if (!teacherFriends.isEmpty()) {
+            String teacherFriendIds = teacherFriends.stream().map(n -> n.getFriendId().toString()).distinct().collect(Collectors.joining(","));
+            List<Long> existFriendIds = imUserFriendService.getDao().queryExistCustomerServiceFriend(teacherFriendIds, ClientEnum.TEACHER.getCode())
+                    .stream()
+                    .map(ImUserFriend::getUserId)
+                    .collect(Collectors.toList());
+            userFriendList.removeIf(n -> ClientEnum.TEACHER.equals(n.getFriendType()) && existFriendIds.contains(n.getFriendId()));
+        }
+
+        List<ImUserFriend> studentFriends = userFriendList.stream().filter(n -> ClientEnum.STUDENT.equals(n.getFriendType())).collect(Collectors.toList());
+        if (!studentFriends.isEmpty()) {
+            String studentFriendIds = studentFriends.stream().map(n -> n.getFriendId().toString()).distinct().collect(Collectors.joining(","));
+            List<Long> existFriendIds = imUserFriendService.getDao().queryExistCustomerServiceFriend(studentFriendIds, ClientEnum.STUDENT.getCode())
+                    .stream()
+                    .map(ImUserFriend::getUserId)
+                    .collect(Collectors.toList());
+            userFriendList.removeIf(n -> ClientEnum.STUDENT.equals(n.getFriendType()) && existFriendIds.contains(n.getFriendId()));
+        }
+
+        if (userFriendList.isEmpty()) {
+            return result;
+        }
+
+        // 好友交接
+        Map<Long, Set<Long>> teacherFriendMap = new LinkedHashMap<>();
+        Map<Long, Set<Long>> studentFriendMap = new LinkedHashMap<>();
+        for (ImUserFriend imUserFriend : userFriendList) {
+            TeacherWrapper.TeacherFriend teacherFriend = customerServiceFriendNums.get(0);
+            if (ClientEnum.TEACHER.equals(imUserFriend.getFriendType())) {
+                Set<Long> set = teacherFriendMap.getOrDefault(teacherFriend.getTeacherId(), new HashSet<>());
+                set.add(imUserFriend.getFriendId());
+                teacherFriendMap.put(teacherFriend.getTeacherId(), set);
+            } else if (ClientEnum.STUDENT.equals(imUserFriend.getFriendType())) {
+                Set<Long> set = studentFriendMap.getOrDefault(teacherFriend.getTeacherId(), new HashSet<>());
+                set.add(imUserFriend.getFriendId());
+                studentFriendMap.put(teacherFriend.getTeacherId(), set);
+            }
+            teacherFriend.setFriendNums(teacherFriend.getFriendNums() + 1);
+            Collections.sort(customerServiceFriendNums);
+        }
+
+        teacherFriendMap.forEach((teacherId, teacherIds) -> {
+            imUserFriendService.saveUserTeacherFriend(teacherId, teacherIds);
+            // 发送消息
+            TeacherWrapper.CustomerServiceSendMsg2User customerServiceSendMsg2User = new TeacherWrapper.CustomerServiceSendMsg2User();
+            customerServiceSendMsg2User.setCustomerId(teacherId);
+            customerServiceSendMsg2User.getTeacherIds().addAll(teacherIds);
+            result.add(customerServiceSendMsg2User);
+//            imUserFriendService.sendCustomerServiceAddFriendMessage(teacherId, customerTitle, customerMessage, new ArrayList<>(teacherIds), ClientEnum.TEACHER);
+        });
+        studentFriendMap.forEach((teacherId, studentIds) -> {
+            imUserFriendService.saveUserFriend(teacherId, studentIds);
+            TeacherWrapper.CustomerServiceSendMsg2User customerServiceSendMsg2User = new TeacherWrapper.CustomerServiceSendMsg2User();
+            customerServiceSendMsg2User.setCustomerId(teacherId);
+            customerServiceSendMsg2User.getTeacherIds().addAll(studentIds);
+            result.add(customerServiceSendMsg2User);
+//            imUserFriendService.sendCustomerServiceAddFriendMessage(teacherId, customerTitle, customerMessage, new ArrayList<>(studentIds), ClientEnum.STUDENT);
+        });
+        return result;
+    }
+
     /***
      * 封装用户信息
      * @author liweifan
@@ -721,6 +915,7 @@ public class TeacherServiceImpl extends ServiceImpl<TeacherDao, Teacher> impleme
         teacher.setSettlementFrom(teacherSubmitReq.getSettlementFrom());
         teacher.setTenantId(teacherSubmitReq.getTenantId() == null ? -1L : teacherSubmitReq.getTenantId());
         teacher.setAvatar(Optional.ofNullable(teacherSubmitReq.getAvatar()).orElse(teacher.getAvatar()));
+        teacher.setCustomerService(teacherSubmitReq.getCustomerService());
         if (StringUtil.isEmpty(teacherSubmitReq.getTeacherType())) {
             return teacher;
         }
@@ -1139,7 +1334,10 @@ public class TeacherServiceImpl extends ServiceImpl<TeacherDao, Teacher> impleme
                 });
             }
             // 删除好友关系
-            imUserFriendService.delStudentFriendByTenantId(teacher.getTenantId(), teacher.getUserId(), ClientEnum.TEACHER.getCode());
+            Boolean customerService = teacher.getCustomerService();
+            if (Boolean.FALSE.equals(customerService)) {
+                imUserFriendService.delStudentFriendByTenantId(teacher.getTenantId(), teacher.getUserId(), ClientEnum.TEACHER.getCode());
+            }
             addBindUnBindRecord(teacher.getUserId(), teacher.getTenantId(), false);
             SysUser sysUser = sysUserMapper.getByUserId(teacher.getUserId());
             sendBindUnBindSMS(teacher.getUserId(), sysUser.getPhone(), MessageTypeEnum.TEACHER_UNBIND_TENANT, teacher.getTenantId());
@@ -1287,4 +1485,45 @@ public class TeacherServiceImpl extends ServiceImpl<TeacherDao, Teacher> impleme
             teacherVo.setVipType(vipType);
         }
     }
+
+    /**
+     * 获取学生好友最少的一个客服
+     * @return 客服
+     */
+    private Teacher getCustomerServiceByFriendLeast() {
+        return this.getBaseMapper().getCustomerServiceByFriendLeast();
+    }
+
+    @Override
+    public List<Teacher> getCustomerService() {
+        return this.lambdaQuery()
+                .eq(Teacher::getCustomerService, true)
+                .eq(Teacher::getLockFlag, false)
+                .list();
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void updateLock(SysUser sysUser, Long teacherId) {
+        Teacher teacher = this.getById(teacherId);
+        if (teacher == null) {
+            throw new BizException("无效的老师账号");
+        }
+
+        // 冻结客服,移交好友给其他客服
+        Boolean customerService = teacher.getCustomerService();
+        if (UserLockFlag.NORMAL.equals(teacher.getLockFlag()) && Boolean.TRUE.equals(customerService)) {
+            List<TeacherWrapper.CustomerServiceSendMsg2User> customerServiceSendMsg2User = transferFriend(teacherId, false);
+            String customerMessage = sysConfigService.findConfigValue(SysConfigConstant.CUSTOMER_SERVICE_ADD_MSG);
+            String customerTitle = sysConfigService.findConfigValue(SysConfigConstant.CUSTOMER_SERVICE_ADD_MSG_TITLE);
+            for (TeacherWrapper.CustomerServiceSendMsg2User serviceSendMsg2User : customerServiceSendMsg2User) {
+                imUserFriendService.sendCustomerServiceAddFriendMessage(serviceSendMsg2User.getCustomerId(), customerTitle, customerMessage, serviceSendMsg2User.getTeacherIds(),
+                        ClientEnum.TEACHER);
+                imUserFriendService.sendCustomerServiceAddFriendMessage(serviceSendMsg2User.getCustomerId(), customerTitle, customerMessage, serviceSendMsg2User.getStudentIds(),
+                        ClientEnum.STUDENT);
+            }
+        }
+        teacher.setLockFlag(UserLockFlag.NORMAL.equals(teacher.getLockFlag()) ? UserLockFlag.LOCKED : UserLockFlag.NORMAL);
+        this.updateById(teacher);
+    }
 }

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

@@ -296,11 +296,9 @@ public class TenantApplyRecordServiceImpl extends ServiceImpl<TenantApplyRecordM
         tenantApplyRecord.setVerifyUserId(verifyUserId);
         tenantApplyRecord.setReason(entry.getReason());
         tenantApplyRecord.setBriefIntroduction(applyRecord.getBriefIntroduction());
-
-        applyRecord.setStatus(Boolean.TRUE.equals(ifPass) ? AuthStatusEnum.PASS : AuthStatusEnum.UNPASS);
-        applyRecord.setVerifyUserId(verifyUserId);
-        applyRecord.setReason(entry.getReason());
         if (ifPass == true){
+            tenantApplyRecord.setStatus(AuthStatusEnum.PASS);
+
 
             //机构账户新增逻辑
             TenantInfo tenantInfo = new TenantInfo();
@@ -315,13 +313,19 @@ public class TenantApplyRecordServiceImpl extends ServiceImpl<TenantApplyRecordM
             tenantInfo.setBriefIntroduction(applyRecord.getBriefIntroduction());
             if (tenantInfoService.add(tenantInfo)) {
                 applyRecord.setUserId(tenantInfo.getUserId());
+                tenantApplyRecordMapper.updateById(applyRecord);
             }
+            //更改当前记录的审核状态
+            tenantApplyRecordMapper.updateStatusById(id);
+        } else {
+            tenantApplyRecord.setStatus(AuthStatusEnum.UNPASS);
+            tenantApplyRecordMapper.updateUnpassStatusById(id);
         }
-        tenantApplyRecordMapper.updateById(applyRecord);
-
 
+        String name = tenantEntryRecordMapper.selectName(verifyUserId);
+        tenantApplyRecord.setVerifyUserName(name);
 
-        TenantEntryRecord tenantEntryRecord = JSON.parseObject(JSON.toJSONString(applyRecord), TenantEntryRecord.class);
+        TenantEntryRecord tenantEntryRecord = JSON.parseObject(JSON.toJSONString(tenantApplyRecord), TenantEntryRecord.class);
          tenantEntryRecordMapper.insert(tenantEntryRecord);
 
 

+ 95 - 25
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/VipCardRecordServiceImpl.java

@@ -385,6 +385,28 @@ public class VipCardRecordServiceImpl extends ServiceImpl<VipCardRecordDao, VipC
             int num = DateUtil.daysBetween(new Date(), userVip.getSvipEndDate()) +1;
             userVip.setSvipEndDays(Math.max(num, 0));
         }
+
+        // 设置会员过期时间
+        EVipType expireVipType = userVip.getExpireVipType();
+        List<VipCardRecordWrapper.UserVipInfo> userVipInfos = this.baseMapper.queryUserVipInfo(Collections.singletonList(userId), clientEnum.getCode());
+        if(!userVipInfos.isEmpty()){
+            VipCardRecordWrapper.UserVipInfo userVipInfo = userVipInfos.get(0);
+            Date date = new Date();
+            if (EVipType.VIP.equals(expireVipType) || EVipType.ALL_VIP.equals(expireVipType)) {
+                Date vipEndTime = userVipInfo.getVipEndTime();
+                if (vipEndTime != null && vipEndTime.before(date)) {
+                    double days = Math.ceil((date.getTime() - vipEndTime.getTime()) * 1.0D / (24 * 60 * 60 * 1000));
+                    userVip.setVipExpireDays((int) days);
+                }
+            }
+            if (EVipType.SVIP.equals(expireVipType) || EVipType.ALL_VIP.equals(expireVipType)) {
+                Date svipEndTime = userVipInfo.getSvipEndTime();
+                if (svipEndTime != null && svipEndTime.before(date)) {
+                    double days = Math.ceil((date.getTime() - svipEndTime.getTime()) * 1.0D / (24 * 60 * 60 * 1000));
+                    userVip.setSvipExpireDays((int) days);
+                }
+            }
+        }
         return userVip;
     }
 
@@ -591,6 +613,7 @@ public class VipCardRecordServiceImpl extends ServiceImpl<VipCardRecordDao, VipC
         PeriodEnum period = addVipCardRecord.getType();
         // 扣减永久
         if (PeriodEnum.PERPETUAL.equals(period)) {
+            addVipCardRecord.setTimes(1);
             return deductedSVipPerpetual(addVipCardRecord, vipCardRecordList);
         }
 
@@ -600,6 +623,7 @@ public class VipCardRecordServiceImpl extends ServiceImpl<VipCardRecordDao, VipC
             throw new BizException("剩余扣减数量不足");
         }
 
+        Date now = new Date();
         LocalDateTime maxEndTime = collect.stream().map(VipCardRecord::getEndTime).max(Comparator.naturalOrder()).get().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
         // 扣减后的开始时间
         LocalDateTime deductedStartTime;
@@ -629,15 +653,18 @@ public class VipCardRecordServiceImpl extends ServiceImpl<VipCardRecordDao, VipC
         if (deductedStartDate.before(minStartTime)) {
             double day = (minStartTime.getTime() - deductedStartDate.getTime()) * 1.0D / (24 * 60 * 60 * 1000);
             if (day > 1.0D) {
-//                throw new BizException("剩余扣减数量不足");
+                throw new BizException("剩余扣减数量不足");
             }
+//            deductedStartDate = minStartTime;
         }
 
         // 重新计算会员时间,每一条记录置换成一条新的记录,时间区间重新计算
         List<VipCardRecord> updateRecords = new ArrayList<>();
         Long deductMills = null;
+        Long endDeductMills = null;
 
         EVipType vipType = addVipCardRecord.getVipType();
+        boolean giveFlag = false; // 扣减后,剩余时间前移,第一条记录赠送当天的时间
         for (VipCardRecord vipCardRecord : vipCardRecordList) {
             Date startTime = vipCardRecord.getStartTime();
             Date endTime = vipCardRecord.getEndTime();
@@ -651,28 +678,43 @@ public class VipCardRecordServiceImpl extends ServiceImpl<VipCardRecordDao, VipC
 
             Long addId = null;
             // 扣减到当前时间区间
-            if (deductedStartDate.after(startTime) && deductedStartDate.before(endTime)) {
-                VipCardRecord addRecord = JSON.parseObject(JSON.toJSONString(vipCardRecord), VipCardRecord.class);
-                addRecord.setId(null);
-                addRecord.setDisplayFlag(false);
-                addRecord.setEfficientFlag(true);
-                addRecord.setEndTime(deductedStartDate);
-                addRecord.setStatus(EVipRecordStatus.UPDATE);
-                save(addRecord);
-                addId = addRecord.getId();
+            if ((deductedStartDate.after(startTime) || deductedStartDate.equals(startTime)) && deductedStartDate.before(endTime)) {
+                if (deductedStartDate.after(now)) { // 还有剩余天数,不足一天,补充到当前全天
+                    VipCardRecord addRecord = JSON.parseObject(JSON.toJSONString(vipCardRecord), VipCardRecord.class);
+                    addRecord.setId(null);
+                    addRecord.setDisplayFlag(false);
+                    addRecord.setEfficientFlag(true);
+                    addRecord.setEndTime(formatEnd(deductedStartDate));
+                    addRecord.setStatus(EVipRecordStatus.UPDATE);
+                    save(addRecord);
+                    addId = addRecord.getId();
+                }
+
 
                 deductMills = endTime.getTime() - deductedStartDate.getTime();
+                endDeductMills = endTime.getTime() - formatEnd(deductedStartDate).getTime();
             } else {
-                // 有扣减,整体时间前移
-                if (deductMills != null) {
-                    VipCardRecord newRecord = JSON.parseObject(JSON.toJSONString(vipCardRecord), VipCardRecord.class);
-                    newRecord.setId(null);
-                    newRecord.setEfficientFlag(true);
-                    newRecord.setStartTime(new Date(newRecord.getStartTime().getTime() - deductMills));
-                    newRecord.setEndTime(new Date(newRecord.getEndTime().getTime() - deductMills));
-                    newRecord.setStatus(EVipRecordStatus.UPDATE);
-                    save(newRecord);
-                    addId = newRecord.getId();
+                if (vipCardRecord.getVipType().equals(vipType)) {
+                    deductMills = endTime.getTime() - deductedStartDate.getTime();
+                    endDeductMills = endTime.getTime() - formatEnd(deductedStartDate).getTime();
+                } else {
+                    // 有扣减,整体时间前移
+                    if (deductMills != null) {
+                        VipCardRecord newRecord = JSON.parseObject(JSON.toJSONString(vipCardRecord), VipCardRecord.class);
+                        newRecord.setId(null);
+                        newRecord.setDisplayFlag(false);
+                        newRecord.setEfficientFlag(true);
+                        if (!giveFlag) {
+                            newRecord.setStartTime(new Date(newRecord.getStartTime().getTime() - deductMills));
+                            giveFlag = true;
+                        } else {
+                            newRecord.setStartTime(new Date(newRecord.getStartTime().getTime() - endDeductMills));
+                        }
+                        newRecord.setEndTime(new Date(newRecord.getEndTime().getTime() - endDeductMills));
+                        newRecord.setStatus(EVipRecordStatus.UPDATE);
+                        save(newRecord);
+                        addId = newRecord.getId();
+                    }
                 }
             }
 
@@ -704,27 +746,36 @@ public class VipCardRecordServiceImpl extends ServiceImpl<VipCardRecordDao, VipC
 
         // 最后一个非永久SVIP的结束时间
         Date lastUnPereutalDate = new Date();
-        long deductMills = 0L;
+        Date lastEndUnPereutalDate = formatEnd(new Date());
+        Long deductMills = null;
+        Long endDeductMills = null;
+        boolean giveFlag = false;
         for (VipCardRecord vipCardRecord : vipCardRecordList) {
             if (EVipType.SVIP.equals(vipCardRecord.getVipType())) {
                 // 获取后续VIP向前平移的开始时间
                 if (!PeriodEnum.PERPETUAL.equals(vipCardRecord.getType())) {
                     if (vipCardRecord.getEndTime().after(lastUnPereutalDate)) {
                         lastUnPereutalDate = vipCardRecord.getEndTime();
+                        lastEndUnPereutalDate = vipCardRecord.getEndTime();
                     }
                     continue;
                 }
                 vipCardRecord.setEfficientFlag(false);
                 updateById(vipCardRecord);
             } else {
-                deductMills += vipCardRecord.getStartTime().getTime() - lastUnPereutalDate.getTime();
-
                 VipCardRecord newRecord = JSON.parseObject(JSON.toJSONString(vipCardRecord), VipCardRecord.class);
                 newRecord.setId(null);
                 newRecord.setDisplayFlag(false);
                 newRecord.setEfficientFlag(true);
-                newRecord.setStartTime(new Date(newRecord.getStartTime().getTime() - deductMills));
-                newRecord.setEndTime(new Date(newRecord.getEndTime().getTime() - deductMills));
+                if (vipCardRecord.getVipType().equals(addVipCardRecord.getVipType()) && !giveFlag) {
+                    deductMills = vipCardRecord.getStartTime().getTime() - lastUnPereutalDate.getTime();
+                    endDeductMills = vipCardRecord.getStartTime().getTime() - lastEndUnPereutalDate.getTime();
+                    newRecord.setStartTime(new Date(newRecord.getStartTime().getTime() - deductMills));
+                    giveFlag = true;
+                } else {
+                    newRecord.setStartTime(new Date(newRecord.getStartTime().getTime() - endDeductMills));
+                }
+                newRecord.setEndTime(new Date(newRecord.getEndTime().getTime() - endDeductMills));
                 save(newRecord);
                 Long refId = newRecord.getId();
 
@@ -881,4 +932,23 @@ public class VipCardRecordServiceImpl extends ServiceImpl<VipCardRecordDao, VipC
             log.error("会员添加消息发送失败 : {}", e.getMessage());
         }
     }
+
+    private Date formatEnd(Date date) {
+        Calendar c1 = Calendar.getInstance();
+        c1.setTime(date);
+        c1.set(Calendar.HOUR_OF_DAY, 23);
+        c1.set(Calendar.MINUTE, 59);
+        c1.set(Calendar.SECOND, 59);
+        return c1.getTime();
+    }
+
+    private Date formatStart(Date date) {
+        Calendar c1 = Calendar.getInstance();
+        c1.setTime(date);
+        c1.set(Calendar.HOUR_OF_DAY, 0);
+        c1.set(Calendar.MINUTE, 0);
+        c1.set(Calendar.SECOND, 0);
+        c1.set(Calendar.MILLISECOND, 0);
+        return c1.getTime();
+    }
 }

+ 4 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/TeacherHomeVo.java

@@ -1,5 +1,6 @@
 package com.yonge.cooleshow.biz.dal.vo;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.yonge.cooleshow.biz.dal.entity.Teacher;
 import com.yonge.cooleshow.biz.dal.entity.TeacherStyleVideo;
@@ -102,6 +103,9 @@ public class TeacherHomeVo extends Teacher implements Serializable {
     @ApiModelProperty(value = "imToken")
     private String imToken;
 
+    @ApiModelProperty("是否是客服")
+    private Boolean customerService;
+
 
     public String getTenantName() {
         return tenantName;

+ 2 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/TeacherVo.java

@@ -119,6 +119,8 @@ public class TeacherVo extends Teacher {
     @ApiModelProperty("svip结束时间")
     private Date perSvipEndTime;
 
+    @ApiModelProperty("是否客服")
+    private Boolean customerService;
 
     public YesOrNoEnum getDelFlag() {
         return delFlag;

+ 13 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/im/ImUserFriendVO.java

@@ -3,6 +3,7 @@ package com.yonge.cooleshow.biz.dal.vo.im;
 import com.alibaba.fastjson.JSON;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.enums.ImGroupMemberRoleType;
+import com.yonge.cooleshow.biz.dal.wrapper.VipCardRecordWrapper;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
 import lombok.Data;
@@ -58,6 +59,18 @@ public class ImUserFriendVO {
         @ApiModelProperty(value = "修改时间;")
         private Date updateTime;
 
+        @ApiModelProperty(value = "好友会员信息")
+        private VipCardRecordWrapper.UserVip userVip;
+
+        @ApiModelProperty("声部")
+        private String friendSubjectName;
+
+        @ApiModelProperty("性别,1:男,0:女")
+        private Integer friendGender;
+
+        @ApiModelProperty("生日")
+        private Date friendBirthdate;
+
         public static ImUserFriend from(String record) {
 
             return JSON.parseObject(record, ImUserFriend.class);

+ 6 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/VipCardRecordWrapper.java

@@ -39,6 +39,12 @@ public class VipCardRecordWrapper {
 
         @ApiModelProperty(value = "svip剩余天数")
         private Integer svipEndDays =0;
+
+        @ApiModelProperty("vip失效天数")
+        private Integer vipExpireDays;
+
+        @ApiModelProperty("Svip失效天数")
+        private Integer svipExpireDays;
     }
 
     @ApiModel("添加/扣减会员")

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

@@ -158,4 +158,37 @@ public class TeacherWrapper {
         @ApiModelProperty("下载地址")
         private String downloadPath;
     }
+
+
+    @Data
+    public static class TeacherFriend implements Comparable<TeacherFriend> {
+
+        private Long teacherId;
+
+        private Integer friendNums;
+
+        @Override
+        public int compareTo(TeacherFriend o) {
+            return friendNums.compareTo(o.getFriendNums());
+        }
+    }
+
+
+    /**
+     * 客服发送消息的对象列表
+     */
+    @Data
+    public static class CustomerServiceSendMsg2User {
+
+        @ApiModelProperty("客服ID")
+        private Long customerId;
+
+        @ApiModelProperty("老师好友")
+        private List<Long> teacherIds = new ArrayList<>();
+
+
+        @ApiModelProperty("学生好友")
+        private List<Long> studentIds = new ArrayList<>();
+
+    }
 }

+ 13 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/ImUserFriendMapper.xml

@@ -45,4 +45,17 @@
         WHERE (t1.user_id_ = #{userId} and t1.client_type_ = #{clientType} and t2.tenant_id_ = #{tenantId})
            OR (t1.friend_id_ = #{userId} and t1.friend_type_ = #{clientType} and t3.tenant_id_ = #{tenantId})
     </delete>
+
+    <select id="queryExistCustomerServiceFriend" resultMap="BaseResultMap">
+        select distinct t.*
+        from im_user_friend t
+                 left join teacher te on t.friend_id_ = te.user_id_ and te.customer_service_ = 1 and te.lock_flag_ = 0
+                 left join sys_user su on te.user_id_ = su.id_
+        where t.friend_type_ = 'TEACHER'
+          and find_in_set(t.user_id_, #{userIds})
+          and t.client_type_ = #{clientType}
+          and su.del_flag_ = 0
+          and su.lock_flag_ = 0
+          and te.user_id_ is not null
+    </select>
 </mapper>

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

@@ -40,6 +40,7 @@
         <result column="create_time_" property="createTime"/>
         <result column="update_time_" property="updateTime"/>
         <result column="im_device_id_" property="imDeviceId"/>
+        <result column="customer_service_" property="customerService"/>
     </resultMap>
 
     <resultMap id="HotTeacherVoMap" type="com.yonge.cooleshow.biz.dal.vo.HotTeacherVo">
@@ -91,6 +92,7 @@
         , t.tenant_id_ as tenantId
         , t.settlement_from_ as settlementFrom
         , t.im_device_id_ as imDeviceId
+        , t.customer_service_ as customerService
     </sql>
 
     <!-- 分页查询 -->
@@ -207,6 +209,9 @@
             <if test="param.tenantName != null and param.tenantName.trim() != ''">
                 and ti.name_ like concat('%',#{param.tenantName},'%')
             </if>
+            <if test="param.customerService != null">
+                AND t.customer_service_ = #{param.customerService}
+            </if>
         </where>
         group by t.user_id_
         <if test="param.vipStartTime != null and param.vipEndTime != null">
@@ -229,9 +234,11 @@
         <result property="realName" column="real_name_"/>
         <result property="avatar" column="avatar_"/>
         <result property="phone" column="phone_"/>
+        <result property="gender" column="gender_"/>
+        <result property="birthdate" column="birthdate_"/>
     </resultMap>
     <select id="getBasicUserInfo" resultMap="BasicUserInfo">
-        SELECT id_ user_id_, username_, real_name_, avatar_, phone_
+        SELECT id_ user_id_, username_, real_name_, avatar_, phone_, gender_, birthdate_
         FROM sys_user
         WHERE del_flag_ = 0
           and id_ = #{userId}
@@ -425,4 +432,35 @@
         </where>
     </select>
     <!--老师学生人数统计-->
+
+    <select id="getCustomerServiceByFriendLeast" resultMap="BaseResultMap">
+        select
+        t.*
+        from
+        ( SELECT
+        te.user_id_,
+        count(distinct iuf.friend_id_) friends
+        from teacher te
+        left join sys_user su on te.user_id_ = su.id_
+        left join im_user_friend iuf on te.user_id_ = iuf.user_id_
+        where te.lock_flag_ = 0 and te.customer_service_ = 1 and su.del_flag_ = 0 and su.lock_flag_ = 0
+        group by te.user_id_
+        order by friends
+        limit 1) m
+        left join teacher t on m.user_id_ = t.user_id_
+    </select>
+
+    <select id="getCustomerServiceFriendNums" resultType="com.yonge.cooleshow.biz.dal.wrapper.teacher.TeacherWrapper$TeacherFriend">
+        SELECT te.user_id_                    teacherId,
+               count(distinct iuf.friend_id_) friendNums
+        from teacher te
+                 left join sys_user su on te.user_id_ = su.id_
+                 left join im_user_friend iuf on te.user_id_ = iuf.user_id_ and iuf.client_type_ = 'TEACHER'
+        where te.lock_flag_ = 0
+          and te.customer_service_ = 1
+          and su.del_flag_ = 0
+          and su.lock_flag_ = 0
+        group by te.user_id_
+        order by friendNums
+    </select>
 </mapper>