Explorar o código

Merge remote-tracking branch 'origin/feature/1019-tenant' into feature/1019-tenant

Eric hai 1 ano
pai
achega
83f25d20f8
Modificáronse 56 ficheiros con 3105 adicións e 53 borrados
  1. 143 0
      cooleshow-app/src/main/java/com/yonge/cooleshow/admin/controller/TenantAlbumCategoryController.java
  2. 43 0
      cooleshow-app/src/main/java/com/yonge/cooleshow/admin/controller/TenantGroupController.java
  3. 62 0
      cooleshow-app/src/main/java/com/yonge/cooleshow/admin/io/request/TenantAlbumCategoryDetailVo.java
  4. 90 0
      cooleshow-app/src/main/java/com/yonge/cooleshow/admin/io/request/TenantAlbumCategoryVo.java
  5. 72 4
      cooleshow-app/src/main/java/com/yonge/cooleshow/tenant/controller/IndexController.java
  6. 44 7
      cooleshow-app/src/main/java/com/yonge/cooleshow/tenant/controller/StudentController.java
  7. 44 1
      cooleshow-app/src/main/java/com/yonge/cooleshow/tenant/controller/TeacherController.java
  8. 24 3
      cooleshow-app/src/main/java/com/yonge/cooleshow/tenant/controller/TenantActivationCodeController.java
  9. 98 0
      cooleshow-app/src/main/java/com/yonge/cooleshow/tenant/controller/TenantGroupController.java
  10. 69 0
      cooleshow-app/src/main/java/com/yonge/cooleshow/tenant/vo/TenantGroupVo.java
  11. 29 0
      cooleshow-common/src/main/java/com/yonge/cooleshow/common/enums/ETenantAlbumCategoryType.java
  12. 144 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/TenantAlbumCategoryController.java
  13. 46 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/TenantGroupController.java
  14. 62 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/io/request/TenantAlbumCategoryDetailVo.java
  15. 90 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/io/request/TenantAlbumCategoryVo.java
  16. 6 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/io/request/TenantAlbumVo.java
  17. 3 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/StudentDao.java
  18. 8 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/TenantAlbum.java
  19. 58 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/TenantAlbumCategory.java
  20. 36 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/TenantAlbumCategoryDetail.java
  21. 53 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/TenantGroup.java
  22. 27 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/TenantAlbumCategoryDetailMapper.java
  23. 27 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/TenantAlbumCategoryMapper.java
  24. 31 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/TenantGroupMapper.java
  25. 26 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/queryInfo/SysMusicCompareRecordQueryInfo.java
  26. 1 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ImGroupService.java
  27. 3 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/OssFileService.java
  28. 43 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/TenantAlbumCategoryDetailService.java
  29. 46 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/TenantAlbumCategoryService.java
  30. 52 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/TenantGroupService.java
  31. 2 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImGroupServiceImpl.java
  32. 41 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/OssFileServiceImpl.java
  33. 111 5
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/StudentServiceImpl.java
  34. 16 11
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/TeacherServiceImpl.java
  35. 66 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/TenantAlbumCategoryDetailServiceImpl.java
  36. 202 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/TenantAlbumCategoryServiceImpl.java
  37. 423 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/TenantGroupServiceImpl.java
  38. 16 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/TenantUnbindRecordServiceImpl.java
  39. 21 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/PaymentDivMemberRecordWrapper.java
  40. 9 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/StudentWrapper.java
  41. 63 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/TenantAlbumCategoryDetailWrapper.java
  42. 106 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/TenantAlbumCategoryWrapper.java
  43. 6 3
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/TenantAlbumWrapper.java
  44. 171 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/TenantGroupWrapper.java
  45. 20 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/StudentMapper.xml
  46. 64 10
      cooleshow-user/user-biz/src/main/resources/config/mybatis/SysMusicCompareRecordMapper.xml
  47. 20 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/TenantAlbumCategoryDetailMapper.xml
  48. 35 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/TenantAlbumCategoryMapper.xml
  49. 6 2
      cooleshow-user/user-biz/src/main/resources/config/mybatis/TenantAlbumMapper.xml
  50. 35 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/TenantGroupMapper.xml
  51. 1 1
      cooleshow-user/user-tenant/src/main/java/com/yonge/cooleshow/tenant/controller/StudentController.java
  52. 1 1
      cooleshow-user/user-tenant/src/main/java/com/yonge/cooleshow/tenant/controller/TeacherController.java
  53. 10 2
      cooleshow-user/user-tenant/src/main/java/com/yonge/cooleshow/tenant/controller/TenantActivationCodeController.java
  54. 105 0
      cooleshow-user/user-tenant/src/main/java/com/yonge/cooleshow/tenant/controller/TenantGroupController.java
  55. 3 0
      cooleshow-user/user-tenant/src/main/java/com/yonge/cooleshow/tenant/vo/StudentVo.java
  56. 72 0
      cooleshow-user/user-tenant/src/main/java/com/yonge/cooleshow/tenant/vo/TenantGroupVo.java

+ 143 - 0
cooleshow-app/src/main/java/com/yonge/cooleshow/admin/controller/TenantAlbumCategoryController.java

@@ -0,0 +1,143 @@
+package com.yonge.cooleshow.admin.controller;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.microsvc.toolkit.common.response.paging.PageInfo;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import com.microsvc.toolkit.common.response.template.R;
+import com.yonge.cooleshow.admin.io.request.TenantAlbumCategoryVo;
+import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
+import com.yonge.cooleshow.auth.api.entity.SysUser;
+import com.yonge.cooleshow.biz.dal.entity.TenantAlbumCategory;
+import com.yonge.cooleshow.biz.dal.entity.TenantAlbumCategoryDetail;
+import com.yonge.cooleshow.biz.dal.service.SysUserService;
+import com.yonge.cooleshow.biz.dal.service.TenantAlbumCategoryDetailService;
+import com.yonge.cooleshow.biz.dal.service.TenantAlbumCategoryService;
+import com.yonge.cooleshow.biz.dal.wrapper.TenantAlbumCategoryWrapper;
+import com.yonge.toolset.base.exception.BizException;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+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;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Validated
+@RestController
+@RequestMapping("${app-config.url.admin:}/tenantAlbumCategory")
+@Api(tags = "机构专辑分类")
+public class TenantAlbumCategoryController {
+
+    @Autowired
+    private TenantAlbumCategoryService tenantAlbumCategoryService;
+
+    @Autowired
+    private TenantAlbumCategoryDetailService tenantAlbumCategoryDetailService;
+
+    @Resource
+    private SysUserFeignService sysUserFeignService;
+
+    @Autowired
+    private SysUserService sysUserService;
+
+    @ApiOperation(value = "详情", notes = "机构专辑分类-根据详情ID查询单条, 传入id")
+    @PreAuthorize("@auditsvc.hasPermissions('tenantAlbumCategory/detail', {'BACKEND'})")
+    @GetMapping("/detail/{id}")
+    public R<TenantAlbumCategoryWrapper.TenantAlbumCategory> detail(@PathVariable("id") Long id) {
+        return R.from(tenantAlbumCategoryService.detail(id));
+    }
+
+    @ApiOperation(value = "查询分页", notes = "机构专辑分类- 传入 TenantAlbumCategoryVo.TenantAlbumCategoryQuery")
+    @PreAuthorize("@auditsvc.hasPermissions('tenantAlbumCategory/page', {'BACKEND'})")
+    @PostMapping("/page")
+    public R<PageInfo<TenantAlbumCategoryVo.TenantAlbumCategory>> page(@RequestBody TenantAlbumCategoryWrapper.TenantAlbumCategoryQuery query) {
+
+        // 查询数据
+        IPage<TenantAlbumCategory> pages = tenantAlbumCategoryService.selectPage(QueryInfo.getPage(query), query);
+        // 数据类型转换
+        List<TenantAlbumCategoryVo.TenantAlbumCategory> records = JSON.parseArray(JSON.toJSONString(pages.getRecords()),
+                TenantAlbumCategoryVo.TenantAlbumCategory.class);
+
+        if (CollectionUtils.isNotEmpty(records)) {
+            List<Long> updateUserIds =
+                    records.stream().map(TenantAlbumCategoryVo.TenantAlbumCategory::getUpdateBy).distinct().collect(Collectors.toList());
+            Map<Long, com.yonge.cooleshow.biz.dal.entity.SysUser> mapByIds = sysUserService.getMapByIds(updateUserIds);
+            records.forEach(next -> {
+                com.yonge.cooleshow.biz.dal.entity.SysUser sysUser = mapByIds.get(next.getUpdateBy());
+                next.setUpdateUserName(sysUser == null ? "" : sysUser.getUsername());
+            });
+        }
+
+        return R.from(QueryInfo.pageInfo(pages, records));
+    }
+
+    @ApiOperation(value = "新增", notes = "机构专辑分类- 传入 TenantAlbumCategoryVo.TenantAlbumCategory")
+    @PreAuthorize("@auditsvc.hasPermissions('tenantAlbumCategory/save', {'BACKEND'})")
+    @PostMapping("/save")
+    public R<JSONObject> add(@Validated @RequestBody TenantAlbumCategoryVo.TenantAlbumCategory tenantAlbumCategoryVo) {
+        SysUser sysUser = sysUserFeignService.queryUserInfo();
+        // 新增数据
+        TenantAlbumCategoryWrapper.TenantAlbumCategory albumCategory =
+                JSON.parseObject(tenantAlbumCategoryVo.jsonString(),
+                        TenantAlbumCategoryWrapper.TenantAlbumCategory.class);
+        albumCategory.setCreateBy(sysUser.getId());
+        albumCategory.setUpdateBy(sysUser.getId());
+        tenantAlbumCategoryService.add(albumCategory);
+
+        return R.defaultR();
+    }
+
+    @ApiOperation(value = "修改", notes = "机构专辑分类- 传入 TenantAlbumCategoryVo.TenantAlbumCategory")
+    @PreAuthorize("@auditsvc.hasPermissions('tenantAlbumCategory/update', {'BACKEND'})")
+    @PostMapping("/update")
+    public R<JSONObject> update(@Validated @RequestBody TenantAlbumCategoryVo.TenantAlbumCategory tenantAlbumCategoryVo) {
+        SysUser sysUser = sysUserFeignService.queryUserInfo();
+        // 更新数据
+        TenantAlbumCategoryWrapper.TenantAlbumCategory albumCategory =
+                JSON.parseObject(tenantAlbumCategoryVo.jsonString(),
+                        TenantAlbumCategoryWrapper.TenantAlbumCategory.class);
+        albumCategory.setUpdateBy(sysUser.getId());
+        tenantAlbumCategoryService.update(albumCategory);
+
+        return R.defaultR();
+    }
+
+    @ApiOperation(value = "删除", notes = "机构专辑分类- 传入id")
+    @PreAuthorize("@auditsvc.hasPermissions('tenantAlbumCategory/remove', {'BACKEND'})")
+    @PostMapping("/remove")
+    public R<Boolean> remove(@RequestParam Long id) {
+
+        return R.from(tenantAlbumCategoryService.delete(id));
+    }
+
+    @ApiOperation(value = "获取专辑分类值列表")
+    @PreAuthorize("@auditsvc.hasPermissions('tenantAlbumCategory/queryCategoryValue', {'BACKEND'})")
+    @GetMapping("/queryCategoryValue")
+    public R<List<TenantAlbumCategoryDetail>> queryCategoryValue(@RequestParam("id") Long id) {
+        TenantAlbumCategory albumCategory = tenantAlbumCategoryService.getById(id);
+        if (albumCategory == null) {
+            throw new BizException("专辑分类不存在");
+        }
+        List<TenantAlbumCategoryDetail> detailList = tenantAlbumCategoryDetailService.lambdaQuery()
+                .eq(TenantAlbumCategoryDetail::getTenantAlbumCategoryId, id)
+                .list();
+        return R.from(detailList);
+    }
+}

+ 43 - 0
cooleshow-app/src/main/java/com/yonge/cooleshow/admin/controller/TenantGroupController.java

@@ -0,0 +1,43 @@
+package com.yonge.cooleshow.admin.controller;
+
+import com.microsvc.toolkit.common.response.paging.PageInfo;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import com.microsvc.toolkit.common.response.template.R;
+import com.yonge.cooleshow.biz.dal.service.TenantGroupService;
+import com.yonge.cooleshow.biz.dal.wrapper.TenantGroupWrapper;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@Slf4j
+@Validated
+@RestController
+@RequestMapping("${app-config.url.admin:}/tenantGroup")
+@Api(tags = "机构小组表")
+public class TenantGroupController {
+
+    @Autowired
+    private TenantGroupService tenantGroupService;
+
+    @ApiOperation(value = "查询分页", notes = "机构小组表- 传入 TenantGroupWrapper.TenantGroupQuery")
+    @PreAuthorize("@auditsvc.hasPermissions('tenantGroup/page', {'BACKEND'})")
+    @PostMapping("/page")
+    public R<PageInfo<TenantGroupWrapper.TenantGroup>> page(@RequestBody TenantGroupWrapper.TenantGroupQuery query) {
+        return R.from(QueryInfo.pageInfo(tenantGroupService.selectPage(QueryInfo.getPage(query), query)));
+    }
+
+    @ApiOperation(value = "处理机构小组交接", notes = "处理机构小组交接")
+    @PreAuthorize("@auditsvc.hasPermissions('tenantGroup/adjustTenantGroup', {'BACKEND'})")
+    @PostMapping("/adjustTenantGroup")
+    public R<Boolean> adjustTenantGroup(@RequestBody TenantGroupWrapper.AdjustTenantGroup adjustTenantGroup) {
+        return R.from(tenantGroupService.adjustTenantGroup(adjustTenantGroup));
+    }
+
+}

+ 62 - 0
cooleshow-app/src/main/java/com/yonge/cooleshow/admin/io/request/TenantAlbumCategoryDetailVo.java

@@ -0,0 +1,62 @@
+package com.yonge.cooleshow.admin.io.request;
+
+import com.alibaba.fastjson.JSON;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import lombok.Data;
+
+/**
+ * 机构专辑分类详情
+ * 2023-10-10 10:17:38
+ */
+@ApiModel(value = "TenantAlbumCategoryDetailVo对象", description = "机构专辑分类详情查询视图对象")
+public class TenantAlbumCategoryDetailVo {
+
+    @Data
+    @ApiModel(" TenantAlbumCategoryDetailQuery-机构专辑分类详情")
+    public static class TenantAlbumCategoryDetailQuery implements QueryInfo {
+
+        @ApiModelProperty("当前页")
+        private Integer page;
+
+        @ApiModelProperty("分页行数")
+        private Integer rows;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantAlbumCategoryDetailQuery from(String json) {
+            return JSON.parseObject(json, TenantAlbumCategoryDetailQuery.class);
+        }
+    }
+
+    @Data
+    @ApiModel(" TenantAlbumCategoryDetail-机构专辑分类详情")
+    public static class TenantAlbumCategoryDetail {
+
+
+        @ApiModelProperty("主键ID")
+        private Long id;
+
+
+        @ApiModelProperty("机构专辑分类ID")
+        private Long tenantAlbumCategoryId;
+
+
+        @ApiModelProperty("级别/类别")
+        private String value;
+
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantAlbumCategoryDetail from(String json) {
+            return JSON.parseObject(json, TenantAlbumCategoryDetail.class);
+        }
+    }
+
+}

+ 90 - 0
cooleshow-app/src/main/java/com/yonge/cooleshow/admin/io/request/TenantAlbumCategoryVo.java

@@ -0,0 +1,90 @@
+package com.yonge.cooleshow.admin.io.request;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import com.yonge.cooleshow.biz.dal.entity.TenantAlbumCategoryDetail;
+import com.yonge.cooleshow.biz.dal.wrapper.TenantAlbumCategoryDetailWrapper;
+import com.yonge.cooleshow.common.enums.ETenantAlbumCategoryType;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import lombok.Data;
+
+/**
+ * 机构专辑分类
+ * 2023-10-10 09:48:46
+ */
+@ApiModel(value = "TenantAlbumCategoryVo对象", description = "机构专辑分类查询视图对象")
+public class TenantAlbumCategoryVo {
+
+    @Data
+    @ApiModel(" TenantAlbumCategoryQuery-机构专辑分类")
+    public static class TenantAlbumCategoryQuery implements QueryInfo {
+
+        @ApiModelProperty("当前页")
+        private Integer page;
+
+        @ApiModelProperty("分页行数")
+        private Integer rows;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantAlbumCategoryQuery from(String json) {
+            return JSON.parseObject(json, TenantAlbumCategoryQuery.class);
+        }
+    }
+
+    @Data
+    @ApiModel(" TenantAlbumCategory-机构专辑分类")
+    public static class TenantAlbumCategory {
+
+
+        @ApiModelProperty("主键ID")
+        private Long id;
+
+
+        @ApiModelProperty("专辑分类名称")
+        private String name;
+
+        @ApiModelProperty("创建人")
+        private Long createBy;
+
+        @ApiModelProperty("创建时间")
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+        private Date createTime;
+
+        @ApiModelProperty("修改人")
+        private Long updateBy;
+
+        @ApiModelProperty("修改人")
+        private String updateUserName;
+
+        @ApiModelProperty("更新时间")
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+        private Date updateTime;
+
+
+        @ApiModelProperty("专辑分类类别,CATEGORY_TYPE:类型,CATEGORY_LEVEL:级别")
+        private ETenantAlbumCategoryType categoryType;
+
+        @ApiModelProperty("专辑分类级别/类别值")
+        private List<TenantAlbumCategoryDetail> values = new ArrayList<>();
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantAlbumCategory from(String json) {
+            return JSON.parseObject(json, TenantAlbumCategory.class);
+        }
+    }
+
+}

+ 72 - 4
cooleshow-app/src/main/java/com/yonge/cooleshow/tenant/controller/IndexController.java

@@ -5,12 +5,11 @@ import com.microsvc.toolkit.common.webportal.exception.BizException;
 import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
 import com.yonge.cooleshow.auth.api.entity.SysUser;
 import com.yonge.cooleshow.biz.dal.queryInfo.SysMusicCompareRecordQueryInfo;
-import com.yonge.cooleshow.biz.dal.service.PaymentDivMemberRecordService;
-import com.yonge.cooleshow.biz.dal.service.SysMusicCompareRecordService;
-import com.yonge.cooleshow.biz.dal.service.TenantPersonStatService;
-import com.yonge.cooleshow.biz.dal.service.UserOrderService;
+import com.yonge.cooleshow.biz.dal.service.*;
+import com.yonge.cooleshow.biz.dal.wrapper.OssFileWrapper;
 import com.yonge.cooleshow.biz.dal.wrapper.PaymentDivMemberRecordWrapper;
 import com.yonge.cooleshow.biz.dal.wrapper.TenantPersonStatWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.teacher.TeacherWrapper;
 import com.yonge.cooleshow.common.controller.BaseController;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
 import com.yonge.toolset.base.page.PageInfo;
@@ -24,7 +23,11 @@ import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 @Api(tags = "首页统计")
 @RequestMapping("${app-config.url.tenant:}/index")
@@ -41,6 +44,8 @@ public class IndexController extends BaseController {
     private PaymentDivMemberRecordService paymentDivMemberRecordService;
     @Autowired
     private UserOrderService userOrderService;
+    @Autowired
+    private OssFileService ossFileService;
 
 
     @ApiOperation("学员训练数据统计列表")
@@ -58,6 +63,39 @@ public class IndexController extends BaseController {
         return succeed(sysMusicCompareRecordService.weChatStudentTrainData(queryInfo));
     }
 
+    @ApiOperation("导出学员训练数据统计列表")
+    @PostMapping("exportStudentTrainData")
+    public HttpResponseResult<OssFileWrapper.ExportFile> exportStudentTrainData
+            (@RequestBody SysMusicCompareRecordQueryInfo.WechatCompareRecordQueryInfo queryInfo){
+        SysUser sysUser = sysUserFeignService.queryUserInfo();
+        if (sysUser == null) {
+            return failed("获取用户信息失败");
+        }
+        if(StringUtils.isEmpty(queryInfo.getStartTime())){
+            throw new BizException("请选择筛选时间");
+        }
+        queryInfo.setTenantId(sysUser.getTenantId());
+        List<SysMusicCompareRecordQueryInfo.WechatCompareRecordPageDto> rows = sysMusicCompareRecordService.weChatStudentTrainData(queryInfo).getRows();
+        if (rows.isEmpty()) {
+            throw new BizException("没有可导出数据");
+        }
+
+        List<SysMusicCompareRecordQueryInfo.WechatCompareRecordExportDto> templates = rows.stream().map(next -> {
+            SysMusicCompareRecordQueryInfo.WechatCompareRecordExportDto template = new SysMusicCompareRecordQueryInfo.WechatCompareRecordExportDto();
+            template.setUsername(next.getUsername());
+            template.setSubjectName(next.getSubjectName());
+            template.setAvgTrainTimes(next.getAvgTrainTimes());
+            template.setTrainTimes(next.getTrainTimes());
+            template.setTrainDays(next.getTrainDays());
+            return template;
+        }).collect(Collectors.toList());
+
+        String format = new SimpleDateFormat("yyyyMMddHHmm").format(new Date());
+        OssFileWrapper.ExportFile exportFile = ossFileService.uploadFile(templates,
+                TeacherWrapper.ExportTeacherTemplate.class, format, "练习统计列表");
+        return succeed(exportFile);
+    }
+
 
     @ApiOperation("首页学员训练数据统计列表、汇总")
     @PostMapping("studentTrainData")
@@ -125,6 +163,36 @@ public class IndexController extends BaseController {
         return succeed(PageUtil.pageInfo(paymentDivMemberRecordService.queryIncome(QueryInfo.getPage(query), query)));
     }
 
+    @ApiOperation("导出收入明细列表")
+    @PostMapping("exportIncome")
+    public HttpResponseResult<OssFileWrapper.ExportFile>
+    exportIncome(@RequestBody PaymentDivMemberRecordWrapper.IndexIncomeQuery query){
+        SysUser sysUser = sysUserFeignService.queryUserInfo();
+        if (sysUser == null) {
+            return failed("获取用户信息失败");
+        }
+        query.setTenantId(sysUser.getTenantId());
+        List<PaymentDivMemberRecordWrapper.IndexIncomeQueryDto> rows = paymentDivMemberRecordService.queryIncome(QueryInfo.getPage(query), query).getRecords();
+        if (rows.isEmpty()) {
+            throw new BizException("没有可导出数据");
+        }
+
+        List<PaymentDivMemberRecordWrapper.IndexIncomeExportDto> templates = rows.stream().map(next -> {
+            PaymentDivMemberRecordWrapper.IndexIncomeExportDto template = new PaymentDivMemberRecordWrapper.IndexIncomeExportDto();
+            template.setName(next.getName());
+            template.setPhone(next.getPhone());
+            template.setAmount(next.getAmount());
+            template.setPayTime(next.getPayTime());
+            template.setOrderType(next.getOrderType().getName());
+            return template;
+        }).collect(Collectors.toList());
+
+        String format = new SimpleDateFormat("yyyyMMddHHmm").format(new Date());
+        OssFileWrapper.ExportFile exportFile = ossFileService.uploadFile(templates,
+                TeacherWrapper.ExportTeacherTemplate.class, format, "练习统计列表");
+        return succeed(exportFile);
+    }
+
     @ApiOperation("收入订单详情")
     @PostMapping("getIncomeOrderDetail")
     public HttpResponseResult<PaymentDivMemberRecordWrapper.OrderDetailDto> getIncomeOrderDetail(String orderNo){

+ 44 - 7
cooleshow-app/src/main/java/com/yonge/cooleshow/tenant/controller/StudentController.java

@@ -10,11 +10,13 @@ import com.yonge.cooleshow.biz.dal.entity.TenantActivationCode;
 import com.yonge.cooleshow.biz.dal.entity.TenantInfo;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.service.ImUserFriendService;
+import com.yonge.cooleshow.biz.dal.service.OssFileService;
 import com.yonge.cooleshow.biz.dal.service.StudentService;
 import com.yonge.cooleshow.biz.dal.service.TeacherService;
 import com.yonge.cooleshow.biz.dal.service.TenantActivationCodeService;
 import com.yonge.cooleshow.biz.dal.service.TenantInfoService;
 import com.yonge.cooleshow.biz.dal.vo.StudentVo;
+import com.yonge.cooleshow.biz.dal.wrapper.OssFileWrapper;
 import com.yonge.cooleshow.biz.dal.wrapper.StudentWrapper;
 import com.yonge.cooleshow.common.controller.BaseController;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
@@ -44,7 +46,10 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.multipart.MultipartFile;
 
+import javax.annotation.Resource;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -58,18 +63,17 @@ public class StudentController extends BaseController {
 
     @Autowired
     private StudentService studentService;
-    @Autowired
-    private TeacherService teacherService;
-    @Autowired
+    @Resource
     private SysUserFeignService sysUserFeignService;
     @Autowired
-    private ImUserFriendService imUserFriendService;
-    @Autowired
     private TenantInfoService tenantInfoService;
 
     @Autowired
     private TenantActivationCodeService tenantActivationCodeService;
 
+    @Autowired
+    private OssFileService ossFileService;
+
     @GetMapping("/detail/{id}")
     @ApiOperation(value = "详情", notes = "传入id")
     public HttpResponseResult<StudentVo> detail(@PathVariable("id") Long id) {
@@ -93,7 +97,7 @@ public class StudentController extends BaseController {
         rows.stream().forEach(r->{
             if (StringUtils.isEmpty(r.getRealName())){
                 if (StringUtils.isNotEmpty(r.getUsername()))
-                r.setRealName(r.getUsername());
+                    r.setRealName(r.getUsername());
             }
         });
 
@@ -102,7 +106,7 @@ public class StudentController extends BaseController {
         if (!rows.isEmpty() && query.getTenantAlbumPurchaseId() != null) {
             List<String> studentPhones =
                     rows.stream().map(StudentVo::getPhone).filter(StringUtils::isNotEmpty).collect(Collectors.toList());
-             groupByPhone = tenantActivationCodeService.lambdaQuery()
+            groupByPhone = tenantActivationCodeService.lambdaQuery()
                     .eq(TenantActivationCode::getId, query.getTenantAlbumPurchaseId())
                     .in(TenantActivationCode::getActivationPhone, studentPhones)
                     .list().stream().collect(Collectors.groupingBy(TenantActivationCode::getActivationPhone));
@@ -126,6 +130,39 @@ public class StudentController extends BaseController {
         return succeed(PageUtil.pageInfo(pages));
     }
 
+    @PostMapping("/exportStudent")
+    @ApiOperation(value = "导出学生", notes = "传入TeacherSearch")
+    public HttpResponseResult<OssFileWrapper.ExportFile> exportTeacher(@RequestBody StudentSearch query) {
+        TenantInfo tenantInfo = getTenantInfo();
+        query.setTenantId(tenantInfo.getId());
+        query.setDelFlag(YesOrNoEnum.NO);
+        query.setLockFlag(UserLockFlag.NORMAL);
+        query.setOrderBy("u.username_ asc");
+        query.setPage(1);
+        query.setRows(9999);
+
+        IPage<StudentVo> pages = studentService.selectPage(PageUtil.getPage(query), query);
+        List<StudentVo> rows = pages.getRecords();
+
+        if (rows.isEmpty()) {
+            throw new BizException("没有可导出数据");
+        }
+
+        List<StudentWrapper.ExportStudentTemplate> templateList = rows.stream().map(next -> {
+            StudentWrapper.ExportStudentTemplate template = new StudentWrapper.ExportStudentTemplate();
+            template.setUserName(next.getUsername());
+            template.setPhone(next.getPhone());
+            template.setSubjectName(next.getSubjectName());
+            template.setTenantGroupName(next.getTenantGroupName());
+            return template;
+        }).collect(Collectors.toList());
+
+        String format = new SimpleDateFormat("yyyyMMddHHmm").format(new Date());
+        OssFileWrapper.ExportFile exportFile = ossFileService.uploadFile(templateList,
+                StudentWrapper.ExportStudentTemplate.class, format, "学生");
+        return succeed(exportFile);
+    }
+
     @PostMapping("/save")
     @ApiOperation(value = "新增/修改", notes = "传入Student")
     public HttpResponseResult<Boolean> save(

+ 44 - 1
cooleshow-app/src/main/java/com/yonge/cooleshow/tenant/controller/TeacherController.java

@@ -10,9 +10,11 @@ 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.enums.AuthStatusEnum;
+import com.yonge.cooleshow.biz.dal.service.OssFileService;
 import com.yonge.cooleshow.biz.dal.service.TeacherService;
 import com.yonge.cooleshow.biz.dal.service.TenantInfoService;
 import com.yonge.cooleshow.biz.dal.vo.TeacherVo;
+import com.yonge.cooleshow.biz.dal.wrapper.OssFileWrapper;
 import com.yonge.cooleshow.biz.dal.wrapper.teacher.TeacherWrapper;
 import com.yonge.cooleshow.common.controller.BaseController;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
@@ -33,7 +35,10 @@ import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import javax.annotation.Resource;
 import javax.validation.Valid;
+import java.text.SimpleDateFormat;
+import java.util.Date;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -45,12 +50,15 @@ public class TeacherController extends BaseController {
     @Autowired
     private TeacherService teacherService;
 
-    @Autowired
+    @Resource
     private SysUserFeignService sysUserFeignService;
 
     @Autowired
     private TenantInfoService tenantInfoService;
 
+    @Autowired
+    private OssFileService ossFileService;
+
     /**
      * 查询单条
      */
@@ -102,6 +110,41 @@ public class TeacherController extends BaseController {
         return succeed(PageUtil.pageInfo(pages));
     }
 
+
+    @PostMapping("/exportTeacher")
+    @ApiOperation(value = "导出老师", notes = "传入TeacherSearch")
+    public HttpResponseResult<OssFileWrapper.ExportFile> exportTeacher(@RequestBody TeacherSearch query) {
+        TenantInfo tenantInfo = getTenantInfo();
+        query.setTenantId(tenantInfo.getId());
+        query.setDelFlag(YesOrNoEnum.NO);
+        query.setLockFlag(UserLockFlag.NORMAL);
+        query.setPage(1);
+        query.setRows(9999);
+        query.setOrderBy("u.username_ asc");
+
+        IPage<TeacherVo> pages = teacherService.selectPage(PageUtil.getPage(query), query);
+        List<TeacherVo> rows = pages.getRecords();
+
+        if (rows.isEmpty()) {
+            throw new BizException("没有可导出数据");
+        }
+
+        List<TeacherWrapper.ExportTeacherTemplate> templates = rows.stream().map(next -> {
+            TeacherWrapper.ExportTeacherTemplate template = new TeacherWrapper.ExportTeacherTemplate();
+            template.setUserName(next.getUsername());
+            template.setPhone(next.getPhone());
+            template.setMusicSheet(next.getMusicSheetName());
+            template.setSubjectName(next.getSubjectName());
+            template.setSettlementFrom(next.getSettlementFrom() == null ? "" : next.getSettlementFrom().getMsg());
+            return template;
+        }).collect(Collectors.toList());
+
+        String format = new SimpleDateFormat("yyyyMMddHHmm").format(new Date());
+        OssFileWrapper.ExportFile exportFile = ossFileService.uploadFile(templates,
+                TeacherWrapper.ExportTeacherTemplate.class, format, "老师");
+        return succeed(exportFile);
+    }
+
     /**
      * 新增或修改
      */

+ 24 - 3
cooleshow-app/src/main/java/com/yonge/cooleshow/tenant/controller/TenantActivationCodeController.java

@@ -16,13 +16,19 @@ import com.microsvc.toolkit.middleware.oss.impl.TencentOssPlugin;
 import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
 import com.yonge.cooleshow.auth.api.entity.SysUser;
 import com.yonge.cooleshow.biz.dal.entity.TenantActivationCode;
+import com.yonge.cooleshow.biz.dal.entity.TenantAlbum;
 import com.yonge.cooleshow.biz.dal.entity.TenantAlbumPurchase;
 import com.yonge.cooleshow.biz.dal.entity.TenantInfo;
 import com.yonge.cooleshow.biz.dal.entity.TenantStaff;
+import com.yonge.cooleshow.biz.dal.entity.UserOrder;
+import com.yonge.cooleshow.biz.dal.service.OssFileService;
 import com.yonge.cooleshow.biz.dal.service.TenantActivationCodeService;
 import com.yonge.cooleshow.biz.dal.service.TenantAlbumPurchaseService;
+import com.yonge.cooleshow.biz.dal.service.TenantAlbumService;
 import com.yonge.cooleshow.biz.dal.service.TenantInfoService;
 import com.yonge.cooleshow.biz.dal.service.TenantStaffService;
+import com.yonge.cooleshow.biz.dal.service.UserOrderService;
+import com.yonge.cooleshow.biz.dal.wrapper.OssFileWrapper;
 import com.yonge.cooleshow.biz.dal.wrapper.TenantActivationCodeWrapper;
 import com.yonge.cooleshow.common.controller.BaseController;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
@@ -48,6 +54,7 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.multipart.MultipartFile;
 
+import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
 import java.io.File;
 import java.text.MessageFormat;
@@ -71,7 +78,7 @@ public class TenantActivationCodeController extends BaseController {
     @Autowired
     private TenantAlbumPurchaseService tenantAlbumPurchaseService;
 
-    @Autowired
+    @Resource
     private SysUserFeignService sysUserFeignService;
 
     @Autowired
@@ -81,6 +88,12 @@ public class TenantActivationCodeController extends BaseController {
     private TenantStaffService tenantStaffService;
     @Autowired
     private OssPluginContext ossPluginContext;
+    @Autowired
+    private UserOrderService userOrderService;
+    @Autowired
+    private TenantAlbumService tenantAlbumService;
+    @Autowired
+    private OssFileService ossFileService;
 
     @ApiOperation(value = "详情", notes = "机构激活码-根据详情ID查询单条, 传入id")
 //    @GetMapping("/detail/{id}")
@@ -186,7 +199,7 @@ public class TenantActivationCodeController extends BaseController {
 
     @GetMapping("/exportActiveCode")
     @ApiOperation(value = "导出模板")
-    public HttpResponseResult<TenantActivationCodeWrapper.ExportFile> exportActiveCode(HttpServletResponse response, String orderNo) {
+    public HttpResponseResult<OssFileWrapper.ExportFile> exportActiveCode(HttpServletResponse response, String orderNo) {
         SysUser sysUser = sysUserFeignService.queryUserInfo();
         if (sysUser == null) {
             throw new BizException("请登录");
@@ -212,7 +225,15 @@ public class TenantActivationCodeController extends BaseController {
         List<TenantActivationCodeWrapper.ExportTemplate> templates = JSONArray.parseArray(JSON.toJSONString(rows), TenantActivationCodeWrapper.ExportTemplate.class);
         templates.forEach(next->next.setActivationPhone(""));
 
-        TenantActivationCodeWrapper.ExportFile exportFile = generateExportExcelFile(templates, TenantActivationCodeWrapper.ExportTemplate.class, "激活码导出", "激活码");
+        TenantAlbumPurchase tenantAlbumPurchase = tenantAlbumPurchaseService.getById(Long.valueOf(orderNo));
+        TenantAlbum tenantAlbum = tenantAlbumService.getById(tenantAlbumPurchase.getTenantAlbumId());
+        UserOrder userOrder = userOrderService.getByOrderNo(tenantAlbumPurchase.getOrderNo());
+
+        String format = new SimpleDateFormat("yyyyMMddHHmm").format(userOrder.getCreateTime());
+
+
+        OssFileWrapper.ExportFile exportFile = ossFileService.uploadFile(templates,
+                TenantActivationCodeWrapper.ExportTemplate.class, format + tenantAlbum.getName(), "激活码");
         return succeed(exportFile);
     }
 

+ 98 - 0
cooleshow-app/src/main/java/com/yonge/cooleshow/tenant/controller/TenantGroupController.java

@@ -0,0 +1,98 @@
+package com.yonge.cooleshow.tenant.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.microsvc.toolkit.common.response.paging.PageInfo;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import com.microsvc.toolkit.common.response.template.R;
+import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
+import com.yonge.cooleshow.auth.api.entity.SysUser;
+import com.yonge.cooleshow.biz.dal.entity.TenantGroup;
+import com.yonge.cooleshow.biz.dal.service.TenantGroupService;
+import com.yonge.cooleshow.biz.dal.wrapper.TenantGroupWrapper;
+import com.yonge.cooleshow.tenant.vo.TenantGroupVo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+@Slf4j
+@Validated
+@RestController
+@RequestMapping("${app-config.url.tenant:}/tenantGroup")
+@Api(tags = "机构小组表")
+public class TenantGroupController {
+
+    @Autowired
+    private TenantGroupService tenantGroupService;
+
+    @Resource
+    private SysUserFeignService sysUserFeignService;
+
+    @ApiOperation(value = "详情", notes = "机构小组表-根据详情ID查询单条, 传入id")
+    @GetMapping("/detail/{id}")
+    public R<TenantGroup> detail(@PathVariable("id") Long id) {
+        TenantGroup wrapper = tenantGroupService.detail(id);
+        return R.from(wrapper);
+    }
+
+    @ApiOperation(value = "查询机构群成员", notes = "查询机构群成员")
+    @GetMapping("/queryGroupMember/{tenantGroupId}")
+    public R<List<TenantGroupWrapper.TenantGroupMember>> queryGroupMember(@PathVariable("tenantGroupId") Long tenantGroupId) {
+        SysUser sysUser = sysUserFeignService.queryUserInfo();
+        return R.from(tenantGroupService.queryGroupMember(sysUser.getTenantId(), tenantGroupId));
+    }
+
+    @ApiOperation(value = "查询分页", notes = "机构小组表- 传入 TenantGroupWrapper.TenantGroupQuery")
+    @PostMapping("/page")
+    public R<PageInfo<TenantGroupWrapper.TenantGroup>> page(@RequestBody TenantGroupWrapper.TenantGroupQuery query) {
+
+        IPage<TenantGroupWrapper.TenantGroup> pages = tenantGroupService.selectPage(QueryInfo.getPage(query), query);
+
+        return R.from(QueryInfo.pageInfo(pages));
+    }
+
+    @ApiOperation(value = "新增", notes = "机构小组表- 传入 TenantGroupWrapper.TenantGroup")
+    @PostMapping("/save")
+    public R<JSONObject> add(@Validated @RequestBody TenantGroupVo.TenantGroup tenantGroup) {
+        SysUser sysUser = sysUserFeignService.queryUserInfo();
+        TenantGroupWrapper.TenantGroup group = TenantGroupWrapper.TenantGroup.from(tenantGroup.jsonString());
+        group.setCreateBy(sysUser.getId());
+        group.setTenantId(sysUser.getTenantId());
+        // 新增数据
+        tenantGroupService.add(group);
+
+        return R.defaultR();
+    }
+
+    @ApiOperation(value = "修改", notes = "机构小组表- 传入 TenantGroupWrapper.TenantGroup")
+    @PostMapping("/update")
+    public R<JSONObject> update(@Validated @RequestBody TenantGroupVo.TenantGroup tenantGroup) {
+        // 更新数据
+        SysUser sysUser = sysUserFeignService.queryUserInfo();
+        TenantGroupWrapper.TenantGroup group = TenantGroupWrapper.TenantGroup.from(tenantGroup.jsonString());
+        group.setCreateBy(sysUser.getId());
+        group.setTenantId(sysUser.getTenantId());
+        tenantGroupService.update(group);
+
+        return R.defaultR();
+    }
+
+    @ApiOperation(value = "删除", notes = "机构小组表- 传入id")
+    @PostMapping("/remove")
+    public R<Boolean> remove(@RequestParam Long id) {
+
+        return R.from(tenantGroupService.delete(id));
+    }
+}

+ 69 - 0
cooleshow-app/src/main/java/com/yonge/cooleshow/tenant/vo/TenantGroupVo.java

@@ -0,0 +1,69 @@
+package com.yonge.cooleshow.tenant.vo;
+
+import com.alibaba.fastjson.JSON;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 机构小组表
+ * 2023-10-11 09:24:50
+ */
+@ApiModel(value = "TenantGroupVo对象", description = "机构小组表查询视图对象")
+public class TenantGroupVo {
+
+    @Data
+    @ApiModel(" TenantGroupQuery-机构小组表")
+    public static class TenantGroupQuery implements QueryInfo {
+
+        @ApiModelProperty("当前页")
+        private Integer page;
+
+        @ApiModelProperty("分页行数")
+        private Integer rows;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantGroupQuery from(String json) {
+            return JSON.parseObject(json, TenantGroupQuery.class);
+        }
+    }
+
+    @Data
+    @ApiModel(" TenantGroup-机构小组表")
+    public static class TenantGroup {
+
+        @ApiModelProperty("主键ID")
+        private Long id;
+
+        @ApiModelProperty("机构ID")
+        private Long tenantId;
+
+        @ApiModelProperty("小组名称")
+        @NotNull(message = "小组名称不能为空")
+        private String name;
+
+        @ApiModelProperty("小组负责人")
+        @NotNull(message = "小组负责人不能为空")
+        private Long adminId;
+
+        @ApiModelProperty("群成员列表")
+        private List<Long> userIds = new ArrayList<>();
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantGroup from(String json) {
+            return JSON.parseObject(json, TenantGroup.class);
+        }
+    }
+
+}

+ 29 - 0
cooleshow-common/src/main/java/com/yonge/cooleshow/common/enums/ETenantAlbumCategoryType.java

@@ -0,0 +1,29 @@
+package com.yonge.cooleshow.common.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.yonge.toolset.base.enums.BaseEnum;
+
+public enum ETenantAlbumCategoryType implements BaseEnum<String, ETenantAlbumCategoryType> {
+
+    CATEGORY_TYPE("类型"),
+    CATEGORY_LEVEL("级别"),
+    ;
+
+    @EnumValue
+    private String code;
+    private String msg;
+
+    ETenantAlbumCategoryType(String msg) {
+        this.code = this.name();
+        this.msg = msg;
+    }
+
+    @Override
+    public String getCode() {
+        return this.code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+}

+ 144 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/TenantAlbumCategoryController.java

@@ -0,0 +1,144 @@
+package com.yonge.cooleshow.admin.controller;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.microsvc.toolkit.common.response.paging.PageInfo;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import com.microsvc.toolkit.common.response.template.R;
+import com.yonge.cooleshow.admin.io.request.TenantAlbumCategoryVo;
+import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
+import com.yonge.cooleshow.auth.api.entity.SysUser;
+import com.yonge.cooleshow.biz.dal.entity.TenantAlbumCategory;
+import com.yonge.cooleshow.biz.dal.entity.TenantAlbumCategoryDetail;
+import com.yonge.cooleshow.biz.dal.service.SysUserService;
+import com.yonge.cooleshow.biz.dal.service.TenantAlbumCategoryDetailService;
+import com.yonge.cooleshow.biz.dal.service.TenantAlbumCategoryService;
+import com.yonge.cooleshow.biz.dal.wrapper.TenantAlbumCategoryWrapper;
+import com.yonge.toolset.base.exception.BizException;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+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;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Validated
+@RestController
+@RequestMapping("/tenantAlbumCategory")
+@Api(tags = "机构专辑分类")
+public class TenantAlbumCategoryController {
+
+    @Autowired
+    private TenantAlbumCategoryService tenantAlbumCategoryService;
+
+    @Autowired
+    private TenantAlbumCategoryDetailService tenantAlbumCategoryDetailService;
+
+    @Resource
+    private SysUserFeignService sysUserFeignService;
+
+    @Autowired
+    private SysUserService sysUserService;
+
+    @ApiOperation(value = "详情", notes = "机构专辑分类-根据详情ID查询单条, 传入id")
+    @PreAuthorize("@auditsvc.hasPermissions('tenantAlbumCategory/detail', {'BACKEND'})")
+    @GetMapping("/detail/{id}")
+    public R<TenantAlbumCategoryWrapper.TenantAlbumCategory> detail(@PathVariable("id") Long id) {
+        return R.from(tenantAlbumCategoryService.detail(id));
+    }
+
+    @ApiOperation(value = "查询分页", notes = "机构专辑分类- 传入 TenantAlbumCategoryVo.TenantAlbumCategoryQuery")
+    @PreAuthorize("@auditsvc.hasPermissions('tenantAlbumCategory/page', {'BACKEND'})")
+    @PostMapping("/page")
+    public R<PageInfo<TenantAlbumCategoryVo.TenantAlbumCategory>> page(@RequestBody TenantAlbumCategoryWrapper.TenantAlbumCategoryQuery query) {
+
+        // 查询数据
+        IPage<TenantAlbumCategory> pages = tenantAlbumCategoryService.selectPage(QueryInfo.getPage(query), query);
+        // 数据类型转换
+        List<TenantAlbumCategoryVo.TenantAlbumCategory> records = JSON.parseArray(JSON.toJSONString(pages.getRecords()),
+                TenantAlbumCategoryVo.TenantAlbumCategory.class);
+
+        if (CollectionUtils.isNotEmpty(records)) {
+            List<Long> updateUserIds =
+                    records.stream().map(TenantAlbumCategoryVo.TenantAlbumCategory::getUpdateBy).distinct().collect(Collectors.toList());
+            Map<Long, com.yonge.cooleshow.biz.dal.entity.SysUser> mapByIds = sysUserService.getMapByIds(updateUserIds);
+            records.forEach(next -> {
+                com.yonge.cooleshow.biz.dal.entity.SysUser sysUser = mapByIds.get(next.getUpdateBy());
+                next.setUpdateUserName(sysUser == null ? "" : sysUser.getUsername());
+            });
+        }
+
+        return R.from(QueryInfo.pageInfo(pages, records));
+    }
+
+    @ApiOperation(value = "新增", notes = "机构专辑分类- 传入 TenantAlbumCategoryVo.TenantAlbumCategory")
+    @PreAuthorize("@auditsvc.hasPermissions('tenantAlbumCategory/save', {'BACKEND'})")
+    @PostMapping("/save")
+    public R<JSONObject> add(@Validated @RequestBody TenantAlbumCategoryVo.TenantAlbumCategory tenantAlbumCategoryVo) {
+        SysUser sysUser = sysUserFeignService.queryUserInfo();
+        // 新增数据
+        TenantAlbumCategoryWrapper.TenantAlbumCategory albumCategory =
+                JSON.parseObject(tenantAlbumCategoryVo.jsonString(),
+                        TenantAlbumCategoryWrapper.TenantAlbumCategory.class);
+        albumCategory.setCreateBy(sysUser.getId());
+        albumCategory.setUpdateBy(sysUser.getId());
+        tenantAlbumCategoryService.add(albumCategory);
+
+        return R.defaultR();
+    }
+
+    @ApiOperation(value = "修改", notes = "机构专辑分类- 传入 TenantAlbumCategoryVo.TenantAlbumCategory")
+    @PreAuthorize("@auditsvc.hasPermissions('tenantAlbumCategory/update', {'BACKEND'})")
+    @PostMapping("/update")
+    public R<JSONObject> update(@Validated @RequestBody TenantAlbumCategoryVo.TenantAlbumCategory tenantAlbumCategoryVo) {
+        SysUser sysUser = sysUserFeignService.queryUserInfo();
+        // 更新数据
+        TenantAlbumCategoryWrapper.TenantAlbumCategory albumCategory =
+                JSON.parseObject(tenantAlbumCategoryVo.jsonString(),
+                        TenantAlbumCategoryWrapper.TenantAlbumCategory.class);
+        albumCategory.setUpdateBy(sysUser.getId());
+        tenantAlbumCategoryService.update(albumCategory);
+
+        return R.defaultR();
+    }
+
+    @ApiOperation(value = "删除", notes = "机构专辑分类- 传入id")
+    @PreAuthorize("@auditsvc.hasPermissions('tenantAlbumCategory/remove', {'BACKEND'})")
+    @PostMapping("/remove")
+    public R<Boolean> remove(@RequestParam Long id) {
+
+        return R.from(tenantAlbumCategoryService.delete(id));
+    }
+
+    @ApiOperation(value = "获取专辑分类值列表")
+    @PreAuthorize("@auditsvc.hasPermissions('tenantAlbumCategory/queryCategoryValue', {'BACKEND'})")
+    @GetMapping("/queryCategoryValue")
+    public R<List<TenantAlbumCategoryDetail>> queryCategoryValue(@RequestParam("id") Long id) {
+        TenantAlbumCategory albumCategory = tenantAlbumCategoryService.getById(id);
+        if (albumCategory == null) {
+            throw new BizException("专辑分类不存在");
+        }
+        List<TenantAlbumCategoryDetail> detailList = tenantAlbumCategoryDetailService.lambdaQuery()
+                .eq(TenantAlbumCategoryDetail::getTenantAlbumCategoryId, id)
+                .list();
+        return R.from(detailList);
+    }
+
+}

+ 46 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/TenantGroupController.java

@@ -0,0 +1,46 @@
+package com.yonge.cooleshow.admin.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.microsvc.toolkit.common.response.paging.PageInfo;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import com.microsvc.toolkit.common.response.template.R;
+import com.yonge.cooleshow.biz.dal.service.TenantGroupService;
+import com.yonge.cooleshow.biz.dal.wrapper.TenantGroupWrapper;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@Slf4j
+@Validated
+@RestController
+@RequestMapping("/tenantGroup")
+@Api(tags = "机构小组表")
+public class TenantGroupController {
+
+    @Autowired
+    private TenantGroupService tenantGroupService;
+
+    @ApiOperation(value = "查询分页", notes = "机构小组表- 传入 TenantGroupWrapper.TenantGroupQuery")
+    @PreAuthorize("@auditsvc.hasPermissions('tenantGroup/page', {'BACKEND'})")
+    @PostMapping("/page")
+    public R<PageInfo<TenantGroupWrapper.TenantGroup>> page(@RequestBody TenantGroupWrapper.TenantGroupQuery query) {
+        return R.from(QueryInfo.pageInfo(tenantGroupService.selectPage(QueryInfo.getPage(query), query)));
+    }
+
+    @ApiOperation(value = "处理机构小组交接", notes = "处理机构小组交接")
+    @PreAuthorize("@auditsvc.hasPermissions('tenantGroup/adjustTenantGroup', {'BACKEND'})")
+    @PostMapping("/adjustTenantGroup")
+    public R<Boolean> adjustTenantGroup(@RequestBody TenantGroupWrapper.AdjustTenantGroup adjustTenantGroup) {
+        return R.from(tenantGroupService.adjustTenantGroup(adjustTenantGroup));
+    }
+
+}

+ 62 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/io/request/TenantAlbumCategoryDetailVo.java

@@ -0,0 +1,62 @@
+package com.yonge.cooleshow.admin.io.request;
+
+import com.alibaba.fastjson.JSON;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import lombok.Data;
+
+/**
+ * 机构专辑分类详情
+ * 2023-10-10 10:17:38
+ */
+@ApiModel(value = "TenantAlbumCategoryDetailVo对象", description = "机构专辑分类详情查询视图对象")
+public class TenantAlbumCategoryDetailVo {
+
+    @Data
+    @ApiModel(" TenantAlbumCategoryDetailQuery-机构专辑分类详情")
+    public static class TenantAlbumCategoryDetailQuery implements QueryInfo {
+
+        @ApiModelProperty("当前页")
+        private Integer page;
+
+        @ApiModelProperty("分页行数")
+        private Integer rows;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantAlbumCategoryDetailQuery from(String json) {
+            return JSON.parseObject(json, TenantAlbumCategoryDetailQuery.class);
+        }
+    }
+
+    @Data
+    @ApiModel(" TenantAlbumCategoryDetail-机构专辑分类详情")
+    public static class TenantAlbumCategoryDetail {
+
+
+        @ApiModelProperty("主键ID")
+        private Long id;
+
+
+        @ApiModelProperty("机构专辑分类ID")
+        private Long tenantAlbumCategoryId;
+
+
+        @ApiModelProperty("级别/类别")
+        private String value;
+
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantAlbumCategoryDetail from(String json) {
+            return JSON.parseObject(json, TenantAlbumCategoryDetail.class);
+        }
+    }
+
+}

+ 90 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/io/request/TenantAlbumCategoryVo.java

@@ -0,0 +1,90 @@
+package com.yonge.cooleshow.admin.io.request;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import com.yonge.cooleshow.biz.dal.entity.TenantAlbumCategoryDetail;
+import com.yonge.cooleshow.biz.dal.wrapper.TenantAlbumCategoryDetailWrapper;
+import com.yonge.cooleshow.common.enums.ETenantAlbumCategoryType;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import lombok.Data;
+
+/**
+ * 机构专辑分类
+ * 2023-10-10 09:48:46
+ */
+@ApiModel(value = "TenantAlbumCategoryVo对象", description = "机构专辑分类查询视图对象")
+public class TenantAlbumCategoryVo {
+
+    @Data
+    @ApiModel(" TenantAlbumCategoryQuery-机构专辑分类")
+    public static class TenantAlbumCategoryQuery implements QueryInfo {
+
+        @ApiModelProperty("当前页")
+        private Integer page;
+
+        @ApiModelProperty("分页行数")
+        private Integer rows;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantAlbumCategoryQuery from(String json) {
+            return JSON.parseObject(json, TenantAlbumCategoryQuery.class);
+        }
+    }
+
+    @Data
+    @ApiModel(" TenantAlbumCategory-机构专辑分类")
+    public static class TenantAlbumCategory {
+
+
+        @ApiModelProperty("主键ID")
+        private Long id;
+
+
+        @ApiModelProperty("专辑分类名称")
+        private String name;
+
+        @ApiModelProperty("创建人")
+        private Long createBy;
+
+        @ApiModelProperty("创建时间")
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+        private Date createTime;
+
+        @ApiModelProperty("修改人")
+        private Long updateBy;
+
+        @ApiModelProperty("修改人")
+        private String updateUserName;
+
+        @ApiModelProperty("更新时间")
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+        private Date updateTime;
+
+
+        @ApiModelProperty("专辑分类类别,CATEGORY_TYPE:类型,CATEGORY_LEVEL:级别")
+        private ETenantAlbumCategoryType categoryType;
+
+        @ApiModelProperty("专辑分类级别/类别值")
+        private List<TenantAlbumCategoryDetail> values = new ArrayList<>();
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantAlbumCategory from(String json) {
+            return JSON.parseObject(json, TenantAlbumCategory.class);
+        }
+    }
+
+}

+ 6 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/io/request/TenantAlbumVo.java

@@ -66,6 +66,12 @@ public class TenantAlbumVo {
         @ApiModelProperty("原价")
         private BigDecimal costPrice;
 
+        @ApiModelProperty("专辑分类类型ID")
+        private Long categoryTypeId;
+
+        @ApiModelProperty("专辑分类等级ID")
+        private Long categoryLevelId;
+
         @ApiModelProperty("曲目相关信息")
         private List<MusicSheetData> musicSheetData = new ArrayList<>();
 

+ 3 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/StudentDao.java

@@ -11,6 +11,7 @@ import com.yonge.cooleshow.biz.dal.entity.Student;
 import com.yonge.cooleshow.biz.dal.entity.Subject;
 import com.yonge.cooleshow.biz.dal.vo.MyFollow;
 import com.yonge.cooleshow.biz.dal.vo.StudentVo;
+import com.yonge.cooleshow.biz.dal.wrapper.StudentWrapper;
 import com.yonge.cooleshow.biz.dal.wrapper.TenantInfoWrapper;
 import org.apache.ibatis.annotations.Param;
 
@@ -79,4 +80,6 @@ public interface StudentDao extends BaseMapper<Student> {
     List<TenantInfoWrapper.UserCount> countTeacherByTenantIds(@Param("tenantIdList") List<Long> tenantIdList);
 
     Integer queryStudentCounts(@Param("id") Long id);
+
+    List<StudentWrapper.UserCount> countStudentByTenantGroupIds(@Param("tenantId") Long tenantId, @Param("groupIdList") List<Long> groupIdList);
 }

+ 8 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/TenantAlbum.java

@@ -80,4 +80,12 @@ public class TenantAlbum implements Serializable {
 	@TableField(value = "create_time_")
     private Date createTime;
 
+    @ApiModelProperty("专辑分类类型ID")
+    @TableField(value = "category_type_id_")
+    private Long categoryTypeId;
+
+    @ApiModelProperty("专辑分类等级ID")
+    @TableField(value = "category_level_id_")
+    private Long categoryLevelId;
+
 }

+ 58 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/TenantAlbumCategory.java

@@ -0,0 +1,58 @@
+package com.yonge.cooleshow.biz.dal.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.yonge.cooleshow.common.enums.ETenantAlbumCategoryType;
+import lombok.Data;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 机构专辑分类
+ * 2023-10-10 09:48:46
+ */
+@Data
+@ApiModel(" TenantAlbumCategory-机构专辑分类")
+@TableName("tenant_album_category")
+public class TenantAlbumCategory implements Serializable {
+
+    @ApiModelProperty("主键ID")
+    @TableId(value = "id_", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("专辑分类名称")
+    @TableField(value = "name_")
+    private String name;
+
+    @ApiModelProperty("专辑分类类别")
+    @TableField(value = "category_type_")
+    private ETenantAlbumCategoryType categoryType;
+
+    @ApiModelProperty("删除标记")
+    @TableField(value = "del_flag_")
+    private Boolean delFlag;
+
+    @ApiModelProperty("创建人")
+    @TableField(value = "create_by_")
+    private Long createBy;
+
+    @ApiModelProperty("创建时间")
+    @TableField(value = "create_time_")
+    private Date createTime;
+
+    @ApiModelProperty("修改人")
+    @TableField(value = "update_by_")
+    private Long updateBy;
+
+    @ApiModelProperty("更新时间")
+    @TableField(value = "update_time_")
+    private Date updateTime;
+
+}

+ 36 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/TenantAlbumCategoryDetail.java

@@ -0,0 +1,36 @@
+package com.yonge.cooleshow.biz.dal.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import lombok.Data;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+
+
+/**
+ * 机构专辑分类详情
+ * 2023-10-10 10:17:38
+ */
+@Data
+@ApiModel(" TenantAlbumCategoryDetail-机构专辑分类详情")
+@TableName("tenant_album_category_detail")
+public class TenantAlbumCategoryDetail implements Serializable {
+
+    @ApiModelProperty("主键ID")
+    @TableId(value = "id_", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("机构专辑分类ID")
+    @TableField(value = "tenant_album_category_id_")
+    private Long tenantAlbumCategoryId;
+
+    @ApiModelProperty("级别/类别")
+    @TableField(value = "value_")
+    private String value;
+
+}

+ 53 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/TenantGroup.java

@@ -0,0 +1,53 @@
+package com.yonge.cooleshow.biz.dal.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import lombok.Data;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 机构小组表
+ * 2023-10-11 09:24:50
+ */
+@Data
+@ApiModel(" TenantGroup-机构小组表")
+@TableName("tenant_group")
+public class TenantGroup implements Serializable {
+
+    @ApiModelProperty("主键ID")
+    @TableId(value = "id_", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("机构ID")
+    @TableField(value = "tenant_id_")
+    private Long tenantId;
+
+    @ApiModelProperty("小组名称")
+    @TableField(value = "name_")
+    private String name;
+
+    @ApiModelProperty("管理员ID")
+    @TableField(value = "admin_id_")
+    private Long adminId;
+
+    @ApiModelProperty("IM群ID")
+    @TableField(value = "im_group_id_")
+    private String imGroupId;
+
+    @ApiModelProperty("创建人")
+    @TableField(value = "create_by_")
+    private Long createBy;
+
+    @ApiModelProperty("创建时间")
+    @TableField(value = "create_time_")
+    private Date createTime;
+
+}

+ 27 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/TenantAlbumCategoryDetailMapper.java

@@ -0,0 +1,27 @@
+package com.yonge.cooleshow.biz.dal.mapper;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.stereotype.Repository;
+import com.yonge.cooleshow.biz.dal.entity.TenantAlbumCategoryDetail;
+import com.yonge.cooleshow.biz.dal.wrapper.TenantAlbumCategoryDetailWrapper;
+
+/**
+ * 机构专辑分类详情
+ * 2023-10-10 10:17:38
+ */
+@Repository
+public interface TenantAlbumCategoryDetailMapper extends BaseMapper<TenantAlbumCategoryDetail> {
+
+    /**
+     * 分页查询
+     * @param page IPage<TenantAlbumCategoryDetailWrapper.TenantAlbumCategoryDetail>
+     * @param param TenantAlbumCategoryDetailWrapper.TenantAlbumCategoryDetailQuery
+     * @return List<TenantAlbumCategoryDetailWrapper.TenantAlbumCategoryDetail>
+     */
+    List<TenantAlbumCategoryDetail> selectPage(@Param("page") IPage<TenantAlbumCategoryDetail> page, @Param("param") TenantAlbumCategoryDetailWrapper.TenantAlbumCategoryDetailQuery param);
+
+}

+ 27 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/TenantAlbumCategoryMapper.java

@@ -0,0 +1,27 @@
+package com.yonge.cooleshow.biz.dal.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.yonge.cooleshow.biz.dal.entity.TenantAlbumCategory;
+import com.yonge.cooleshow.biz.dal.wrapper.TenantAlbumCategoryWrapper;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * 机构专辑分类
+ * 2023-10-10 09:48:46
+ */
+@Repository
+public interface TenantAlbumCategoryMapper extends BaseMapper<TenantAlbumCategory> {
+
+    /**
+     * 分页查询
+     * @param page IPage<TenantAlbumCategoryWrapper.TenantAlbumCategory>
+     * @param param TenantAlbumCategoryWrapper.TenantAlbumCategoryQuery
+     * @return List<TenantAlbumCategoryWrapper.TenantAlbumCategory>
+     */
+    List<TenantAlbumCategory> selectPage(@Param("page") IPage<TenantAlbumCategory> page, @Param("param") TenantAlbumCategoryWrapper.TenantAlbumCategoryQuery param);
+
+}

+ 31 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/TenantGroupMapper.java

@@ -0,0 +1,31 @@
+package com.yonge.cooleshow.biz.dal.mapper;
+
+import java.util.List;
+import java.util.Map;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import org.apache.ibatis.annotations.Param;
+import org.apache.poi.ss.formula.functions.T;
+import org.springframework.stereotype.Repository;
+import com.yonge.cooleshow.biz.dal.entity.TenantGroup;
+import com.yonge.cooleshow.biz.dal.wrapper.TenantGroupWrapper;
+
+/**
+ * 机构小组表
+ * 2023-10-11 09:24:50
+ */
+@Repository
+public interface TenantGroupMapper extends BaseMapper<TenantGroup> {
+
+    /**
+     * 分页查询
+     * @param page IPage<TenantGroupWrapper.TenantGroup>
+     * @param param TenantGroupWrapper.TenantGroupQuery
+     * @return List<TenantGroupWrapper.TenantGroup>
+     */
+    List<TenantGroupWrapper.TenantGroup> selectPage(@Param("page") IPage<TenantGroupWrapper.TenantGroup> page,
+                                                    @Param("param") TenantGroupWrapper.TenantGroupQuery param);
+
+}

+ 26 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/queryInfo/SysMusicCompareRecordQueryInfo.java

@@ -1,5 +1,6 @@
 package com.yonge.cooleshow.biz.dal.queryInfo;
 
+import com.alibaba.excel.annotation.ExcelProperty;
 import com.yonge.cooleshow.biz.dal.enums.FeatureType;
 import com.yonge.cooleshow.biz.dal.enums.HeardLevelEnum;
 import com.yonge.toolset.base.page.QueryInfo;
@@ -133,27 +134,52 @@ public class SysMusicCompareRecordQueryInfo {
     @Data
     public static class WechatCompareRecordPageDto{
         @ApiModelProperty(name = "用户编号")
+        @ExcelProperty(value = "用户编号")
         private Long userId;
 
         @ApiModelProperty(name = "头像")
+        @ExcelProperty(value = "头像")
         private String avatar;
 
         @ApiModelProperty(name = "姓名")
+        @ExcelProperty(value = "姓名")
         private String username;
 
         @ApiModelProperty(name = "声部")
+        @ExcelProperty(value = "声部")
         private String subjectName;
 
         @ApiModelProperty(name = "训练总时长")
+        @ExcelProperty(value = "训练总时长")
         private Integer trainTimes = 0;
 
         @ApiModelProperty(name = "训练天数")
+        @ExcelProperty(value = "训练天数")
         private Integer trainDays = 0;
 
         @ApiModelProperty(name = "平均训练时长")
+        @ExcelProperty(value = "平均训练时长")
         private Integer avgTrainTimes = 0;
 
     }
+    @Data
+    public static class WechatCompareRecordExportDto{
+        @ExcelProperty(value = "姓名")
+        private String username;
+
+        @ExcelProperty(value = "声部")
+        private String subjectName;
+
+        @ExcelProperty(value = "平均训练时长")
+        private Integer avgTrainTimes = 0;
+
+        @ExcelProperty(value = "总时长")
+        private Integer trainTimes = 0;
+
+        @ExcelProperty(value = "训练天数")
+        private Integer trainDays = 0;
+
+    }
 
     @Data
     public static class WechatCompareRecordDto{

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

@@ -79,7 +79,7 @@ public interface ImGroupService extends IService<ImGroup> {
     * @author zx
     * @date 2022/3/22 11:17
     */
-    void create(ImGroupWrapper.ImGroup imGroup) throws Exception;
+    String create(ImGroupWrapper.ImGroup imGroup) throws Exception;
 
     /**
     * @description: 成课后自动创建群聊,建议放在最后执行

+ 3 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/OssFileService.java

@@ -12,4 +12,7 @@ public interface OssFileService {
 
     OssFileWrapper.ExportFile uploadFile(List<?> dataList, Class<?> clazz,
                                          String fileName, String sheetName);
+
+    OssFileWrapper.ExportFile uploadFileWithShortUrl(List<?> dataList, Class<?> clazz,
+                                                     String fileName, String sheetName);
 }

+ 43 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/TenantAlbumCategoryDetailService.java

@@ -0,0 +1,43 @@
+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.biz.dal.wrapper.TenantAlbumCategoryDetailWrapper;
+import com.yonge.cooleshow.biz.dal.entity.TenantAlbumCategoryDetail;
+
+/**
+ * 机构专辑分类详情
+ * 2023-10-10 10:17:38
+ */
+public interface TenantAlbumCategoryDetailService extends IService<TenantAlbumCategoryDetail>  {
+
+    /**
+     * 查询详情
+     * @param id 详情ID
+     * @return TenantAlbumCategoryDetail
+     */
+    TenantAlbumCategoryDetail detail(Long id);
+
+    /**
+     * 分页查询
+     * @param page IPage<TenantAlbumCategoryDetail>
+     * @param query TenantAlbumCategoryDetailWrapper.TenantAlbumCategoryDetailQuery
+     * @return IPage<TenantAlbumCategoryDetail>
+     */
+    IPage<TenantAlbumCategoryDetail> selectPage(IPage<TenantAlbumCategoryDetail> page, TenantAlbumCategoryDetailWrapper.TenantAlbumCategoryDetailQuery query);
+
+    /**
+     * 添加
+     * @param tenantAlbumCategoryDetail TenantAlbumCategoryDetailWrapper.TenantAlbumCategoryDetail
+     * @return Boolean
+     */
+    Boolean add(TenantAlbumCategoryDetailWrapper.TenantAlbumCategoryDetail tenantAlbumCategoryDetail);
+
+    /**
+     * 更新
+     * @param tenantAlbumCategoryDetail TenantAlbumCategoryDetailWrapper.TenantAlbumCategoryDetail
+     * @return Boolean
+     */
+    Boolean update(TenantAlbumCategoryDetailWrapper.TenantAlbumCategoryDetail tenantAlbumCategoryDetail);
+
+}

+ 46 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/TenantAlbumCategoryService.java

@@ -0,0 +1,46 @@
+package com.yonge.cooleshow.biz.dal.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.microsvc.toolkit.common.response.ParamResponse;
+import com.yonge.cooleshow.biz.dal.wrapper.TenantAlbumCategoryWrapper;
+import com.yonge.cooleshow.biz.dal.entity.TenantAlbumCategory;
+
+/**
+ * 机构专辑分类
+ * 2023-10-10 09:48:46
+ */
+public interface TenantAlbumCategoryService extends IService<TenantAlbumCategory>  {
+
+    /**
+     * 查询详情
+     * @param id 详情ID
+     * @return TenantAlbumCategory
+     */
+    TenantAlbumCategoryWrapper.TenantAlbumCategory detail(Long id);
+
+    /**
+     * 分页查询
+     * @param page IPage<TenantAlbumCategory>
+     * @param query TenantAlbumCategoryWrapper.TenantAlbumCategoryQuery
+     * @return IPage<TenantAlbumCategory>
+     */
+    IPage<TenantAlbumCategory> selectPage(IPage<TenantAlbumCategory> page,
+                                                                     TenantAlbumCategoryWrapper.TenantAlbumCategoryQuery query);
+
+    /**
+     * 添加
+     * @param tenantAlbumCategory TenantAlbumCategoryWrapper.TenantAlbumCategory
+     * @return Boolean
+     */
+    Boolean add(TenantAlbumCategoryWrapper.TenantAlbumCategory tenantAlbumCategory);
+
+    /**
+     * 更新
+     * @param tenantAlbumCategory TenantAlbumCategoryWrapper.TenantAlbumCategory
+     * @return Boolean
+     */
+    Boolean update(TenantAlbumCategoryWrapper.TenantAlbumCategory tenantAlbumCategory);
+
+    Boolean delete(Long id);
+}

+ 52 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/TenantGroupService.java

@@ -0,0 +1,52 @@
+package com.yonge.cooleshow.biz.dal.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.microsvc.toolkit.common.response.ParamResponse;
+import com.yonge.cooleshow.biz.dal.wrapper.TenantGroupWrapper;
+import com.yonge.cooleshow.biz.dal.entity.TenantGroup;
+
+import java.util.List;
+
+/**
+ * 机构小组表
+ * 2023-10-11 09:24:50
+ */
+public interface TenantGroupService extends IService<TenantGroup>  {
+
+    /**
+     * 查询详情
+     * @param id 详情ID
+     * @return TenantGroup
+     */
+    TenantGroup detail(Long id);
+
+    /**
+     * 分页查询
+     * @param page IPage<TenantGroup>
+     * @param query TenantGroupWrapper.TenantGroupQuery
+     * @return IPage<TenantGroup>
+     */
+    IPage<TenantGroupWrapper.TenantGroup> selectPage(IPage<TenantGroupWrapper.TenantGroup> page,
+                                                     TenantGroupWrapper.TenantGroupQuery query);
+
+    /**
+     * 添加
+     * @param tenantGroup TenantGroupWrapper.TenantGroup
+     * @return Boolean
+     */
+    Boolean add(TenantGroupWrapper.TenantGroup tenantGroup);
+
+    /**
+     * 更新
+     * @param tenantGroup TenantGroupWrapper.TenantGroup
+     * @return Boolean
+     */
+    Boolean update(TenantGroupWrapper.TenantGroup tenantGroup);
+
+    Boolean delete(Long id);
+
+    List<TenantGroupWrapper.TenantGroupMember> queryGroupMember(Long tenantId, Long tenantGroupId);
+
+    Boolean adjustTenantGroup(TenantGroupWrapper.AdjustTenantGroup adjustTenantGroup);
+}

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

@@ -303,7 +303,7 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public void create(ImGroupWrapper.ImGroup imGroup) throws Exception {
+    public String create(ImGroupWrapper.ImGroup imGroup) throws Exception {
         Date now = new Date();
         if (imGroup.getType() == null) {
             imGroup.setType(ImGroupType.FAN.getCode());
@@ -317,6 +317,7 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
         if (!imGroup.getStudentIdList().isEmpty()) {
             addGroupMember(groupId,imGroup.getStudentIdList());
         }
+        return groupId;
 
 
 //        String imGroupId = UUID.randomUUID() + imGroup.getType().getCode();

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

@@ -40,6 +40,47 @@ public class OssFileServiceImpl implements OssFileService {
         String uploadOssPath = MessageFormat.format("excel-download/{0}", DateTime.now().toString("yyyy-MM"));
 
         // 文件上传下载地址
+        String uploadPath = MessageFormat.format("{0}/{1}-{2}/{3}.xlsx", uploadOssPath,
+                String.valueOf(IdWorker.getId()),
+                DateTime.now().toString("MMddHHmmss"),fileName);
+
+        // 本地文件地址
+        String localPath = DownloadManager.getInstance().path(uploadPath);
+        log.debug("pageExport localPath={}", localPath);
+
+        EasyExcel
+                .write(localPath, clazz)
+                //.withTemplate(resource.getStream()) // 利用模板的输出流
+                .sheet(0, sheetName)
+                .doWrite(dataList);
+
+        // 上传本地文件到OSS服务器
+        String downloadPath = ossPluginContext.getPluginService(TencentOssPlugin.PLUGIN_NAME)
+                .uploadFile(uploadOssPath, new File(localPath));
+        log.debug("pageExport downloadPath={}", downloadPath);
+
+        // 删除本地缓存文件
+        DownloadManager.getInstance().deleteOnExit(localPath);
+
+        // 学生统计下载
+        return OssFileWrapper.ExportFile
+                .builder()
+                .fileName(MessageFormat.format("{0}.xlsx", fileName))
+                .downloadPath(downloadPath)
+                .build();
+    }
+
+    @Override
+    public OssFileWrapper.ExportFile uploadFileWithShortUrl(List<?> dataList, Class<?> clazz,
+                                                            String fileName, String sheetName) {
+        if (CollectionUtils.isEmpty(dataList)) {
+            throw new BizException("导出数据为空");
+        }
+
+        // OSS上传文件目录
+        String uploadOssPath = MessageFormat.format("excel-download/{0}", DateTime.now().toString("yyyy-MM"));
+
+        // 文件上传下载地址
         String uploadPath = MessageFormat.format("{0}/{1}-{2}.xlsx", uploadOssPath, String.valueOf(IdWorker.getId()),
                 DateTime.now().toString("MMddHHmmss"));
 

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

@@ -4,9 +4,9 @@ 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.CollectionUtils;
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.yonge.cooleshow.api.feign.dto.ImUserInfo;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.microsvc.toolkit.common.webportal.exception.BizException;
@@ -25,18 +25,16 @@ import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.enums.ImGroupMemberRoleType;
 import com.yonge.cooleshow.biz.dal.enums.ImGroupType;
 import com.yonge.cooleshow.biz.dal.mapper.SysUserMapper;
+import com.yonge.cooleshow.biz.dal.mapper.TenantGroupMapper;
 import com.yonge.cooleshow.biz.dal.service.ImGroupService;
 import com.yonge.cooleshow.biz.dal.mapper.TenantAlbumMapper;
-import com.yonge.cooleshow.biz.dal.mapper.TenantAlbumPurchaseMapper;
 import com.yonge.cooleshow.biz.dal.mapper.TenantAlbumRefMapper;
 import com.yonge.cooleshow.biz.dal.service.ImGroupMemberService;
-import com.yonge.cooleshow.biz.dal.service.ImGroupService;
 import com.yonge.cooleshow.biz.dal.service.ImUserFriendService;
 import com.yonge.cooleshow.biz.dal.service.StudentService;
 import com.yonge.cooleshow.biz.dal.service.StudentTotalService;
 import com.yonge.cooleshow.biz.dal.service.im.ImGroupCoreService;
 import com.yonge.cooleshow.biz.dal.service.SysConfigService;
-import com.yonge.cooleshow.biz.dal.service.TeacherService;
 import com.yonge.cooleshow.biz.dal.service.TenantInfoService;
 import com.yonge.cooleshow.biz.dal.service.UserTenantAlbumRecordService;
 import com.yonge.cooleshow.biz.dal.vo.MyFollow;
@@ -67,7 +65,6 @@ import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
 import java.text.MessageFormat;
-import java.text.ParseException;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
@@ -129,6 +126,9 @@ public class StudentServiceImpl extends ServiceImpl<StudentDao, Student> impleme
     @Autowired
     private ImGroupCoreService imGroupCoreService;
 
+    @Autowired
+    private TenantGroupMapper tenantGroupMapper;
+
     @Override
     public StudentVo detail(Long userId) {
         return baseMapper.detail(userId);
@@ -434,6 +434,16 @@ public class StudentServiceImpl extends ServiceImpl<StudentDao, Student> impleme
     @Transactional(rollbackFor = Exception.class)
     @Override
     public Boolean save(StudentWrapper.Student studentInfo) {
+        if (studentInfo.getTenantGroupId() == null) {
+            studentInfo.setTenantGroupId(-1L);
+        }
+
+        Long tenantGroupId = studentInfo.getTenantGroupId();
+        TenantGroup tenantGroup = tenantGroupMapper.selectById(tenantGroupId);
+        if (tenantGroup == null) {
+            throw new BizException("机构小组不存在");
+        }
+
         if (studentInfo.getId() == null) {
             return createStudent(studentInfo);
         } else {
@@ -569,6 +579,7 @@ public class StudentServiceImpl extends ServiceImpl<StudentDao, Student> impleme
             }
         }
         this.lambdaUpdate().set(Student::getTenantId, toTenantId)
+                .set(Student::getTenantGroupId, -1L)
                 .eq(Student::getUserId, student.getUserId())
                 .update();
     }
@@ -605,6 +616,22 @@ public class StudentServiceImpl extends ServiceImpl<StudentDao, Student> impleme
 //             删除好友
             imUserFriendService.delTeacherFriendByTenantId(student.getTenantId(), student.getUserId(),
                     ClientEnum.STUDENT.getCode());
+
+            // 与新机构老师成为好友
+            if (newTenantId != -1L) {
+                // 自动与机构老师成为好友
+                QueryWrapper<Teacher> queryWrapper = new QueryWrapper<>();
+                queryWrapper.lambda().eq(Teacher::getTenantId, student.getTenantId());
+                List<Teacher> teacherList = teacherDao.selectList(queryWrapper);
+                teacherList.forEach(next -> imUserFriendService.saveUserFriend(next.getUserId(),
+                        new HashSet<>(ImmutableList.of(student.getUserId()))));
+            }
+        }
+
+        // 加入机构小组群
+        if (studentInfo.getTenantGroupId() != -1L && !studentInfo.getTenantGroupId().equals(student.getTenantGroupId())) {
+            quitFromTenantGroupIM(student.getTenantGroupId(), student.getUserId());
+            add2TenantGroupIM(studentInfo.getTenantGroupId(), studentInfo.getId());
         }
 
 
@@ -615,6 +642,7 @@ public class StudentServiceImpl extends ServiceImpl<StudentDao, Student> impleme
                     .set(Student::getTenantId, studentInfo.getTenantId())
                     .set(Student::getUserId, sysUser.getId())
                     .set(Student::getUpdateTime, new Date())
+                    .set(Student::getTenantGroupId, studentInfo.getTenantGroupId())
                     .set(StringUtils.isNotEmpty(studentInfo.getAvatar()), Student::getAvatar, studentInfo.getAvatar())
                     .eq(Student::getUserId, studentInfo.getId())
                     .update();
@@ -625,6 +653,7 @@ public class StudentServiceImpl extends ServiceImpl<StudentDao, Student> impleme
             newStudent.setUpdateTime(new Date());
             newStudent.setTenantId(studentInfo.getTenantId());
             newStudent.setAvatar(studentInfo.getAvatar());
+            newStudent.setTenantGroupId(studentInfo.getTenantGroupId());
             this.getBaseMapper().updateById(newStudent);
         }
         com.yonge.cooleshow.biz.dal.entity.SysUser sysUser = sysUserMapper.selectById(studentInfo.getId());
@@ -647,6 +676,77 @@ public class StudentServiceImpl extends ServiceImpl<StudentDao, Student> impleme
         return true;
     }
 
+    private void quitFromTenantGroupIM(Long tenantGroupId, Long studentId) {
+        TenantGroup tenantGroup = tenantGroupMapper.selectById(tenantGroupId);
+        if (tenantGroup == null || StringUtils.isEmpty(tenantGroup.getImGroupId())) {
+            return;
+        }
+        String imGroupId = tenantGroup.getImGroupId();
+        ImGroup imGroup = imGroupService.getById(imGroupId);
+        if (imGroup == null) {
+            return;
+        }
+        Integer count = imGroupMemberService.lambdaQuery()
+                .eq(ImGroupMember::getId, imGroupId)
+                .eq(ImGroupMember::getUserId, studentId)
+                .eq(ImGroupMember::getRoleType, ImGroupMemberRoleType.STUDENT)
+                .count();
+        if (count == 0) {
+            return;
+        }
+        try {
+            imGroupService.quit(imGroupId, studentId, ClientEnum.STUDENT, true);
+        } catch (Exception e) {
+            log.error("退出机构小组群失败", e);
+            throw new BizException("退出机构小组群失败");
+        }
+    }
+
+    /**
+     * 添加到机构小组IM群
+     * @param tenantGroupId 机构小组ID
+     * @param studentId 学生ID
+     */
+    private void add2TenantGroupIM(Long tenantGroupId, Long studentId) {
+        TenantGroup tenantGroup = tenantGroupMapper.selectById(tenantGroupId);
+        String imGroupId = tenantGroup.getImGroupId();
+        if (imGroupId == null) {
+            // 建群
+            try {
+                ImGroupWrapper.ImGroup imGroup = new ImGroupWrapper.ImGroup();
+                imGroup.setId(IdWorker.getId());
+                imGroup.setCreateBy(tenantGroup.getAdminId());
+                imGroup.setType(ImGroupType.ORG.getCode());
+                imGroup.setName(tenantGroup.getName());
+                imGroupId = imGroupService.create(imGroup);
+                tenantGroup.setImGroupId(imGroupId);
+                tenantGroupMapper.updateById(tenantGroup);
+            } catch (Exception e) {
+                log.error("创建机构小组群失败", e);
+                throw new BizException("创建机构小组群失败");
+            }
+        }
+        if ("-1".equals(imGroupId)) {
+            // 群已经解散,改为-1标志
+            log.info("群已经解散,不再添加机构小组群:" + imGroupId);
+            return;
+        }
+        try {
+            ImGroup imGroup = imGroupService.getById(imGroupId);
+            if (imGroup != null) {
+                Set<Long> userIds = new HashSet<>();
+                userIds.add(studentId);
+                imGroupService.addGroupMember(imGroupId, userIds);
+            } else {
+                tenantGroup.setImGroupId("-1");
+                tenantGroupMapper.updateById(tenantGroup);
+            }
+        } catch (Exception e) {
+            log.error("加入机构小组群失败", e);
+            throw new BizException("加入机构小组群失败");
+        }
+    }
+
     private Boolean createStudent(StudentWrapper.Student studentInfo) {
         String avatar = studentInfo.getAvatar();
         if (StringUtils.isEmpty(avatar)) {
@@ -661,6 +761,7 @@ public class StudentServiceImpl extends ServiceImpl<StudentDao, Student> impleme
         student.setAvatar(studentInfo.getAvatar());
         student.setCreateTime(new Date());
         student.setLockFlag(UserLockFlag.NORMAL);
+        student.setTenantGroupId(studentInfo.getTenantGroupId());
 
         save(student);
         try {
@@ -696,6 +797,11 @@ public class StudentServiceImpl extends ServiceImpl<StudentDao, Student> impleme
             }
         }
 
+        // 加入机构小组群
+        if (studentInfo.getTenantGroupId() != -1L) {
+            add2TenantGroupIM(student.getTenantGroupId(), student.getUserId());
+        }
+
         return true;
     }
 

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

@@ -20,10 +20,12 @@ import com.yonge.cooleshow.biz.dal.dto.search.TeacherSearch;
 import com.yonge.cooleshow.biz.dal.entity.*;
 import com.yonge.cooleshow.biz.dal.enums.AuthStatusEnum;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.enums.ImGroupMemberRoleType;
 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.mapper.TenantGroupMapper;
 import com.yonge.cooleshow.biz.dal.queryInfo.TeacherQueryInfo;
 import com.yonge.cooleshow.biz.dal.service.ImGroupService;
 import com.yonge.cooleshow.biz.dal.service.StudentService;
@@ -204,6 +206,9 @@ public class TeacherServiceImpl extends ServiceImpl<TeacherDao, Teacher> impleme
     @Autowired
     private SysUserMapper sysUserMapper;
 
+    @Autowired
+    private TenantGroupMapper tenantGroupMapper;
+
 
     @Override
     public TeacherVo detail(Long userId) {
@@ -561,16 +566,6 @@ public class TeacherServiceImpl extends ServiceImpl<TeacherDao, Teacher> impleme
                 log.error("导入IM账号到三方 teacherId={}", teacher.getUserId(), e);
             }
 
-            // 机构老师与学生互加好友关系
-//            if (Optional.ofNullable(teacher.getTenantId()).orElse(-1L) > 0) {
-//                // 自动与机构老师成为好友
-//                Set<Long> collect = studentService.lambdaQuery()
-//                        .eq(Student::getTenantId, teacher.getTenantId()).list().stream()
-//                        .map(Student::getUserId).collect(Collectors.toSet());
-//
-//                imUserFriendService.saveUserFriend(teacher.getUserId(), collect);
-//            }
-
             //插入老师账户表
             UserAccount oldAcc = userAccountService.getById(teacherSubmitReq.getUserId());
             if (null == oldAcc) {
@@ -1046,7 +1041,7 @@ public class TeacherServiceImpl extends ServiceImpl<TeacherDao, Teacher> impleme
         return wrapper;
     }
 
-//    @Transactional(rollbackFor = Exception.class)
+    @Transactional(rollbackFor = Exception.class)
     @Override
     public void updateTenant(TeacherWrapper.UpdateTenant updateTenant,Long userId) {
         Teacher teacher = this.getById(updateTenant.getTeacherId());
@@ -1055,6 +1050,16 @@ public class TeacherServiceImpl extends ServiceImpl<TeacherDao, Teacher> impleme
         }
         Long oldTenantId = teacher.getTenantId();
         if (oldTenantId != -1L) {
+            // 判断是否存在机构小组群主身份
+            QueryWrapper<TenantGroup> queryWrapper = new QueryWrapper<>();
+            queryWrapper.lambda()
+                    .eq(TenantGroup::getTenantId, oldTenantId)
+                    .eq(TenantGroup::getAdminId, teacher.getUserId());
+            Integer count = tenantGroupMapper.selectCount(queryWrapper);
+            if (count > 0) {
+                throw new BizException(5100, "该老师存在机构小组负责人身份,请先移交小组或者解散小组");
+            }
+
             // 机构老师处理流程
             List<ImGroup> imGroups = imGroupService.lambdaQuery()
                     .eq(ImGroup::getCreateBy, teacher.getUserId())

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

@@ -0,0 +1,66 @@
+package com.yonge.cooleshow.biz.dal.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+import org.springframework.beans.BeanUtils;
+import lombok.extern.slf4j.Slf4j;
+import com.yonge.cooleshow.biz.dal.entity.TenantAlbumCategoryDetail;
+import com.yonge.cooleshow.biz.dal.wrapper.TenantAlbumCategoryDetailWrapper;
+import com.yonge.cooleshow.biz.dal.mapper.TenantAlbumCategoryDetailMapper;
+import com.yonge.cooleshow.biz.dal.service.TenantAlbumCategoryDetailService;
+
+/**
+ * 机构专辑分类详情
+ * 2023-10-10 10:17:38
+ */
+@Slf4j
+@Service
+public class TenantAlbumCategoryDetailServiceImpl extends ServiceImpl<TenantAlbumCategoryDetailMapper, TenantAlbumCategoryDetail> implements TenantAlbumCategoryDetailService {
+
+    /**
+     * 查询详情
+     * @param id 详情ID
+     * @return TenantAlbumCategoryDetail
+     */
+    @Override
+    public TenantAlbumCategoryDetail detail(Long id) {
+
+        return baseMapper.selectById(id);
+    }
+
+    /**
+     * 分页查询
+     * @param page IPage<TenantAlbumCategoryDetail>
+     * @param query TenantAlbumCategoryDetailWrapper.TenantAlbumCategoryDetailQuery
+     * @return IPage<TenantAlbumCategoryDetail>
+     */
+    @Override
+    public IPage<TenantAlbumCategoryDetail> selectPage(IPage<TenantAlbumCategoryDetail> page, TenantAlbumCategoryDetailWrapper.TenantAlbumCategoryDetailQuery query) {
+
+        return page.setRecords(baseMapper.selectPage(page, query));
+    }
+
+    /**
+     * 添加
+     * @param tenantAlbumCategoryDetail TenantAlbumCategoryDetailWrapper.TenantAlbumCategoryDetail
+     * @return Boolean
+     */
+    @Override
+    public Boolean add(TenantAlbumCategoryDetailWrapper.TenantAlbumCategoryDetail tenantAlbumCategoryDetail) {
+
+        return this.save(JSON.parseObject(tenantAlbumCategoryDetail.jsonString(), TenantAlbumCategoryDetail.class));
+    }
+
+    /**
+     * 更新
+     * @param tenantAlbumCategoryDetail TenantAlbumCategoryDetailWrapper.TenantAlbumCategoryDetail
+     * @return Boolean
+     */
+    @Override
+    public Boolean update(TenantAlbumCategoryDetailWrapper.TenantAlbumCategoryDetail tenantAlbumCategoryDetail){
+
+        return this.updateById(JSON.parseObject(tenantAlbumCategoryDetail.jsonString(), TenantAlbumCategoryDetail.class));
+    }
+}

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

@@ -0,0 +1,202 @@
+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.extension.service.impl.ServiceImpl;
+import com.yonge.cooleshow.biz.dal.entity.TenantAlbum;
+import com.yonge.cooleshow.biz.dal.entity.TenantAlbumCategory;
+import com.yonge.cooleshow.biz.dal.entity.TenantAlbumCategoryDetail;
+import com.yonge.cooleshow.biz.dal.entity.TenantAlbumMusic;
+import com.yonge.cooleshow.biz.dal.mapper.TenantAlbumCategoryMapper;
+import com.yonge.cooleshow.biz.dal.mapper.TenantAlbumMapper;
+import com.yonge.cooleshow.biz.dal.mapper.TenantAlbumMusicMapper;
+import com.yonge.cooleshow.biz.dal.service.TenantAlbumCategoryDetailService;
+import com.yonge.cooleshow.biz.dal.service.TenantAlbumCategoryService;
+import com.yonge.cooleshow.biz.dal.wrapper.TenantAlbumCategoryWrapper;
+import com.yonge.cooleshow.common.enums.ETenantAlbumCategoryType;
+import com.yonge.toolset.base.exception.BizException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * 机构专辑分类
+ * 2023-10-10 09:48:46
+ */
+@Slf4j
+@Service
+public class TenantAlbumCategoryServiceImpl extends ServiceImpl<TenantAlbumCategoryMapper, TenantAlbumCategory> implements TenantAlbumCategoryService {
+
+    @Autowired
+    private TenantAlbumCategoryDetailService tenantAlbumCategoryDetailService;
+    @Autowired
+    private TenantAlbumMusicMapper tenantAlbumMusicMapper;
+    @Autowired
+    private TenantAlbumMapper tenantAlbumMapper;
+
+    /**
+     * 查询详情
+     *
+     * @param id 详情ID
+     * @return TenantAlbumCategory
+     */
+    @Override
+    public TenantAlbumCategoryWrapper.TenantAlbumCategory detail(Long id) {
+        TenantAlbumCategory albumCategory = baseMapper.selectById(id);
+        if (albumCategory == null) {
+            throw new BizException("专辑分类不存在");
+        }
+        TenantAlbumCategoryWrapper.TenantAlbumCategory tenantAlbumCategory =
+                TenantAlbumCategoryWrapper.TenantAlbumCategory.from(JSON.toJSONString(albumCategory));
+
+        List<TenantAlbumCategoryDetail> list = tenantAlbumCategoryDetailService.lambdaQuery()
+                .eq(TenantAlbumCategoryDetail::getTenantAlbumCategoryId, id)
+                .list();
+        tenantAlbumCategory.setValues(list);
+        return tenantAlbumCategory;
+    }
+
+    /**
+     * 分页查询
+     *
+     * @param page  IPage<TenantAlbumCategory>
+     * @param query TenantAlbumCategoryWrapper.TenantAlbumCategoryQuery
+     * @return IPage<TenantAlbumCategory>
+     */
+    @Override
+    public IPage<TenantAlbumCategory> selectPage(IPage<TenantAlbumCategory> page,
+                                                 TenantAlbumCategoryWrapper.TenantAlbumCategoryQuery query) {
+
+        return page.setRecords(baseMapper.selectPage(page, query));
+    }
+
+    /**
+     * 添加
+     *
+     * @param tenantAlbumCategory TenantAlbumCategoryWrapper.TenantAlbumCategory
+     * @return Boolean
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public Boolean add(TenantAlbumCategoryWrapper.TenantAlbumCategory tenantAlbumCategory) {
+        Integer count = this.lambdaQuery()
+                .eq(TenantAlbumCategory::getName, tenantAlbumCategory.getName())
+                .eq(TenantAlbumCategory::getCategoryType, tenantAlbumCategory.getCategoryType())
+                .eq(TenantAlbumCategory::getDelFlag, false)
+                .count();
+        if (count > 0) {
+            throw new BizException("专辑分类名称已存在");
+        }
+
+        TenantAlbumCategory albumCategory = JSON.parseObject(tenantAlbumCategory.jsonString(),
+                TenantAlbumCategory.class);
+        this.save(albumCategory);
+        List<TenantAlbumCategoryDetail> values = tenantAlbumCategory.getValues();
+        if (!values.isEmpty()) {
+            values.forEach(next -> next.setTenantAlbumCategoryId(albumCategory.getId()));
+            tenantAlbumCategoryDetailService.saveBatch(values);
+        }
+        return true;
+    }
+
+    /**
+     * 更新
+     *
+     * @param tenantAlbumCategory TenantAlbumCategoryWrapper.TenantAlbumCategory
+     * @return Boolean
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public Boolean update(TenantAlbumCategoryWrapper.TenantAlbumCategory tenantAlbumCategory) {
+
+        Integer count = this.lambdaQuery()
+                .eq(TenantAlbumCategory::getName, tenantAlbumCategory.getName())
+                .eq(TenantAlbumCategory::getCategoryType, tenantAlbumCategory.getCategoryType())
+                .eq(TenantAlbumCategory::getDelFlag, false)
+                .ne(TenantAlbumCategory::getId, tenantAlbumCategory.getId())
+                .count();
+        if (count > 0) {
+            throw new BizException("专辑分类名称已存在");
+        }
+
+        // 校验是否存在引用删除
+        List<TenantAlbumCategoryDetail> detailList = tenantAlbumCategoryDetailService.lambdaQuery()
+                .eq(TenantAlbumCategoryDetail::getTenantAlbumCategoryId, tenantAlbumCategory.getId())
+                .list();
+
+        List<TenantAlbumCategoryDetail> values = tenantAlbumCategory.getValues();
+        List<Long> newUpdateIds = values.stream().map(TenantAlbumCategoryDetail::getId).filter(Objects::nonNull)
+                .collect(Collectors.toList());
+
+        List<TenantAlbumCategoryDetail> removeDetailIdList =
+                detailList.stream().filter(next -> !newUpdateIds.contains(next.getId())).collect(Collectors.toList());
+        if (!removeDetailIdList.isEmpty()) {
+            TenantAlbumCategory albumCategory = this.getById(tenantAlbumCategory.getId());
+            checkTenantAlbumCategoryDetailUsed(albumCategory.getCategoryType(), removeDetailIdList);
+        }
+
+        tenantAlbumCategoryDetailService.lambdaUpdate()
+                .eq(TenantAlbumCategoryDetail::getTenantAlbumCategoryId, tenantAlbumCategory.getId())
+                .remove();
+        this.updateById(JSON.parseObject(tenantAlbumCategory.jsonString(), TenantAlbumCategory.class));
+        if (!values.isEmpty()) {
+            values.forEach(next -> next.setTenantAlbumCategoryId(tenantAlbumCategory.getId()));
+            tenantAlbumCategoryDetailService.saveBatch(values);
+        }
+        return true;
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public Boolean delete(Long id) {
+        TenantAlbumCategory albumCategory = this.getById(id);
+        if (albumCategory == null) {
+            throw new BizException("删除的数据不存在");
+        }
+        QueryWrapper<TenantAlbum> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda()
+                .eq(TenantAlbum::getCategoryLevelId, id)
+                .or()
+                .eq(TenantAlbum::getCategoryTypeId, id);
+        Integer count = tenantAlbumMapper.selectCount(queryWrapper);
+        if (count > 0) {
+            throw new BizException("专辑分类被使用");
+        }
+
+//        List<TenantAlbumCategoryDetail> list = tenantAlbumCategoryDetailService.lambdaQuery()
+//                .eq(TenantAlbumCategoryDetail::getTenantAlbumCategoryId, albumCategory.getId())
+//                .list();
+//        checkTenantAlbumCategoryDetailUsed(albumCategory.getCategoryType(), list);
+        albumCategory.setDelFlag(true);
+        this.updateById(albumCategory);
+        return true;
+    }
+
+    /**
+     * 校验专辑分类中的值是否有被应用
+     */
+    private void checkTenantAlbumCategoryDetailUsed(ETenantAlbumCategoryType categoryType,
+                                                    List<TenantAlbumCategoryDetail> list) {
+        if (list.isEmpty()) {
+            return;
+        }
+        List<String> idStrList = list.stream().map(next -> next.getId().toString()).collect(Collectors.toList());
+        QueryWrapper<TenantAlbumMusic> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda()
+                .in(ETenantAlbumCategoryType.CATEGORY_TYPE.equals(categoryType),
+                        TenantAlbumMusic::getType, idStrList)
+                .in(ETenantAlbumCategoryType.CATEGORY_LEVEL.equals(categoryType),
+                        TenantAlbumMusic::getType, idStrList);
+        Integer useCount = tenantAlbumMusicMapper.selectCount(queryWrapper);
+        // 删除的ID被应用的数量大于0
+        if (useCount > 0) {
+            throw new BizException("专辑分类存在" + (ETenantAlbumCategoryType.CATEGORY_TYPE.equals(categoryType) ? "类型" : "级别") + "引用");
+        }
+    }
+}

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

@@ -0,0 +1,423 @@
+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.IdWorker;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.yonge.cooleshow.auth.api.entity.SysUser;
+import com.yonge.cooleshow.biz.dal.dao.StudentDao;
+import com.yonge.cooleshow.biz.dal.dao.SubjectDao;
+import com.yonge.cooleshow.biz.dal.dao.TeacherDao;
+import com.yonge.cooleshow.biz.dal.dto.search.StudentSearch;
+import com.yonge.cooleshow.biz.dal.entity.ImGroup;
+import com.yonge.cooleshow.biz.dal.entity.ImGroupMember;
+import com.yonge.cooleshow.biz.dal.entity.Student;
+import com.yonge.cooleshow.biz.dal.entity.Subject;
+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.enums.ImGroupMemberRoleType;
+import com.yonge.cooleshow.biz.dal.enums.ImGroupType;
+import com.yonge.cooleshow.biz.dal.mapper.TenantGroupMapper;
+import com.yonge.cooleshow.biz.dal.service.ImGroupMemberService;
+import com.yonge.cooleshow.biz.dal.service.ImGroupService;
+import com.yonge.cooleshow.biz.dal.service.SysUserService;
+import com.yonge.cooleshow.biz.dal.service.TenantGroupService;
+import com.yonge.cooleshow.biz.dal.service.im.ImGroupCoreService;
+import com.yonge.cooleshow.biz.dal.vo.StudentVo;
+import com.yonge.cooleshow.biz.dal.wrapper.StudentWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.TenantGroupWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.im.ImGroupWrapper;
+import com.yonge.toolset.base.exception.BizException;
+import com.yonge.toolset.mybatis.support.PageUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 机构小组表
+ * 2023-10-11 09:24:50
+ */
+@Slf4j
+@Service
+public class TenantGroupServiceImpl extends ServiceImpl<TenantGroupMapper, TenantGroup> implements TenantGroupService {
+
+    @Autowired
+    private ImGroupService imGroupService;
+    @Autowired
+    private ImGroupCoreService imGroupCoreService;
+    @Autowired
+    private StudentDao studentDao;
+    @Autowired
+    private SubjectDao subjectDao;
+    @Autowired
+    private TeacherDao teacherDao;
+    @Autowired
+    private ImGroupMemberService imGroupMemberService;
+    @Autowired
+    private SysUserService sysUserService;
+
+    /**
+     * 查询详情
+     *
+     * @param id 详情ID
+     * @return TenantGroup
+     */
+    @Override
+    public TenantGroup detail(Long id) {
+
+        return baseMapper.selectById(id);
+    }
+
+    /**
+     * 分页查询
+     *
+     * @param page  IPage<TenantGroup>
+     * @param query TenantGroupWrapper.TenantGroupQuery
+     * @return IPage<TenantGroup>
+     */
+    @Override
+    public IPage<TenantGroupWrapper.TenantGroup> selectPage(IPage<TenantGroupWrapper.TenantGroup> page,
+                                                            TenantGroupWrapper.TenantGroupQuery query) {
+
+        IPage<TenantGroupWrapper.TenantGroup> iPage = page.setRecords(baseMapper.selectPage(page, query));
+        List<TenantGroupWrapper.TenantGroup> records = iPage.getRecords();
+        // 设置小组成员数量
+        if (!records.isEmpty()) {
+            List<Long> groupIdList =
+                    records.stream().map(TenantGroupWrapper.TenantGroup::getId).collect(Collectors.toList());
+            List<StudentWrapper.UserCount> userCounts = studentDao.countStudentByTenantGroupIds(query.getTenantId(),
+                    groupIdList);
+            Map<Long, Integer> groupBy =
+                    userCounts.stream().collect(Collectors.toMap(StudentWrapper.UserCount::getTenantGroupId
+                            , StudentWrapper.UserCount::getCount));
+            for (TenantGroupWrapper.TenantGroup record : records) {
+                record.setGroupUserCount(groupBy.getOrDefault(record.getId(), 0));
+            }
+        }
+        return iPage;
+    }
+
+    /**
+     * 添加
+     *
+     * @param tenantGroup TenantGroupWrapper.TenantGroup
+     * @return Boolean
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public Boolean add(TenantGroupWrapper.TenantGroup tenantGroup) {
+        TenantGroup entity = JSON.parseObject(tenantGroup.jsonString(), TenantGroup.class);
+        Integer count = this.lambdaQuery()
+                .eq(TenantGroup::getTenantId, tenantGroup.getTenantId())
+                .eq(TenantGroup::getName, entity.getName())
+                .count();
+        if (count > 0) {
+            throw new BizException("小组名称已存在");
+        }
+        this.save(entity);
+        List<Long> userIds = tenantGroup.getUserIds();
+        // 有群成员才建立机构小组群
+        if (!userIds.isEmpty()) {
+            // 建群
+            try {
+                ImGroupWrapper.ImGroup imGroup = new ImGroupWrapper.ImGroup();
+                imGroup.setId(IdWorker.getId());
+                imGroup.setCreateBy(tenantGroup.getAdminId());
+                imGroup.setType(ImGroupType.ORG.getCode());
+                imGroup.setName(tenantGroup.getName());
+                String imGroupId = imGroupService.create(imGroup);
+                entity.setImGroupId(imGroupId);
+                this.updateById(entity);
+            } catch (Exception e) {
+                log.error("创建机构小组群失败", e);
+                throw new BizException("创建机构小组群失败");
+            }
+            // 加群成员
+            try {
+                imGroupService.addGroupMember(entity.getImGroupId(), new HashSet<>(userIds));
+            } catch (Exception e) {
+                log.error("加入机构小组群失败", e);
+                throw new BizException("加入机构小组群失败");
+            }
+            studentDao.update(null, Wrappers.<Student>lambdaUpdate()
+                    .in(Student::getUserId, userIds)
+                    .eq(Student::getTenantId, tenantGroup.getTenantId())
+                    .set(Student::getTenantGroupId, entity.getId()));
+        }
+        return true;
+    }
+
+    /**
+     * 更新
+     *
+     * @param tenantGroup TenantGroupWrapper.TenantGroup
+     * @return Boolean
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public Boolean update(TenantGroupWrapper.TenantGroup tenantGroup) {
+        TenantGroup oldGroup = this.getById(tenantGroup.getId());
+
+        Integer count = this.lambdaQuery()
+                .ne(TenantGroup::getId, tenantGroup.getId())
+                .eq(TenantGroup::getTenantId, tenantGroup.getTenantId())
+                .eq(TenantGroup::getName, tenantGroup.getName())
+                .count();
+        if (count > 0) {
+            throw new BizException("小组名称已经存在");
+        }
+
+        TenantGroup entity = JSON.parseObject(tenantGroup.jsonString(), TenantGroup.class);
+        // 修改小组负责人,调整群主,更换群主需要新的群主为该群成员
+        if (oldGroup.getImGroupId() != null && !oldGroup.getAdminId().equals(entity.getAdminId())) {
+
+            // 如果新负责人不在群,先加入群
+            Integer toAdminInThisGroup = imGroupMemberService.lambdaQuery()
+                    .eq(ImGroupMember::getGroupId, oldGroup.getImGroupId())
+                    .eq(ImGroupMember::getUserId, entity.getAdminId())
+                    .count();
+            if (toAdminInThisGroup == 0) {
+                // 新负责人加入群
+                try {
+                    SysUser newTeacher = sysUserService.getByUserId(entity.getAdminId());
+                    ImGroupMember imGroupMember = new ImGroupMember();
+                    imGroupMember.setGroupId(oldGroup.getImGroupId());
+                    imGroupMember.setUserId(entity.getAdminId());
+                    imGroupMember.setRoleType(ImGroupMemberRoleType.TEACHER);
+                    imGroupMember.setAvatar(newTeacher.getAvatar());
+                    imGroupMember.setNickname(newTeacher.getUsername());
+                    imGroupMember.setIsAdmin(false);
+                    imGroupMember.setRoleType(ImGroupMemberRoleType.TEACHER);
+
+                    imGroupMemberService.join(Collections.singletonList(imGroupMember), oldGroup.getImGroupId());
+                } catch (Exception e) {
+                    log.error("负责人加入群失败", e);
+                    throw new BizException("负责人加入群失败");
+                }
+            }
+
+            ImGroup oldImGroup = imGroupService.getById(oldGroup.getImGroupId());
+            if (oldImGroup != null) {
+                try {
+                    imGroupCoreService.changeGroupOwner(oldGroup.getImGroupId(),
+                            String.valueOf(tenantGroup.getAdminId()),
+                            String.valueOf(oldGroup.getAdminId()));
+                    imGroupCoreService.groupQuit(oldGroup.getAdminId(), ClientEnum.TEACHER.getCode(),
+                            oldGroup.getImGroupId(), true);
+                } catch (Exception e) {
+                    log.error("更换群主失败", e);
+                    throw new BizException("更换群主失败");
+                }
+            }
+        }
+        QueryWrapper<Student> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda()
+                .eq(Student::getTenantId, tenantGroup.getTenantId())
+                .eq(Student::getTenantGroupId, tenantGroup.getId());
+        List<Long> oldGroupUsers = studentDao.selectList(queryWrapper).stream().map(Student::getUserId)
+                .collect(Collectors.toList());
+
+        List<Long> newGroupUsers = tenantGroup.getUserIds();
+
+        List<Long> removeGroupUsers = oldGroupUsers.stream()
+                .filter(next -> !newGroupUsers.contains(next)).collect(Collectors.toList());
+        // 移除的群成员,退群
+        if (!removeGroupUsers.isEmpty()) {
+            try {
+                for (Long removeUserId : removeGroupUsers) {
+                    imGroupCoreService.groupQuit(removeUserId, ClientEnum.STUDENT.getCode(), oldGroup.getImGroupId(),
+                            true);
+                }
+            } catch (Exception e) {
+                log.error("群成员移出群失败", e);
+                throw new BizException("群成员移出群失败");
+            }
+            studentDao.update(null, Wrappers.<Student>lambdaUpdate()
+                    .in(Student::getUserId, removeGroupUsers)
+                    .eq(Student::getTenantId, tenantGroup.getTenantId())
+                    .set(Student::getTenantGroupId, -1L));
+        }
+        // 新增的群成员
+        List<Long> newAddGroupUsers = newGroupUsers.stream().filter(next -> !oldGroupUsers.contains(next))
+                .collect(Collectors.toList());
+        if (!newAddGroupUsers.isEmpty()) {
+            // 加群成员
+            try {
+                imGroupService.addGroupMember(oldGroup.getImGroupId(), new HashSet<>(newAddGroupUsers));
+            } catch (Exception e) {
+                log.error("加入机构小组群失败", e);
+                throw new BizException("加入机构小组群失败");
+            }
+            studentDao.update(null, Wrappers.<Student>lambdaUpdate()
+                    .in(Student::getUserId, newAddGroupUsers)
+                    .eq(Student::getTenantId, tenantGroup.getTenantId())
+                    .set(Student::getTenantGroupId, entity.getId()));
+        }
+        return this.updateById(entity);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public Boolean delete(Long id) {
+        TenantGroup group = this.getById(id);
+        if (group == null) {
+            throw new BizException("机构小组不存在");
+        }
+        String imGroupId = group.getImGroupId();
+        if (imGroupId != null) {
+            ImGroup imGroup = imGroupService.getById(imGroupId);
+            if (imGroup != null) {
+                try {
+                    imGroupCoreService.groupDismiss(imGroupId);
+                } catch (Exception e) {
+                    log.error("解散群失败", e);
+                }
+            }
+        }
+        studentDao.update(null, Wrappers.<Student>lambdaUpdate()
+                .eq(Student::getTenantId, group.getTenantId())
+                .set(Student::getTenantGroupId, -1L));
+        return removeById(id);
+    }
+
+    /**
+     * 查询机构小组群成员
+     *
+     * @param tenantId      机构ID
+     * @param tenantGroupId 机构小组ID
+     * @return 机构小组成员列表
+     */
+    @Override
+    public List<TenantGroupWrapper.TenantGroupMember> queryGroupMember(Long tenantId, Long tenantGroupId) {
+        StudentSearch studentSearch = new StudentSearch();
+        studentSearch.setRows(1);
+        studentSearch.setPage(9999);
+        List<StudentVo> studentVos = studentDao.selectPage(PageUtil.getPage(studentSearch), studentSearch);
+        if (studentVos.isEmpty()) {
+            return new ArrayList<>();
+        }
+        Map<String, String> subjectIdNameMap = new HashMap<>();
+        List<String> subjectIds = studentVos.stream().map(Student::getSubjectId).filter(StringUtils::isNotEmpty)
+                .distinct().collect(Collectors.toList());
+
+        if (!subjectIds.isEmpty()) {
+            List<Subject> subject = subjectDao.findBySubjectByIdList(String.join(",", subjectIds));
+            Map<String, String> map = subject.stream().collect(Collectors.toMap(next -> next.getId().toString(),
+                    Subject::getName));
+            subjectIdNameMap.putAll(map);
+        }
+
+
+        return studentVos.stream().map(next -> {
+            TenantGroupWrapper.TenantGroupMember member = new TenantGroupWrapper.TenantGroupMember();
+            member.setUserId(next.getUserId());
+            member.setTenantId(next.getTenantId());
+            member.setName(next.getUsername());
+            member.setGender(next.getGender());
+            member.setBirthdate(next.getBirthdate());
+            member.setPhone(next.getPhone());
+            member.setAvatar(next.getAvatar());
+            member.setSubjectId(next.getSubjectId());
+            member.setSubjectName(subjectIdNameMap.getOrDefault(next.getSubjectId(), ""));
+            return member;
+        }).collect(Collectors.toList());
+    }
+
+    /**
+     * 小组交接
+     *
+     * @param adjustTenantGroup 入参
+     * @return 成功/失败
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public Boolean adjustTenantGroup(TenantGroupWrapper.AdjustTenantGroup adjustTenantGroup) {
+        Long teacherId = adjustTenantGroup.getTeacherId();
+        Teacher teacher = teacherDao.selectById(teacherId);
+        if (teacher == null) {
+            throw new BizException("移交的老师不存在");
+        }
+        List<TenantGroupWrapper.AdjustTenantGroupDetail> details = adjustTenantGroup.getAdjustTenantGroupDetails();
+        if (details.isEmpty()) {
+            throw new BizException("未选择移交信息");
+        }
+        List<Long> tenantGroupIds =
+                details.stream().map(TenantGroupWrapper.AdjustTenantGroupDetail::getTenantGroupId).collect(Collectors.toList());
+        List<TenantGroup> tenantGroups = this.lambdaQuery().in(TenantGroup::getId, tenantGroupIds).list();
+        Map<Long, String> groupBy = tenantGroups.stream().collect(Collectors.toMap(TenantGroup::getId,
+                TenantGroup::getImGroupId));
+        for (TenantGroupWrapper.AdjustTenantGroupDetail detail : details) {
+            Long toTeacher = detail.getTeacherId();
+            if (teacherId.equals(toTeacher) && Boolean.FALSE.equals(detail.getDismiss())) {
+                continue;
+            }
+            String imGroupId = groupBy.get(detail.getTenantGroupId());
+            ImGroup imGroup = imGroupService.getById(Optional.ofNullable(imGroupId).orElse("-1"));
+            if (toTeacher != null) {
+                // 更换群主
+                if (imGroup != null) {
+                    // 新负责人加入群
+                    try {
+                        SysUser newTeacher = sysUserService.getByUserId(toTeacher);
+                        ImGroupMember imGroupMember = new ImGroupMember();
+                        imGroupMember.setGroupId(imGroupId);
+                        imGroupMember.setUserId(toTeacher);
+                        imGroupMember.setRoleType(ImGroupMemberRoleType.TEACHER);
+                        imGroupMember.setAvatar(newTeacher.getAvatar());
+                        imGroupMember.setNickname(newTeacher.getUsername());
+                        imGroupMember.setIsAdmin(false);
+                        imGroupMember.setRoleType(ImGroupMemberRoleType.TEACHER);
+
+                        imGroupMemberService.join(Collections.singletonList(imGroupMember), imGroupId);
+                    } catch (Exception e) {
+                        log.error("负责人加入群失败", e);
+                        throw new BizException("负责人加入群失败");
+                    }
+
+                    try {
+                        imGroupCoreService.changeGroupOwner(imGroupId, String.valueOf(toTeacher),
+                                String.valueOf(teacherId));
+                        imGroupCoreService.groupQuit(teacherId, ClientEnum.TEACHER.getCode(),
+                                imGroupId, true);
+                    } catch (Exception e) {
+                        log.error("移交负责人失败", e);
+                        throw new BizException("移交负责人失败");
+                    }
+                }
+                this.lambdaUpdate()
+                        .set(TenantGroup::getAdminId, toTeacher)
+                        .set(imGroup == null, TenantGroup::getImGroupId, "-1")
+                        .eq(TenantGroup::getId, detail.getTenantGroupId())
+                        .update();
+            } else if (Boolean.TRUE.equals(detail.getDismiss())) {
+                // 解散群
+                if (imGroup != null) {
+                    try {
+                        imGroupCoreService.groupDismiss(imGroupId);
+                    } catch (Exception e) {
+                        log.error("解散群失败", e);
+                        throw new BizException("解散群失败");
+                    }
+                }
+                this.removeById(detail.getTenantGroupId());
+            }
+        }
+        return true;
+    }
+}

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

@@ -16,6 +16,7 @@ import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.enums.ImGroupType;
 import com.yonge.cooleshow.biz.dal.enums.MessageTypeEnum;
 import com.yonge.cooleshow.biz.dal.mapper.SysAreaMapper;
+import com.yonge.cooleshow.biz.dal.mapper.TenantGroupMapper;
 import com.yonge.cooleshow.biz.dal.mapper.TenantUnbindRecordMapper;
 import com.yonge.cooleshow.biz.dal.service.*;
 import com.yonge.cooleshow.biz.dal.wrapper.TenantUnbindRecordWrapper;
@@ -85,6 +86,9 @@ public class TenantUnbindRecordServiceImpl extends ServiceImpl<TenantUnbindRecor
     @Autowired
     private ImUserFriendService imUserFriendService;
 
+    @Autowired
+    private TenantGroupMapper tenantGroupMapper;
+
     /**
      * @param id 详情ID
      * @return TenantUnbindRecord
@@ -211,6 +215,18 @@ public class TenantUnbindRecordServiceImpl extends ServiceImpl<TenantUnbindRecor
         if (unbindRecord == null || !ETenantUnBindAuditStatus.DOING.equals(unbindRecord.getStatus())) {
             throw new BizException("审核记录不存在");
         }
+        Teacher teacher = teacherDao.selectById(unbindRecord.getUserId());
+
+        // 判断是否存在机构小组群主身份
+        QueryWrapper<TenantGroup> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda()
+                .eq(TenantGroup::getTenantId, teacher.getTenantId())
+                .eq(TenantGroup::getAdminId, teacher.getUserId());
+        Integer count = tenantGroupMapper.selectCount(queryWrapper);
+        if (count > 0) {
+            throw new BizException(5100, "该老师存在机构小组负责人身份,请先移交小组或者解散小组");
+        }
+
         this.lambdaUpdate()
                 .set(TenantUnbindRecord::getStatus, Boolean.TRUE.equals(audio.getStatus()) ?
                         ETenantUnBindAuditStatus.PASS : ETenantUnBindAuditStatus.UNPASS)
@@ -221,7 +237,6 @@ public class TenantUnbindRecordServiceImpl extends ServiceImpl<TenantUnbindRecor
                 .eq(TenantUnbindRecord::getStatus, ETenantUnBindAuditStatus.DOING)
                 .update();
         if (Boolean.TRUE.equals(audio.getStatus())) {
-            Teacher teacher = teacherDao.selectById(unbindRecord.getUserId());
             Long tenantId = teacher.getTenantId();
             if (tenantId != -1L) {
                 // 解散机构群

+ 21 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/PaymentDivMemberRecordWrapper.java

@@ -1,5 +1,6 @@
 package com.yonge.cooleshow.biz.dal.wrapper;
 
+import com.alibaba.excel.annotation.ExcelProperty;
 import com.alibaba.fastjson.JSON;
 import com.microsvc.toolkit.common.response.paging.QueryInfo;
 import com.yonge.cooleshow.biz.dal.enums.OrderStatusEnum;
@@ -124,6 +125,26 @@ public class PaymentDivMemberRecordWrapper {
     }
 
     @Data
+    @ApiModel("首页收入明细导出")
+    public static class IndexIncomeExportDto{
+
+        @ExcelProperty("用户名称")
+        private String name;
+
+        @ExcelProperty("手机号")
+        private String phone;
+
+        @ExcelProperty("收入")
+        private BigDecimal amount;
+
+        @ExcelProperty("订单时间")
+        private String payTime;
+
+        @ExcelProperty("订单类型")
+        private String orderType;
+    }
+
+    @Data
     @ApiModel("首页收入、支出汇总")
     public static class IndexIncomeSummaryDto{
 

+ 9 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/StudentWrapper.java

@@ -102,6 +102,9 @@ public class StudentWrapper {
         @ApiModelProperty("所属机构,开放接口必填")
         private Long tenantId;
 
+        @ApiModelProperty("机构小组ID")
+        private Long tenantGroupId;
+
         @ApiModelProperty("姓名")
         private String name;
 
@@ -215,4 +218,10 @@ public class StudentWrapper {
         @NotNull
         private String password;
     }
+
+    @Data
+    public static class UserCount {
+        private Long tenantGroupId;
+        private Integer count;
+    }
 }

+ 63 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/TenantAlbumCategoryDetailWrapper.java

@@ -0,0 +1,63 @@
+package com.yonge.cooleshow.biz.dal.wrapper;
+
+import com.alibaba.fastjson.JSON;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import java.util.Optional;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * 机构专辑分类详情
+ * 2023-10-10 10:17:38
+ */
+@ApiModel(value = "TenantAlbumCategoryDetailWrapper对象", description = "机构专辑分类详情查询对象")
+public class TenantAlbumCategoryDetailWrapper {
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel(" TenantAlbumCategoryDetailQuery-机构专辑分类详情")
+    public static class TenantAlbumCategoryDetailQuery implements QueryInfo {
+
+        @ApiModelProperty("当前页")
+        private Integer page;
+
+        @ApiModelProperty("分页行数")
+        private Integer rows;
+
+        @ApiModelProperty("关键字匹配")
+        private String keyword;
+
+        public String getKeyword() {
+            return Optional.ofNullable(keyword).filter(StringUtils::isNotBlank).orElse(null);
+        }
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantAlbumCategoryDetailQuery from(String json) {
+            return JSON.parseObject(json, TenantAlbumCategoryDetailQuery.class);
+        }
+    }
+
+    @ApiModel(" TenantAlbumCategoryDetail-机构专辑分类详情")
+    public static class TenantAlbumCategoryDetail {
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantAlbumCategoryDetail from(String json) {
+            return JSON.parseObject(json, TenantAlbumCategoryDetail.class);
+        }
+    }
+
+}

+ 106 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/TenantAlbumCategoryWrapper.java

@@ -0,0 +1,106 @@
+package com.yonge.cooleshow.biz.dal.wrapper;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import com.yonge.cooleshow.biz.dal.entity.TenantAlbumCategoryDetail;
+import com.yonge.cooleshow.common.enums.ETenantAlbumCategoryType;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * 机构专辑分类
+ * 2023-10-10 09:48:46
+ */
+@ApiModel(value = "TenantAlbumCategoryWrapper对象", description = "机构专辑分类查询对象")
+public class TenantAlbumCategoryWrapper {
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel(" TenantAlbumCategoryQuery-机构专辑分类")
+    public static class TenantAlbumCategoryQuery implements QueryInfo {
+
+        @ApiModelProperty("当前页")
+        private Integer page;
+
+        @ApiModelProperty("分页行数")
+        private Integer rows;
+
+        @ApiModelProperty("关键字匹配")
+        private String keyword;
+
+        @ApiModelProperty("分类类别")
+        private ETenantAlbumCategoryType categoryType;
+
+        public String getKeyword() {
+            return Optional.ofNullable(keyword).filter(StringUtils::isNotBlank).orElse(null);
+        }
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantAlbumCategoryQuery from(String json) {
+            return JSON.parseObject(json, TenantAlbumCategoryQuery.class);
+        }
+    }
+
+    @Data
+    @ApiModel(" TenantAlbumCategory-机构专辑分类")
+    public static class TenantAlbumCategory {
+
+        @ApiModelProperty("主键ID")
+        private Long id;
+
+        @ApiModelProperty("专辑分类名称")
+        @NotNull(message = "专辑分类名称不能为空")
+        private String name;
+
+        @ApiModelProperty("专辑分类类别")
+        private ETenantAlbumCategoryType categoryType;
+
+        @ApiModelProperty("删除标记")
+        private Boolean delFlag;
+
+        @ApiModelProperty("创建人")
+        private Long createBy;
+
+        @ApiModelProperty("创建时间")
+        private Date createTime;
+
+        @ApiModelProperty("修改人")
+        private Long updateBy;
+
+        @ApiModelProperty("更新时间")
+        private Date updateTime;
+
+        @ApiModelProperty("专辑分类级别/类别值")
+        private List<TenantAlbumCategoryDetail> values = new ArrayList<>();
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantAlbumCategory from(String json) {
+            return JSON.parseObject(json, TenantAlbumCategory.class);
+        }
+    }
+
+}

+ 6 - 3
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/TenantAlbumWrapper.java

@@ -50,9 +50,6 @@ public class TenantAlbumWrapper {
         @ApiModelProperty("机构ID")
         private Long tenantId;
 
-        @ApiModelProperty("专辑名称")
-        private String name;
-
         public String getKeyword() {
             return Optional.ofNullable(keyword).filter(StringUtils::isNotBlank).orElse(null);
         }
@@ -139,6 +136,12 @@ public class TenantAlbumWrapper {
         @ApiModelProperty("声部曲目数")
         private Integer SubjectCounts;
 
+        @ApiModelProperty("专辑分类类型ID")
+        private Long categoryTypeId;
+
+        @ApiModelProperty("专辑分类等级ID")
+        private Long categoryLevelId;
+
         @ApiModelProperty("曲目相关信息")
         private List<MusicSheetData> musicSheetData = new ArrayList<>();
 

+ 171 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/TenantGroupWrapper.java

@@ -0,0 +1,171 @@
+package com.yonge.cooleshow.biz.dal.wrapper;
+
+import com.alibaba.fastjson.JSON;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import com.yonge.cooleshow.biz.dal.enums.GenderEnum;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * 机构小组表
+ * 2023-10-11 09:24:50
+ */
+@ApiModel(value = "TenantGroupWrapper对象", description = "机构小组表查询对象")
+public class TenantGroupWrapper {
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel(" TenantGroupQuery-机构小组表")
+    public static class TenantGroupQuery implements QueryInfo {
+
+        @ApiModelProperty("当前页")
+        private Integer page;
+
+        @ApiModelProperty("分页行数")
+        private Integer rows;
+
+        @ApiModelProperty("关键字匹配")
+        private String keyword;
+
+        @ApiModelProperty("机构ID")
+        private Long tenantId;
+
+        @ApiModelProperty("机构小组负责人")
+        private Long adminId;
+
+        public String getKeyword() {
+            return Optional.ofNullable(keyword).filter(StringUtils::isNotBlank).orElse(null);
+        }
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantGroupQuery from(String json) {
+            return JSON.parseObject(json, TenantGroupQuery.class);
+        }
+    }
+
+    @Data
+    @ApiModel(" TenantGroup-机构小组表")
+    public static class TenantGroup {
+
+        @ApiModelProperty("主键ID")
+        private Long id;
+
+        @ApiModelProperty("机构ID")
+        private Long tenantId;
+
+        @ApiModelProperty("小组名称")
+        private String name;
+
+        @ApiModelProperty("管理员ID")
+        private Long adminId;
+
+        @ApiModelProperty("IM群聊ID")
+        private String imGroupId;
+
+        @ApiModelProperty("创建人")
+        private Long createBy;
+
+        @ApiModelProperty("创建时间")
+        private Date createTime;
+
+        @ApiModelProperty("群成员列表")
+        private List<Long> userIds = new ArrayList<>();
+
+        @ApiModelProperty("群成员数量")
+        private Integer groupUserCount = 0;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantGroup from(String json) {
+            return JSON.parseObject(json, TenantGroup.class);
+        }
+    }
+
+    @Data
+    @ApiModel(" TenantGroup-机构小组成员")
+    public static class TenantGroupMember {
+
+        @ApiModelProperty("userId")
+        private Long userId;
+
+        @ApiModelProperty("所属机构,开放接口必填")
+        private Long tenantId;
+
+        @ApiModelProperty("姓名")
+        private String name;
+
+        @ApiModelProperty("性别。0:女 1:男")
+        private GenderEnum gender;
+
+        @ApiModelProperty("生日")
+        private Date birthdate;
+
+        @ApiModelProperty("手机号码")
+        private String phone;
+
+        @ApiModelProperty("头像")
+        private String avatar;
+
+        @ApiModelProperty("声部")
+        private String subjectId;
+
+        @ApiModelProperty("声部")
+        private String subjectName;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantGroup from(String json) {
+            return JSON.parseObject(json, TenantGroup.class);
+        }
+    }
+
+    @Data
+    @ApiModel("调整机构小组")
+    public static class AdjustTenantGroup {
+
+        @ApiModelProperty("移交前的负责人ID")
+        @NotNull
+        private Long teacherId;
+
+        @ApiModelProperty("机构小组调整详情")
+        private List<AdjustTenantGroupDetail> adjustTenantGroupDetails = new ArrayList<>();
+
+    }
+
+    @Data
+    @ApiModel("调整机构小组详情")
+    public static class AdjustTenantGroupDetail{
+        @ApiModelProperty("机构小组ID")
+        private Long tenantGroupId;
+
+        @ApiModelProperty("移交后的负责人ID")
+        private Long teacherId;
+
+        @ApiModelProperty("是否解散")
+        private Boolean dismiss = false;
+    }
+
+}

+ 20 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/StudentMapper.xml

@@ -14,6 +14,7 @@
         <result column="create_time_" property="createTime"/>
         <result column="update_time_" property="updateTime"/>
         <result column="avatar_" property="avatar"/>
+        <result column="tenant_group_id_" property="tenantGroupId"/>
     </resultMap>
 
     <!-- 表字段 -->
@@ -31,6 +32,7 @@
         , t.tenant_id_ as tenantId
         , t.create_time_ as `createTime`
         , t.update_time_ as `updateTime`
+        , t.tenant_group_id_ as `tenantGroupId`
         </sql>
     <update id="setSubject">
         update student set subject_id_ = #{subjectIds},update_time_ = now() where user_id_ = #{id}
@@ -353,4 +355,22 @@
             </if>
         </where>
     </select>
+
+    <select id="countStudentByTenantGroupIds" resultType="com.yonge.cooleshow.biz.dal.wrapper.StudentWrapper$UserCount">
+        select ifnull(tenant_group_id_,-1) tenantGroupId, count(user_id_) count
+        from student
+        <where>
+            lock_flag_ = 0
+            <if test="tenantId != null">
+                AND tenant_id_ =#{tenantId}
+            </if>
+            <if test="groupIdList != null and groupIdList.size() > 0">
+                AND tenant_group_id_ in
+                <foreach collection="groupIdList" item="item" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </if>
+        </where>
+        group by tenant_group_id_
+    </select>
 </mapper>

+ 64 - 10
cooleshow-user/user-biz/src/main/resources/config/mybatis/SysMusicCompareRecordMapper.xml

@@ -380,28 +380,82 @@
 			AND st.tenant_id_ = #{tenantId}
 		</if>
 		group by st.user_id_
+		<trim prefix="HAVING" suffixOverrides="AND" >
+			<if test="minTrainTimes != null">
+				trainTimes &gt;= #{minTrainTimes} AND
+			</if>
+			<if test="maxTrainTimes != null">
+				trainTimes &lt;= #{maxTrainTimes} AND
+			</if>
+			<if test="minTrainDays != null">
+				trainDays &gt;= #{minTrainDays} AND
+			</if>
+			<if test="maxTrainDays != null">
+				trainDays &lt;= #{maxTrainDays} AND
+			</if>
+			<if test="minAvgTrainTimes != null">
+				avgTrainTimes &gt;= #{minAvgTrainTimes} AND
+			</if>
+			<if test="maxAvgTrainTimes != null">
+				avgTrainTimes &lt;= #{maxAvgTrainTimes} AND
+			</if>
+		</trim>
 		ORDER BY ${sortField} ${sortType}
 		<include refid="global.limit"/>
 	</select>
 	<select id="countWeChatStudentTrainData" resultType="java.lang.Integer">
-		select COUNT(DISTINCT st.user_id_)
+		select COUNT(c.user_id_) from
+		(
+		select st.user_id_
 		from student st
-		left join sys_user su ON st.user_id_ = su.id_
-		LEFT JOIN sys_music_compare_record smcr ON smcr.user_id_ = st.user_id_
+		left join
+		(select smcr.user_id_,
+		DATE_FORMAT(smcr.create_time_, '%Y-%m-%d') create_time_,
+		SUM(smcr.play_time_)                       playTime
+		from sys_music_compare_record smcr
 		<where>
-			<if test="clientId != null and clientId!= ''">
-				AND smcr.client_id_ = #{clientId}
+			<if test="startTime != null and startTime != ''">
+				AND smcr.create_time_ BETWEEN #{startTime} AND #{endTime}
 			</if>
 			<if test="tenantId != null">
 				AND smcr.tenant_id_ = #{tenantId}
 			</if>
-			<if test="startTime != null and startTime != ''">
-				AND smcr.create_time_ BETWEEN #{startTime} AND #{endTime}
-			</if>
-			<if test="search != null and search != ''">
-				AND su.username_ LIKE CONCAT('%',#{keyword},'%')
+			<if test="clientId != null and clientId!= ''">
+				AND smcr.client_id_ = #{clientId}
 			</if>
 		</where>
+		group by smcr.user_id_, DATE_FORMAT(create_time_, '%Y-%m-%d')) mprs ON mprs.user_id_ = st.user_id_
+		left join sys_user su ON st.user_id_ = su.id_
+		left join subject sb ON sb.id_ = st.subject_id_
+		where mprs.user_id_ = st.user_id_
+		<if test="search != null and search != ''">
+			AND su.username_ LIKE CONCAT('%',#{keyword},'%')
+		</if>
+		<if test="tenantId != null">
+			AND st.tenant_id_ = #{tenantId}
+		</if>
+		group by st.user_id_
+		<trim prefix="HAVING" suffixOverrides="AND" >
+			<if test="minTrainTimes != null">
+				trainTimes &gt;= #{minTrainTimes} AND
+			</if>
+			<if test="maxTrainTimes != null">
+				trainTimes &lt;= #{maxTrainTimes} AND
+			</if>
+			<if test="minTrainDays != null">
+				trainDays &gt;= #{minTrainDays} AND
+			</if>
+			<if test="maxTrainDays != null">
+				trainDays &lt;= #{maxTrainDays} AND
+			</if>
+			<if test="minAvgTrainTimes != null">
+				avgTrainTimes &gt;= #{minAvgTrainTimes} AND
+			</if>
+			<if test="maxAvgTrainTimes != null">
+				avgTrainTimes &lt;= #{maxAvgTrainTimes} AND
+			</if>
+		</trim>
+		)c
 	</select>
 	<select id="wechatCompareRecordSum"
 			resultType="com.yonge.cooleshow.biz.dal.queryInfo.SysMusicCompareRecordQueryInfo$WechatCompareRecordSumDto">

+ 20 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/TenantAlbumCategoryDetailMapper.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE  mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="com.yonge.cooleshow.biz.dal.mapper.TenantAlbumCategoryDetailMapper">
+
+
+
+    <!-- 表字段 -->
+    <sql id="baseColumns">
+        t.id_ AS id
+        , t.tenant_album_category_id_ AS tenantAlbumCategoryId
+        , t.value_ AS value
+    </sql>
+
+    <select id="selectPage" resultType="com.yonge.cooleshow.biz.dal.entity.TenantAlbumCategoryDetail">
+        SELECT
+        <include refid="baseColumns" />
+        FROM tenant_album_category_detail t
+    </select>
+
+</mapper>

+ 35 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/TenantAlbumCategoryMapper.xml

@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE  mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="com.yonge.cooleshow.biz.dal.mapper.TenantAlbumCategoryMapper">
+
+
+
+    <!-- 表字段 -->
+    <sql id="baseColumns">
+        t.id_ AS id
+        , t.name_ AS name
+        , t.category_type_ AS categoryType
+        , t.del_flag_ AS delFlag
+        , t.create_by_ AS createBy
+        , t.create_time_ AS createTime
+        , t.update_by_ AS updateBy
+        , t.update_time_ AS updateTime
+    </sql>
+
+    <select id="selectPage" resultType="com.yonge.cooleshow.biz.dal.entity.TenantAlbumCategory">
+        SELECT
+        <include refid="baseColumns" />
+        FROM tenant_album_category t
+        <where>
+            t.del_flag_ = 0
+            <if test="param.keyword != null and param.keyword.trim() != ''">
+                AND t.name_ LIKE concat('%',#{param.keyword},'%')
+            </if>
+            <if test="param.categoryType != null">
+                AND t.category_type_ = #{param.categoryType}
+            </if>
+        </where>
+
+    </select>
+
+</mapper>

+ 6 - 2
cooleshow-user/user-biz/src/main/resources/config/mybatis/TenantAlbumMapper.xml

@@ -17,6 +17,8 @@
         , t.update_time_ AS updateTime
         , t.create_time_ AS createTime
         , t.cost_price_ AS costPrice
+        , t.category_type_id_ AS categoryTypeId
+        , t.category_level_id_ AS categoryLevelId
     </sql>
 
     <select id="selectPage" resultType="com.yonge.cooleshow.biz.dal.wrapper.TenantAlbumWrapper$TenantAlbum">
@@ -28,8 +30,10 @@
         LEFT JOIN tenant_album_ref r on t.id_ = r.tenant_album_id_
         left join tenant_info i on r.tenant_id_ = i.id_
         <where>
-            <if test="param.name != null and param.name != ''">
-                and t.name_ like CONCAT('%', #{param.name}, '%')
+            <if test="param.keyword !=null and param.keyword.trim() !=''">
+                and (t.name_ like concat('%',#{param.keyword},'%')
+                or i.name_ like concat('%',#{param.keyword},'%')
+                )
             </if>
             <if test="param.tenantId != null ">
                 and i.id_= #{param.tenantId}

+ 35 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/TenantGroupMapper.xml

@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE  mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="com.yonge.cooleshow.biz.dal.mapper.TenantGroupMapper">
+
+
+
+    <!-- 表字段 -->
+    <sql id="baseColumns">
+        t.id_ AS id
+        , t.tenant_id_ AS tenantId
+        , t.name_ AS name
+        , t.admin_id_ AS adminId
+        , t.im_group_id_ AS imGroupId
+        , t.create_by_ AS createBy
+        , t.create_time_ AS createTime
+    </sql>
+
+    <select id="selectPage" resultType="com.yonge.cooleshow.biz.dal.wrapper.TenantGroupWrapper$TenantGroup">
+        SELECT
+        <include refid="baseColumns" />
+        FROM tenant_group t
+        <where>
+            <if test="param.keyword != null and param.keyword.trim() !=''">
+                t.name_ like concat('%',#{param.keyword},'%')
+            </if>
+            <if test="param.tenantId != null">
+                t.tenant_id_ = #{param.tenantId}
+            </if>
+            <if test="param.adminId != null">
+                t.admin_id_ = #{param.adminId}
+            </if>
+        </where>
+    </select>
+
+</mapper>

+ 1 - 1
cooleshow-user/user-tenant/src/main/java/com/yonge/cooleshow/tenant/controller/StudentController.java

@@ -137,7 +137,7 @@ public class StudentController extends BaseController {
         return succeed(PageUtil.pageInfo(pages));
     }
 
-    @GetMapping("/exportStudent")
+    @PostMapping("/exportStudent")
     @ApiOperation(value = "导出学生", notes = "传入TeacherSearch")
     public HttpResponseResult<OssFileWrapper.ExportFile> exportTeacher(@RequestBody StudentSearch query) {
         TenantInfo tenantInfo = getTenantInfo();

+ 1 - 1
cooleshow-user/user-tenant/src/main/java/com/yonge/cooleshow/tenant/controller/TeacherController.java

@@ -111,7 +111,7 @@ public class TeacherController extends BaseController {
     }
 
 
-    @GetMapping("/exportTeacher")
+    @PostMapping("/exportTeacher")
     @ApiOperation(value = "导出老师", notes = "传入TeacherSearch")
     public HttpResponseResult<OssFileWrapper.ExportFile> exportTeacher(@RequestBody TeacherSearch query) {
         TenantInfo tenantInfo = getTenantInfo();

+ 10 - 2
cooleshow-user/user-tenant/src/main/java/com/yonge/cooleshow/tenant/controller/TenantActivationCodeController.java

@@ -21,12 +21,14 @@ import com.yonge.cooleshow.biz.dal.entity.TenantAlbumPurchase;
 import com.yonge.cooleshow.biz.dal.entity.TenantInfo;
 import com.yonge.cooleshow.biz.dal.entity.TenantStaff;
 import com.yonge.cooleshow.biz.dal.entity.UserOrder;
+import com.yonge.cooleshow.biz.dal.service.OssFileService;
 import com.yonge.cooleshow.biz.dal.service.TenantActivationCodeService;
 import com.yonge.cooleshow.biz.dal.service.TenantAlbumPurchaseService;
 import com.yonge.cooleshow.biz.dal.service.TenantAlbumService;
 import com.yonge.cooleshow.biz.dal.service.TenantInfoService;
 import com.yonge.cooleshow.biz.dal.service.TenantStaffService;
 import com.yonge.cooleshow.biz.dal.service.UserOrderService;
+import com.yonge.cooleshow.biz.dal.wrapper.OssFileWrapper;
 import com.yonge.cooleshow.biz.dal.wrapper.TenantActivationCodeWrapper;
 import com.yonge.cooleshow.common.controller.BaseController;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
@@ -58,6 +60,9 @@ import org.springframework.web.multipart.MultipartFile;
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
 import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLEncoder;
 import java.text.MessageFormat;
 import java.text.ParsePosition;
 import java.text.SimpleDateFormat;
@@ -93,6 +98,8 @@ public class TenantActivationCodeController extends BaseController {
     private UserOrderService userOrderService;
     @Autowired
     private TenantAlbumService tenantAlbumService;
+    @Autowired
+    private OssFileService ossFileService;
 
     @ApiOperation(value = "详情", notes = "机构激活码-根据详情ID查询单条, 传入id")
 //    @GetMapping("/detail/{id}")
@@ -198,7 +205,7 @@ public class TenantActivationCodeController extends BaseController {
 
     @GetMapping("/exportActiveCode")
     @ApiOperation(value = "导出模板")
-    public HttpResponseResult<TenantActivationCodeWrapper.ExportFile> exportActiveCode(HttpServletResponse response, String orderNo) {
+    public HttpResponseResult<OssFileWrapper.ExportFile> exportActiveCode(HttpServletResponse response, String orderNo) {
         SysUser sysUser = sysUserFeignService.queryUserInfo();
         if (sysUser == null) {
             throw new BizException("请登录");
@@ -230,7 +237,8 @@ public class TenantActivationCodeController extends BaseController {
 
         String format = new SimpleDateFormat("yyyyMMddHHmm").format(userOrder.getCreateTime());
 
-        TenantActivationCodeWrapper.ExportFile exportFile = generateExportExcelFile(templates,
+
+        OssFileWrapper.ExportFile exportFile = ossFileService.uploadFile(templates,
                 TenantActivationCodeWrapper.ExportTemplate.class, format + tenantAlbum.getName(), "激活码");
         return succeed(exportFile);
     }

+ 105 - 0
cooleshow-user/user-tenant/src/main/java/com/yonge/cooleshow/tenant/controller/TenantGroupController.java

@@ -0,0 +1,105 @@
+package com.yonge.cooleshow.tenant.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.microsvc.toolkit.common.response.paging.PageInfo;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import com.microsvc.toolkit.common.response.template.R;
+import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
+import com.yonge.cooleshow.auth.api.entity.SysUser;
+import com.yonge.cooleshow.biz.dal.entity.TenantGroup;
+import com.yonge.cooleshow.biz.dal.service.TenantGroupService;
+import com.yonge.cooleshow.biz.dal.wrapper.TenantGroupWrapper;
+import com.yonge.cooleshow.tenant.vo.TenantGroupVo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+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;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+@Slf4j
+@Validated
+@RestController
+@RequestMapping("/tenantGroup")
+@Api(tags = "机构小组表")
+public class TenantGroupController {
+
+    @Autowired
+    private TenantGroupService tenantGroupService;
+
+    @Resource
+    private SysUserFeignService sysUserFeignService;
+
+    @ApiOperation(value = "详情", notes = "机构小组表-根据详情ID查询单条, 传入id")
+    @GetMapping("/detail/{id}")
+    public R<TenantGroup> detail(@PathVariable("id") Long id) {
+        TenantGroup wrapper = tenantGroupService.detail(id);
+        return R.from(wrapper);
+    }
+
+    @ApiOperation(value = "查询机构群成员", notes = "查询机构群成员")
+    @GetMapping("/queryGroupMember/{tenantGroupId}")
+    public R<List<TenantGroupWrapper.TenantGroupMember>> queryGroupMember(@PathVariable("tenantGroupId") Long tenantGroupId) {
+        SysUser sysUser = sysUserFeignService.queryUserInfo();
+        return R.from(tenantGroupService.queryGroupMember(sysUser.getTenantId(), tenantGroupId));
+    }
+
+    @ApiOperation(value = "查询分页", notes = "机构小组表- 传入 TenantGroupWrapper.TenantGroupQuery")
+    @PostMapping("/page")
+    public R<PageInfo<TenantGroupWrapper.TenantGroup>> page(@RequestBody TenantGroupWrapper.TenantGroupQuery query) {
+
+        IPage<TenantGroupWrapper.TenantGroup> pages = tenantGroupService.selectPage(QueryInfo.getPage(query), query);
+
+        return R.from(QueryInfo.pageInfo(pages));
+    }
+
+    @ApiOperation(value = "新增", notes = "机构小组表- 传入 TenantGroupWrapper.TenantGroup")
+    @PostMapping("/save")
+    public R<JSONObject> add(@Validated @RequestBody TenantGroupVo.TenantGroup tenantGroup) {
+        SysUser sysUser = sysUserFeignService.queryUserInfo();
+        TenantGroupWrapper.TenantGroup group = TenantGroupWrapper.TenantGroup.from(tenantGroup.jsonString());
+        group.setCreateBy(sysUser.getId());
+        group.setTenantId(sysUser.getTenantId());
+        // 新增数据
+        tenantGroupService.add(group);
+
+        return R.defaultR();
+    }
+
+    @ApiOperation(value = "修改", notes = "机构小组表- 传入 TenantGroupWrapper.TenantGroup")
+    @PostMapping("/update")
+    public R<JSONObject> update(@Validated @RequestBody TenantGroupVo.TenantGroup tenantGroup) {
+        // 更新数据
+        SysUser sysUser = sysUserFeignService.queryUserInfo();
+        TenantGroupWrapper.TenantGroup group = TenantGroupWrapper.TenantGroup.from(tenantGroup.jsonString());
+        group.setCreateBy(sysUser.getId());
+        group.setTenantId(sysUser.getTenantId());
+        tenantGroupService.update(group);
+
+        return R.defaultR();
+    }
+
+    @ApiOperation(value = "删除", notes = "机构小组表- 传入id")
+    @PostMapping("/remove")
+    public R<Boolean> remove(@RequestParam Long id) {
+
+        return R.from(tenantGroupService.delete(id));
+    }
+
+    @ApiOperation(value = "处理机构小组交接", notes = "处理机构小组交接")
+    @PostMapping("/adjustTenantGroup")
+    public R<Boolean> adjustTenantGroup(@RequestBody TenantGroupWrapper.AdjustTenantGroup adjustTenantGroup) {
+        return R.from(tenantGroupService.adjustTenantGroup(adjustTenantGroup));
+    }
+}

+ 3 - 0
cooleshow-user/user-tenant/src/main/java/com/yonge/cooleshow/tenant/vo/StudentVo.java

@@ -48,5 +48,8 @@ public class StudentVo {
 
         @ApiModelProperty("短信验证码")
         private String code;
+
+        @ApiModelProperty("机构小组ID")
+        private Long tenantGroupId;
     }
 }

+ 72 - 0
cooleshow-user/user-tenant/src/main/java/com/yonge/cooleshow/tenant/vo/TenantGroupVo.java

@@ -0,0 +1,72 @@
+package com.yonge.cooleshow.tenant.vo;
+
+import com.alibaba.fastjson.JSON;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * 机构小组表
+ * 2023-10-11 09:24:50
+ */
+@ApiModel(value = "TenantGroupVo对象", description = "机构小组表查询视图对象")
+public class TenantGroupVo {
+
+    @Data
+    @ApiModel(" TenantGroupQuery-机构小组表")
+    public static class TenantGroupQuery implements QueryInfo {
+
+        @ApiModelProperty("当前页")
+        private Integer page;
+
+        @ApiModelProperty("分页行数")
+        private Integer rows;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantGroupQuery from(String json) {
+            return JSON.parseObject(json, TenantGroupQuery.class);
+        }
+    }
+
+    @Data
+    @ApiModel(" TenantGroup-机构小组表")
+    public static class TenantGroup {
+
+        @ApiModelProperty("主键ID")
+        private Long id;
+
+        @ApiModelProperty("机构ID")
+        private Long tenantId;
+
+        @ApiModelProperty("小组名称")
+        @NotNull(message = "小组名称不能为空")
+        private String name;
+
+        @ApiModelProperty("小组负责人")
+        @NotNull(message = "小组负责人不能为空")
+        private Long adminId;
+
+        @ApiModelProperty("群成员列表")
+        private List<Long> userIds = new ArrayList<>();
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantGroup from(String json) {
+            return JSON.parseObject(json, TenantGroup.class);
+        }
+    }
+
+}