Эх сурвалжийг харах

!580 会员: 增加会员分组功能
Merge pull request !580 from 疯狂的世界/member_dev

芋道源码 1 жил өмнө
parent
commit
883bdc65a1
53 өөрчлөгдсөн 863 нэмэгдсэн , 415 устгасан
  1. 36 0
      sql/mysql/member_group.sql
  2. 19 12
      yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java
  3. 81 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/MemberGroupController.java
  4. 29 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupBaseVO.java
  5. 14 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupCreateReqVO.java
  6. 30 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupPageReqVO.java
  7. 22 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupRespVO.java
  8. 18 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupSimpleRespVO.java
  9. 20 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupUpdateReqVO.java
  10. 4 43
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/MemberExperienceLogController.java
  11. 0 11
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/MemberLevelController.java
  12. 4 44
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/MemberLevelLogController.java
  13. 0 2
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/experience/MemberExperienceLogBaseVO.java
  14. 0 48
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/experience/MemberExperienceLogExcelVO.java
  15. 0 34
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/experience/MemberExperienceLogExportReqVO.java
  16. 0 3
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/experience/MemberExperienceLogPageReqVO.java
  17. 0 3
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/experience/MemberExperienceLogRespVO.java
  18. 0 2
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelBaseVO.java
  19. 0 4
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelCreateReqVO.java
  20. 0 3
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelPageReqVO.java
  21. 0 3
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelRespVO.java
  22. 1 4
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelSimpleRespVO.java
  23. 0 3
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelUpdateReqVO.java
  24. 0 2
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/log/MemberLevelLogBaseVO.java
  25. 0 46
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/log/MemberLevelLogExcelVO.java
  26. 0 28
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/log/MemberLevelLogExportReqVO.java
  27. 0 3
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/log/MemberLevelLogPageReqVO.java
  28. 0 3
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/log/MemberLevelLogRespVO.java
  29. 19 8
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/MemberUserController.java
  30. 3 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserBaseVO.java
  31. 4 1
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserPageReqVO.java
  32. 3 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserRespVO.java
  33. 35 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/group/MemberGroupConvert.java
  34. 0 3
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/level/MemberExperienceLogConvert.java
  35. 0 3
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/level/MemberLevelLogConvert.java
  36. 7 1
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/user/MemberUserConvert.java
  37. 45 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/group/MemberGroupDO.java
  38. 8 1
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/MemberUserDO.java
  39. 31 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/group/MemberGroupMapper.java
  40. 0 13
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/level/MemberExperienceLogMapper.java
  41. 0 11
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/level/MemberLevelLogMapper.java
  42. 15 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java
  43. 86 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/group/MemberGroupService.java
  44. 102 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/group/MemberGroupServiceImpl.java
  45. 0 16
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberExperienceLogService.java
  46. 0 22
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberExperienceLogServiceImpl.java
  47. 0 9
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelLogService.java
  48. 0 6
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelLogServiceImpl.java
  49. 10 1
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelServiceImpl.java
  50. 13 2
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/tag/MemberTagServiceImpl.java
  51. 160 0
      yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/group/MemberGroupServiceImplTest.java
  52. 2 1
      yudao-module-member/yudao-module-member-biz/src/test/resources/sql/clean.sql
  53. 42 16
      yudao-module-member/yudao-module-member-biz/src/test/resources/sql/create_tables.sql

+ 36 - 0
sql/mysql/member_group.sql

@@ -0,0 +1,36 @@
+create table member_group
+(
+    id          bigint auto_increment comment '编号' primary key,
+    name        varchar(30)  default ''                not null comment '名称',
+    remark      varchar(255) default ''                not null comment '备注',
+    status      tinyint      default 0                 not null comment '状态',
+    creator     varchar(64)  default ''                null comment '创建者',
+    create_time datetime     default CURRENT_TIMESTAMP not null comment '创建时间',
+    updater     varchar(64)  default ''                null comment '更新者',
+    update_time datetime     default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
+    deleted     bit          default b'0'              not null comment '是否删除',
+    tenant_id   bigint       default 0                 not null comment '租户编号'
+)
+    comment '用户分组';
+
+alter table member_user add column group_id bigint null comment '用户分组编号';
+
+-- 菜单 SQL
+INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status, component_name)
+VALUES ('用户分组', '', 2, 5, 2262, 'group', '', 'member/group/index', 0, 'MemberGroup');
+
+-- 按钮父菜单ID
+-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
+SELECT @parentId := LAST_INSERT_ID();
+
+-- 按钮 SQL
+INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
+VALUES ('用户分组查询', 'member:group:query', 3, 1, @parentId, '', '', '', 0);
+INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
+VALUES ('用户分组创建', 'member:group:create', 3, 2, @parentId, '', '', '', 0);
+INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
+VALUES ('用户分组更新', 'member:group:update', 3, 3, @parentId, '', '', '', 0);
+INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
+VALUES ('用户分组删除', 'member:group:delete', 3, 4, @parentId, '', '', '', 0);
+INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
+VALUES ('用户分组导出', 'member:group:export', 3, 5, @parentId, '', '', '', 0);

+ 19 - 12
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java

@@ -24,9 +24,10 @@ public interface ErrorCodeConstants {
     // ========== 用户收件地址 1004004000 ==========
     ErrorCode ADDRESS_NOT_EXISTS = new ErrorCode(1004004000, "用户收件地址不存在");
 
-    //========== 会员标签 1004006000 ==========
-    ErrorCode TAG_NOT_EXISTS = new ErrorCode(1004006000, "会员标签不存在");
-    ErrorCode TAG_NAME_EXISTS = new ErrorCode(1004006001, "会员标签已经存在");
+    //========== 用户标签 1004006000 ==========
+    ErrorCode TAG_NOT_EXISTS = new ErrorCode(1004006000, "用户标签不存在");
+    ErrorCode TAG_NAME_EXISTS = new ErrorCode(1004006001, "用户标签已经存在");
+    ErrorCode TAG_HAS_USER = new ErrorCode(1004006002, "用户标签下存在用户,无法删除");
 
     //========== 积分配置 1004007000 ==========
 
@@ -39,14 +40,20 @@ public interface ErrorCodeConstants {
     //========== 签到配置 1004010000 ==========
 
 
-    //========== 会员等级 1004007000 ==========
-    ErrorCode LEVEL_NOT_EXISTS = new ErrorCode(1004007000, "会员等级不存在");
-    ErrorCode LEVEL_NAME_EXISTS = new ErrorCode(1004007001, "会员等级名称[{}]已被使用");
-    ErrorCode LEVEL_VALUE_EXISTS = new ErrorCode(1004007002, "会员等级值[{}]已被[{}]使用");
-    ErrorCode LEVEL_EXPERIENCE_MIN = new ErrorCode(1004007003, "升级经验必须大于上一个等级[{}]设置的升级经验[{}]");
-    ErrorCode LEVEL_EXPERIENCE_MAX = new ErrorCode(1004007004, "升级经验必须小于下一个等级[{}]设置的升级经验[{}]");
+    //========== 用户等级 1004011000 ==========
+    ErrorCode LEVEL_NOT_EXISTS = new ErrorCode(1004011000, "用户等级不存在");
+    ErrorCode LEVEL_NAME_EXISTS = new ErrorCode(1004011001, "用户等级名称[{}]已被使用");
+    ErrorCode LEVEL_VALUE_EXISTS = new ErrorCode(1004011002, "用户等级值[{}]已被[{}]使用");
+    ErrorCode LEVEL_EXPERIENCE_MIN = new ErrorCode(1004011003, "升级经验必须大于上一个等级[{}]设置的升级经验[{}]");
+    ErrorCode LEVEL_EXPERIENCE_MAX = new ErrorCode(1004011004, "升级经验必须小于下一个等级[{}]设置的升级经验[{}]");
+    ErrorCode LEVEL_HAS_USER = new ErrorCode(1004011005, "用户等级下存在用户,无法删除");
+
+    ErrorCode LEVEL_LOG_NOT_EXISTS = new ErrorCode(1004011100, "用户等级记录不存在");
+    ErrorCode EXPERIENCE_LOG_NOT_EXISTS = new ErrorCode(1004011200, "用户经验记录不存在");
+    ErrorCode LEVEL_REASON_NOT_EXISTS = new ErrorCode(1004011300, "用户等级调整原因不能为空");
+
+    //========== 用户分组 1004012000 ==========
+    ErrorCode GROUP_NOT_EXISTS = new ErrorCode(1004012000, "用户分组不存在");
+    ErrorCode GROUP_HAS_USER = new ErrorCode(1004012001, "用户分组下存在用户,无法删除");
 
-    ErrorCode LEVEL_LOG_NOT_EXISTS = new ErrorCode(1004007100, "会员等级记录不存在");
-    ErrorCode EXPERIENCE_LOG_NOT_EXISTS = new ErrorCode(1004007200, "会员经验记录不存在");
-    ErrorCode LEVEL_REASON_NOT_EXISTS = new ErrorCode(1004007300, "会员等级调整原因不能为空");
 }

+ 81 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/MemberGroupController.java

@@ -0,0 +1,81 @@
+package cn.iocoder.yudao.module.member.controller.admin.group;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.member.controller.admin.group.vo.*;
+import cn.iocoder.yudao.module.member.convert.group.MemberGroupConvert;
+import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO;
+import cn.iocoder.yudao.module.member.service.group.MemberGroupService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+
+@Tag(name = "管理后台 - 用户分组")
+@RestController
+@RequestMapping("/member/group")
+@Validated
+public class MemberGroupController {
+
+    @Resource
+    private MemberGroupService groupService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建用户分组")
+    @PreAuthorize("@ss.hasPermission('member:group:create')")
+    public CommonResult<Long> createGroup(@Valid @RequestBody MemberGroupCreateReqVO createReqVO) {
+        return success(groupService.createGroup(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新用户分组")
+    @PreAuthorize("@ss.hasPermission('member:group:update')")
+    public CommonResult<Boolean> updateGroup(@Valid @RequestBody MemberGroupUpdateReqVO updateReqVO) {
+        groupService.updateGroup(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除用户分组")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('member:group:delete')")
+    public CommonResult<Boolean> deleteGroup(@RequestParam("id") Long id) {
+        groupService.deleteGroup(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得用户分组")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('member:group:query')")
+    public CommonResult<MemberGroupRespVO> getGroup(@RequestParam("id") Long id) {
+        MemberGroupDO group = groupService.getGroup(id);
+        return success(MemberGroupConvert.INSTANCE.convert(group));
+    }
+
+    @GetMapping("/list-all-simple")
+    @Operation(summary = "获取会员分组精简信息列表", description = "只包含被开启的会员分组,主要用于前端的下拉选项")
+    public CommonResult<List<MemberGroupSimpleRespVO>> getSimpleGroupList() {
+        // 获用户列表,只要开启状态的
+        List<MemberGroupDO> list = groupService.getEnableGroupList();
+        return success(MemberGroupConvert.INSTANCE.convertSimpleList(list));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得用户分组分页")
+    @PreAuthorize("@ss.hasPermission('member:group:query')")
+    public CommonResult<PageResult<MemberGroupRespVO>> getGroupPage(@Valid MemberGroupPageReqVO pageVO) {
+        PageResult<MemberGroupDO> pageResult = groupService.getGroupPage(pageVO);
+        return success(MemberGroupConvert.INSTANCE.convertPage(pageResult));
+    }
+
+}

+ 29 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupBaseVO.java

@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.member.controller.admin.group.vo;
+
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * 用户分组 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class MemberGroupBaseVO {
+
+    @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "购物达人")
+    @NotNull(message = "名称不能为空")
+    private String name;
+
+    @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "你猜")
+    private String remark;
+
+    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "状态不能为空")
+    @InEnum(CommonStatusEnum.class)
+    private Integer status;
+
+}

+ 14 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupCreateReqVO.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.member.controller.admin.group.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@Schema(description = "管理后台 - 用户分组创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class MemberGroupCreateReqVO extends MemberGroupBaseVO {
+
+}

+ 30 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupPageReqVO.java

@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.member.controller.admin.group.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 用户分组分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class MemberGroupPageReqVO extends PageParam {
+
+    @Schema(description = "名称", example = "购物达人")
+    private String name;
+
+    @Schema(description = "状态", example = "1")
+    private Integer status;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 22 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupRespVO.java

@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.member.controller.admin.group.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 用户分组 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class MemberGroupRespVO extends MemberGroupBaseVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20357")
+    private Long id;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}

+ 18 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupSimpleRespVO.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.member.controller.admin.group.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.ToString;
+
+@Schema(description = "管理后台 - 用户分组 Response VO")
+@Data
+@ToString(callSuper = true)
+public class MemberGroupSimpleRespVO {
+
+    @Schema(description = "编号", example = "6103")
+    private Long id;
+
+    @Schema(description = "等级名称", example = "芋艿")
+    private String name;
+
+}

+ 20 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupUpdateReqVO.java

@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.member.controller.admin.group.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 用户分组更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class MemberGroupUpdateReqVO extends MemberGroupBaseVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20357")
+    @NotNull(message = "编号不能为空")
+    private Long id;
+
+}

+ 4 - 43
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/MemberExperienceLogController.java

@@ -2,10 +2,6 @@ package cn.iocoder.yudao.module.member.controller.admin.level;
 
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
-import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
-import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogExcelVO;
-import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogExportReqVO;
 import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogPageReqVO;
 import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogRespVO;
 import cn.iocoder.yudao.module.member.convert.level.MemberExperienceLogConvert;
@@ -16,17 +12,15 @@ import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.GetMapping;
+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 javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
 
 // TODO @疯狂:要不 Log 改成 Record,和 PointRecord 保持一致
 @Tag(name = "管理后台 - 会员经验记录")
@@ -38,16 +32,6 @@ public class MemberExperienceLogController {
     @Resource
     private MemberExperienceLogService experienceLogService;
 
-    // TODO @疯狂:不允许删除经验哈
-    @DeleteMapping("/delete")
-    @Operation(summary = "删除会员经验记录")
-    @Parameter(name = "id", description = "编号", required = true)
-    @PreAuthorize("@ss.hasPermission('member:experience-log:delete')")
-    public CommonResult<Boolean> deleteExperienceLog(@RequestParam("id") Long id) {
-        experienceLogService.deleteExperienceLog(id);
-        return success(true);
-    }
-
     @GetMapping("/get")
     @Operation(summary = "获得会员经验记录")
     @Parameter(name = "id", description = "编号", required = true, example = "1024")
@@ -57,16 +41,6 @@ public class MemberExperienceLogController {
         return success(MemberExperienceLogConvert.INSTANCE.convert(experienceLog));
     }
 
-    // TODO @疯狂:这个接口可以删除哈,应该用不到
-    @GetMapping("/list")
-    @Operation(summary = "获得会员经验记录列表")
-    @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
-    @PreAuthorize("@ss.hasPermission('member:experience-log:query')")
-    public CommonResult<List<MemberExperienceLogRespVO>> getExperienceLogList(@RequestParam("ids") Collection<Long> ids) {
-        List<MemberExperienceLogDO> list = experienceLogService.getExperienceLogList(ids);
-        return success(MemberExperienceLogConvert.INSTANCE.convertList(list));
-    }
-
     @GetMapping("/page")
     @Operation(summary = "获得会员经验记录分页")
     @PreAuthorize("@ss.hasPermission('member:experience-log:query')")
@@ -75,17 +49,4 @@ public class MemberExperienceLogController {
         return success(MemberExperienceLogConvert.INSTANCE.convertPage(pageResult));
     }
 
-    // TODO @疯狂:导出可以先不支持,场景不多
-    @GetMapping("/export-excel")
-    @Operation(summary = "导出会员经验记录 Excel")
-    @PreAuthorize("@ss.hasPermission('member:experience-log:export')")
-    @OperateLog(type = EXPORT)
-    public void exportExperienceLogExcel(@Valid MemberExperienceLogExportReqVO exportReqVO,
-                                         HttpServletResponse response) throws IOException {
-        List<MemberExperienceLogDO> list = experienceLogService.getExperienceLogList(exportReqVO);
-        // 导出 Excel
-        List<MemberExperienceLogExcelVO> datas = MemberExperienceLogConvert.INSTANCE.convertList02(list);
-        ExcelUtils.write(response, "会员经验记录.xls", "数据", MemberExperienceLogExcelVO.class, datas);
-    }
-
 }

+ 0 - 11
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/MemberLevelController.java

@@ -15,7 +15,6 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
 import javax.validation.Valid;
-import java.util.Collection;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -62,16 +61,6 @@ public class MemberLevelController {
         return success(MemberLevelConvert.INSTANCE.convert(level));
     }
 
-    // TODO @疯狂:这个应该用不到哈
-    @GetMapping("/list")
-    @Operation(summary = "获得会员等级列表")
-    @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
-    @PreAuthorize("@ss.hasPermission('member:level:query')")
-    public CommonResult<List<MemberLevelRespVO>> getLevelList(@RequestParam("ids") Collection<Long> ids) {
-        List<MemberLevelDO> list = levelService.getLevelList(ids);
-        return success(MemberLevelConvert.INSTANCE.convertList(list));
-    }
-
     @GetMapping("/list-all-simple")
     @Operation(summary = "获取会员等级精简信息列表", description = "只包含被开启的会员等级,主要用于前端的下拉选项")
     public CommonResult<List<MemberLevelSimpleRespVO>> getSimpleLevelList() {

+ 4 - 44
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/MemberLevelLogController.java

@@ -2,10 +2,6 @@ package cn.iocoder.yudao.module.member.controller.admin.level;
 
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
-import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
-import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogExcelVO;
-import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogExportReqVO;
 import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogPageReqVO;
 import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogRespVO;
 import cn.iocoder.yudao.module.member.convert.level.MemberLevelLogConvert;
@@ -16,17 +12,15 @@ import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.GetMapping;
+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 javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
 
 // TODO @疯狂:是不是不用这个 controller;因为日志只是为了记录,db 可以查询、和审计即可,目前暂时不需要开放出来;
 @Tag(name = "管理后台 - 会员等级记录")
@@ -38,16 +32,6 @@ public class MemberLevelLogController {
     @Resource
     private MemberLevelLogService levelLogService;
 
-    // TODO @疯狂:这个不允许删除哈
-    @DeleteMapping("/delete")
-    @Operation(summary = "删除会员等级记录")
-    @Parameter(name = "id", description = "编号", required = true)
-    @PreAuthorize("@ss.hasPermission('member:level-log:delete')")
-    public CommonResult<Boolean> deleteLevelLog(@RequestParam("id") Long id) {
-        levelLogService.deleteLevelLog(id);
-        return success(true);
-    }
-
     @GetMapping("/get")
     @Operation(summary = "获得会员等级记录")
     @Parameter(name = "id", description = "编号", required = true, example = "1024")
@@ -57,16 +41,6 @@ public class MemberLevelLogController {
         return success(MemberLevelLogConvert.INSTANCE.convert(levelLog));
     }
 
-    // TODO @疯狂:这个接口,应该没用
-    @GetMapping("/list")
-    @Operation(summary = "获得会员等级记录列表")
-    @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
-    @PreAuthorize("@ss.hasPermission('member:level-log:query')")
-    public CommonResult<List<MemberLevelLogRespVO>> getLevelLogList(@RequestParam("ids") Collection<Long> ids) {
-        List<MemberLevelLogDO> list = levelLogService.getLevelLogList(ids);
-        return success(MemberLevelLogConvert.INSTANCE.convertList(list));
-    }
-
     @GetMapping("/page")
     @Operation(summary = "获得会员等级记录分页")
     @PreAuthorize("@ss.hasPermission('member:level-log:query')")
@@ -74,18 +48,4 @@ public class MemberLevelLogController {
         PageResult<MemberLevelLogDO> pageResult = levelLogService.getLevelLogPage(pageVO);
         return success(MemberLevelLogConvert.INSTANCE.convertPage(pageResult));
     }
-
-    // TODO @疯狂:导出可以去掉先
-    @GetMapping("/export-excel")
-    @Operation(summary = "导出会员等级记录 Excel")
-    @PreAuthorize("@ss.hasPermission('member:level-log:export')")
-    @OperateLog(type = EXPORT)
-    public void exportLevelLogExcel(@Valid MemberLevelLogExportReqVO exportReqVO,
-                                    HttpServletResponse response) throws IOException {
-        List<MemberLevelLogDO> list = levelLogService.getLevelLogList(exportReqVO);
-        // 导出 Excel
-        List<MemberLevelLogExcelVO> datas = MemberLevelLogConvert.INSTANCE.convertList02(list);
-        ExcelUtils.write(response, "会员等级记录.xls", "数据", MemberLevelLogExcelVO.class, datas);
-    }
-
 }

+ 0 - 2
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/experience/MemberExperienceLogBaseVO.java

@@ -8,8 +8,6 @@ import javax.validation.constraints.NotNull;
 /**
  * 会员经验记录 Base VO,提供给添加、修改、详细的子 VO 使用
  * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
- *
- * @author owen
  */
 @Data
 public class MemberExperienceLogBaseVO {

+ 0 - 48
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/experience/MemberExperienceLogExcelVO.java

@@ -1,48 +0,0 @@
-package cn.iocoder.yudao.module.member.controller.admin.level.vo.experience;
-
-import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
-import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
-import cn.iocoder.yudao.module.member.enums.DictTypeConstants;
-import com.alibaba.excel.annotation.ExcelProperty;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-
-
-/**
- * 会员经验记录 Excel VO
- *
- * @author owen
- */
-@Data
-public class MemberExperienceLogExcelVO {
-
-    @ExcelProperty("编号")
-    private Long id;
-
-    @ExcelProperty("用户编号")
-    private Long userId;
-
-    @ExcelProperty(value = "业务类型", converter = DictConvert.class)
-    @DictFormat(DictTypeConstants.MEMBER_EXPERIENCE_BIZ_TYPE)
-    private Integer bizType;
-
-    @ExcelProperty("业务编号")
-    private String bizId;
-
-    @ExcelProperty("标题")
-    private String title;
-
-    @ExcelProperty("经验")
-    private Integer experience;
-
-    @ExcelProperty("变更后的经验")
-    private Integer totalExperience;
-
-    @ExcelProperty("描述")
-    private String description;
-
-    @ExcelProperty("创建时间")
-    private LocalDateTime createTime;
-
-}

+ 0 - 34
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/experience/MemberExperienceLogExportReqVO.java

@@ -1,34 +0,0 @@
-package cn.iocoder.yudao.module.member.controller.admin.level.vo.experience;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.time.LocalDateTime;
-
-import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
-
-/**
- * @author owen
- */
-@Schema(description = "管理后台 - 会员经验记录 Excel 导出 Request VO,参数和 MemberExperienceLogPageReqVO 是一致的")
-@Data
-public class MemberExperienceLogExportReqVO {
-
-    @Schema(description = "用户编号", example = "3638")
-    private Long userId;
-
-    @Schema(description = "业务类型", example = "1")
-    private Integer bizType;
-
-    @Schema(description = "业务编号", example = "12164")
-    private String bizId;
-
-    @Schema(description = "标题", example = "增加经验")
-    private String title;
-
-    @Schema(description = "创建时间")
-    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
-    private LocalDateTime[] createTime;
-
-}

+ 0 - 3
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/experience/MemberExperienceLogPageReqVO.java

@@ -11,9 +11,6 @@ import java.time.LocalDateTime;
 
 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
 
-/**
- * @author owen
- */
 @Schema(description = "管理后台 - 会员经验记录分页 Request VO")
 @Data
 @EqualsAndHashCode(callSuper = true)

+ 0 - 3
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/experience/MemberExperienceLogRespVO.java

@@ -7,9 +7,6 @@ import lombok.ToString;
 
 import java.time.LocalDateTime;
 
-/**
- * @author owen
- */
 @Schema(description = "管理后台 - 会员经验记录 Response VO")
 @Data
 @EqualsAndHashCode(callSuper = true)

+ 0 - 2
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelBaseVO.java

@@ -14,8 +14,6 @@ import javax.validation.constraints.Positive;
 /**
  * 会员等级 Base VO,提供给添加、修改、详细的子 VO 使用
  * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
- *
- * @author owen
  */
 @Data
 public class MemberLevelBaseVO {

+ 0 - 4
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelCreateReqVO.java

@@ -5,10 +5,6 @@ import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.ToString;
 
-// TODO @疯狂:项目的 vo 和 controller 不写 author 信息哈,只写 swagger 注解
-/**
- * @author owen
- */
 @Schema(description = "管理后台 - 会员等级创建 Request VO")
 @Data
 @EqualsAndHashCode(callSuper = true)

+ 0 - 3
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelPageReqVO.java

@@ -6,9 +6,6 @@ import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.ToString;
 
-/**
- * @author owen
- */
 @Schema(description = "管理后台 - 会员等级分页 Request VO")
 @Data
 @EqualsAndHashCode(callSuper = true)

+ 0 - 3
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelRespVO.java

@@ -7,9 +7,6 @@ import lombok.ToString;
 
 import java.time.LocalDateTime;
 
-/**
- * @author owen
- */
 @Schema(description = "管理后台 - 会员等级 Response VO")
 @Data
 @EqualsAndHashCode(callSuper = true)

+ 1 - 4
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelSimpleRespVO.java

@@ -2,15 +2,12 @@ package cn.iocoder.yudao.module.member.controller.admin.level.vo.level;
 
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
-import lombok.EqualsAndHashCode;
 import lombok.ToString;
 
-// TODO @疯狂:不需要继承 MemberLevelBaseVO
 @Schema(description = "管理后台 - 会员等级 Response VO")
 @Data
-@EqualsAndHashCode(callSuper = true)
 @ToString(callSuper = true)
-public class MemberLevelSimpleRespVO extends MemberLevelBaseVO {
+public class MemberLevelSimpleRespVO {
 
     @Schema(description = "编号", example = "6103")
     private Long id;

+ 0 - 3
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelUpdateReqVO.java

@@ -7,9 +7,6 @@ import lombok.ToString;
 
 import javax.validation.constraints.NotNull;
 
-/**
- * @author owen
- */
 @Schema(description = "管理后台 - 会员等级更新 Request VO")
 @Data
 @EqualsAndHashCode(callSuper = true)

+ 0 - 2
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/log/MemberLevelLogBaseVO.java

@@ -8,8 +8,6 @@ import javax.validation.constraints.NotNull;
 /**
  * 会员等级记录 Base VO,提供给添加、修改、详细的子 VO 使用
  * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
- *
- * @author owen
  */
 @Data
 public class MemberLevelLogBaseVO {

+ 0 - 46
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/log/MemberLevelLogExcelVO.java

@@ -1,46 +0,0 @@
-package cn.iocoder.yudao.module.member.controller.admin.level.vo.log;
-
-import com.alibaba.excel.annotation.ExcelProperty;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-
-/**
- * 会员等级记录 Excel VO
- *
- * @author owen
- */
-@Data
-public class MemberLevelLogExcelVO {
-
-    @ExcelProperty("编号")
-    private Long id;
-
-    @ExcelProperty("用户编号")
-    private Long userId;
-
-    @ExcelProperty("等级编号")
-    private Long levelId;
-
-    @ExcelProperty("会员等级")
-    private Integer level;
-
-    @ExcelProperty("享受折扣")
-    private Integer discount;
-
-    @ExcelProperty("升级经验")
-    private Integer experience;
-
-    @ExcelProperty("会员此时的经验")
-    private Integer userExperience;
-
-    @ExcelProperty("备注")
-    private String remark;
-
-    @ExcelProperty("描述")
-    private String description;
-
-    @ExcelProperty("创建时间")
-    private LocalDateTime createTime;
-
-}

+ 0 - 28
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/log/MemberLevelLogExportReqVO.java

@@ -1,28 +0,0 @@
-package cn.iocoder.yudao.module.member.controller.admin.level.vo.log;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.time.LocalDateTime;
-
-import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
-
-/**
- * @author owen
- */
-@Schema(description = "管理后台 - 会员等级记录 Excel 导出 Request VO,参数和 MemberLevelLogPageReqVO 是一致的")
-@Data
-public class MemberLevelLogExportReqVO {
-
-    @Schema(description = "用户编号", example = "25923")
-    private Long userId;
-
-    @Schema(description = "等级编号", example = "25985")
-    private Long levelId;
-
-    @Schema(description = "创建时间")
-    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
-    private LocalDateTime[] createTime;
-
-}

+ 0 - 3
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/log/MemberLevelLogPageReqVO.java

@@ -11,9 +11,6 @@ import java.time.LocalDateTime;
 
 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
 
-/**
- * @author owen
- */
 @Schema(description = "管理后台 - 会员等级记录分页 Request VO")
 @Data
 @EqualsAndHashCode(callSuper = true)

+ 0 - 3
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/log/MemberLevelLogRespVO.java

@@ -7,9 +7,6 @@ import lombok.ToString;
 
 import java.time.LocalDateTime;
 
-/**
- * @author owen
- */
 @Schema(description = "管理后台 - 会员等级记录 Response VO")
 @Data
 @EqualsAndHashCode(callSuper = true)

+ 19 - 8
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/MemberUserController.java

@@ -7,9 +7,11 @@ import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReq
 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserRespVO;
 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO;
 import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert;
+import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO;
 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO;
 import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO;
 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
+import cn.iocoder.yudao.module.member.service.group.MemberGroupService;
 import cn.iocoder.yudao.module.member.service.level.MemberLevelService;
 import cn.iocoder.yudao.module.member.service.tag.MemberTagService;
 import cn.iocoder.yudao.module.member.service.user.MemberUserService;
@@ -22,10 +24,7 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
 import javax.validation.Valid;
-import java.util.Collection;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
+import java.util.*;
 import java.util.stream.Collectors;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -42,6 +41,8 @@ public class MemberUserController {
     private MemberTagService memberTagService;
     @Resource
     private MemberLevelService memberLevelService;
+    @Resource
+    private MemberGroupService memberGroupService;
 
     @PutMapping("/update")
     @Operation(summary = "更新会员用户")
@@ -69,17 +70,27 @@ public class MemberUserController {
             return success(PageResult.empty());
         }
 
-        // 处理会员标签返显
+        Set<Long> groupIds = new HashSet<>(pageResult.getList().size());
+
+        // 处理用户标签返显
         Set<Long> tagIds = pageResult.getList().stream()
+                .peek(m -> {
+                    if (m.getGroupId() != null) {
+                        groupIds.add(m.getGroupId());
+                    }
+                })
                 .map(MemberUserDO::getTagIds)
                 .filter(Objects::nonNull)
                 .flatMap(Collection::stream)
                 .collect(Collectors.toSet());
         List<MemberTagDO> tags = memberTagService.getTagList(tagIds);
-        // 处理会员级别返显
+
+        // 处理用户级别返显
         List<MemberLevelDO> levels = memberLevelService.getEnableLevelList();
-        // 拼接
-        return success(MemberUserConvert.INSTANCE.convertPage(pageResult, tags, levels));
+        // 处理用户分组返显
+        List<MemberGroupDO> groups = memberGroupService.getGroupList(groupIds);
+
+        return success(MemberUserConvert.INSTANCE.convertPage(pageResult, tags, levels, groups));
     }
 
 }

+ 3 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserBaseVO.java

@@ -56,4 +56,7 @@ public class MemberUserBaseVO {
     @Schema(description = "会员等级编号", example = "1")
     private Long levelId;
 
+    @Schema(description = "用户分组编号", example = "1")
+    private Long groupId;
+
 }

+ 4 - 1
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserPageReqVO.java

@@ -35,7 +35,10 @@ public class MemberUserPageReqVO extends PageParam {
     @Schema(description = "会员标签编号列表", example = "[1, 2]")
     private List<Long> tagIds;
 
-    @Schema(description = "会员等级号", example = "1")
+    @Schema(description = "会员等级号", example = "1")
     private Long levelId;
 
+    @Schema(description = "用户分组编号", example = "1")
+    private Long groupId;
+
 }

+ 3 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserRespVO.java

@@ -38,4 +38,7 @@ public class MemberUserRespVO extends MemberUserBaseVO {
     @Schema(description = "会员等级", example = "黄金会员")
     private String levelName;
 
+    @Schema(description = "用户分组", example = "购物达人")
+    private String groupName;
+
 }

+ 35 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/group/MemberGroupConvert.java

@@ -0,0 +1,35 @@
+package cn.iocoder.yudao.module.member.convert.group;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupCreateReqVO;
+import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupRespVO;
+import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupSimpleRespVO;
+import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupUpdateReqVO;
+import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+/**
+ * 用户分组 Convert
+ *
+ * @author owen
+ */
+@Mapper
+public interface MemberGroupConvert {
+
+    MemberGroupConvert INSTANCE = Mappers.getMapper(MemberGroupConvert.class);
+
+    MemberGroupDO convert(MemberGroupCreateReqVO bean);
+
+    MemberGroupDO convert(MemberGroupUpdateReqVO bean);
+
+    MemberGroupRespVO convert(MemberGroupDO bean);
+
+    List<MemberGroupRespVO> convertList(List<MemberGroupDO> list);
+
+    PageResult<MemberGroupRespVO> convertPage(PageResult<MemberGroupDO> page);
+
+    List<MemberGroupSimpleRespVO> convertSimpleList(List<MemberGroupDO> list);
+}

+ 0 - 3
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/level/MemberExperienceLogConvert.java

@@ -1,7 +1,6 @@
 package cn.iocoder.yudao.module.member.convert.level;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogExcelVO;
 import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogRespVO;
 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberExperienceLogDO;
 import org.mapstruct.Mapper;
@@ -25,6 +24,4 @@ public interface MemberExperienceLogConvert {
 
     PageResult<MemberExperienceLogRespVO> convertPage(PageResult<MemberExperienceLogDO> page);
 
-    List<MemberExperienceLogExcelVO> convertList02(List<MemberExperienceLogDO> list);
-
 }

+ 0 - 3
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/level/MemberLevelLogConvert.java

@@ -1,7 +1,6 @@
 package cn.iocoder.yudao.module.member.convert.level;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogExcelVO;
 import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogRespVO;
 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelLogDO;
 import org.mapstruct.Mapper;
@@ -25,6 +24,4 @@ public interface MemberLevelLogConvert {
 
     PageResult<MemberLevelLogRespVO> convertPage(PageResult<MemberLevelLogDO> page);
 
-    List<MemberLevelLogExcelVO> convertList02(List<MemberLevelLogDO> list);
-
 }

+ 7 - 1
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/user/MemberUserConvert.java

@@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserRespVO;
 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO;
 import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserInfoRespVO;
+import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO;
 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO;
 import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO;
 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
@@ -38,15 +39,20 @@ public interface MemberUserConvert {
 
     default PageResult<MemberUserRespVO> convertPage(PageResult<MemberUserDO> pageResult,
                                                      List<MemberTagDO> tags,
-                                                     List<MemberLevelDO> levels) {
+                                                     List<MemberLevelDO> levels,
+                                                     List<MemberGroupDO> groups) {
         PageResult<MemberUserRespVO> result = convertPage(pageResult);
 
         // 处理关联数据
         Map<Long, String> tagMap = convertMap(tags, MemberTagDO::getId, MemberTagDO::getName);
         Map<Long, String> levelMap = convertMap(levels, MemberLevelDO::getId, MemberLevelDO::getName);
+        Map<Long, String> groupMap = convertMap(groups, MemberGroupDO::getId, MemberGroupDO::getName);
+
+        // 填充关联数据
         for (MemberUserRespVO vo : result.getList()) {
             vo.setTagNames(convertList(vo.getTagIds(), tagMap::get));
             vo.setLevelName(MapUtil.getStr(levelMap, vo.getLevelId(), StrUtil.EMPTY));
+            vo.setGroupName(MapUtil.getStr(groupMap, vo.getGroupId(), StrUtil.EMPTY));
         }
         return result;
     }

+ 45 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/group/MemberGroupDO.java

@@ -0,0 +1,45 @@
+package cn.iocoder.yudao.module.member.dal.dataobject.group;
+
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+/**
+ * 用户分组 DO
+ *
+ * @author owen
+ */
+@TableName("member_group")
+@KeySequence("member_group_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class MemberGroupDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 名称
+     */
+    private String name;
+    /**
+     * 备注
+     */
+    private String remark;
+    /**
+     * 状态
+     * <p>
+     * 枚举 {@link CommonStatusEnum}
+     */
+    private Integer status;
+
+}

+ 8 - 1
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/MemberUserDO.java

@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.ip.core.Area;
 import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
 import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
+import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO;
 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO;
 import cn.iocoder.yudao.module.system.enums.common.SexEnum;
 import com.baomidou.mybatisplus.annotation.KeySequence;
@@ -121,12 +122,18 @@ public class MemberUserDO extends TenantBaseDO {
     /**
      * 会员级别编号
      *
-     * 关联 {@link MemberLevelDO#getLevel()} 字段
+     * 关联 {@link MemberLevelDO#getId()} 字段
      */
     private Long levelId;
     /**
      * 会员经验
      */
     private Integer experience;
+    /**
+     * 用户分组编号
+     *
+     * 关联 {@link MemberGroupDO#getId()} 字段
+     */
+    private Long groupId;
 
 }

+ 31 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/group/MemberGroupMapper.java

@@ -0,0 +1,31 @@
+package cn.iocoder.yudao.module.member.dal.mysql.group;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupPageReqVO;
+import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 用户分组 Mapper
+ *
+ * @author owen
+ */
+@Mapper
+public interface MemberGroupMapper extends BaseMapperX<MemberGroupDO> {
+
+    default PageResult<MemberGroupDO> selectPage(MemberGroupPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<MemberGroupDO>()
+                .likeIfPresent(MemberGroupDO::getName, reqVO.getName())
+                .eqIfPresent(MemberGroupDO::getStatus, reqVO.getStatus())
+                .betweenIfPresent(MemberGroupDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(MemberGroupDO::getId));
+    }
+
+    default List<MemberGroupDO> selectListByStatus(Integer status) {
+        return selectList(MemberGroupDO::getStatus, status);
+    }
+}

+ 0 - 13
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/level/MemberExperienceLogMapper.java

@@ -3,13 +3,10 @@ package cn.iocoder.yudao.module.member.dal.mysql.level;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
-import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogExportReqVO;
 import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogPageReqVO;
 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberExperienceLogDO;
 import org.apache.ibatis.annotations.Mapper;
 
-import java.util.List;
-
 /**
  * 会员经验记录 Mapper
  *
@@ -28,14 +25,4 @@ public interface MemberExperienceLogMapper extends BaseMapperX<MemberExperienceL
                 .orderByDesc(MemberExperienceLogDO::getId));
     }
 
-    default List<MemberExperienceLogDO> selectList(MemberExperienceLogExportReqVO reqVO) {
-        return selectList(new LambdaQueryWrapperX<MemberExperienceLogDO>()
-                .eqIfPresent(MemberExperienceLogDO::getUserId, reqVO.getUserId())
-                .eqIfPresent(MemberExperienceLogDO::getBizId, reqVO.getBizId())
-                .eqIfPresent(MemberExperienceLogDO::getBizType, reqVO.getBizType())
-                .eqIfPresent(MemberExperienceLogDO::getTitle, reqVO.getTitle())
-                .betweenIfPresent(MemberExperienceLogDO::getCreateTime, reqVO.getCreateTime())
-                .orderByDesc(MemberExperienceLogDO::getId));
-    }
-
 }

+ 0 - 11
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/level/MemberLevelLogMapper.java

@@ -3,13 +3,10 @@ package cn.iocoder.yudao.module.member.dal.mysql.level;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
-import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogExportReqVO;
 import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogPageReqVO;
 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelLogDO;
 import org.apache.ibatis.annotations.Mapper;
 
-import java.util.List;
-
 /**
  * 会员等级记录 Mapper
  *
@@ -26,12 +23,4 @@ public interface MemberLevelLogMapper extends BaseMapperX<MemberLevelLogDO> {
                 .orderByDesc(MemberLevelLogDO::getId));
     }
 
-    default List<MemberLevelLogDO> selectList(MemberLevelLogExportReqVO reqVO) {
-        return selectList(new LambdaQueryWrapperX<MemberLevelLogDO>()
-                .eqIfPresent(MemberLevelLogDO::getUserId, reqVO.getUserId())
-                .eqIfPresent(MemberLevelLogDO::getLevelId, reqVO.getLevelId())
-                .betweenIfPresent(MemberLevelLogDO::getCreateTime, reqVO.getCreateTime())
-                .orderByDesc(MemberLevelLogDO::getId));
-    }
-
 }

+ 15 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java

@@ -45,6 +45,7 @@ public interface MemberUserMapper extends BaseMapperX<MemberUserDO> {
                 .likeIfPresent(MemberUserDO::getNickname, reqVO.getNickname())
                 .betweenIfPresent(MemberUserDO::getCreateTime, reqVO.getCreateTime())
                 .eqIfPresent(MemberUserDO::getLevelId, reqVO.getLevelId())
+                .eqIfPresent(MemberUserDO::getGroupId, reqVO.getGroupId())
                 .apply(StrUtil.isNotEmpty(tagIdSql), tagIdSql)
                 .orderByDesc(MemberUserDO::getId));
     }
@@ -62,4 +63,18 @@ public interface MemberUserMapper extends BaseMapperX<MemberUserDO> {
                 .set(MemberUserDO::getExperience, 0)
                 .set(MemberUserDO::getLevelId, null));
     }
+
+    default Long selectCountByGroupId(Long groupId) {
+        return selectCount(MemberUserDO::getGroupId, groupId);
+    }
+
+    default Long selectCountByLevelId(Long levelId) {
+        return selectCount(MemberUserDO::getLevelId, levelId);
+    }
+
+    default Long selectCountByTagId(Long tagId) {
+        return selectCount(new LambdaQueryWrapperX<MemberUserDO>()
+                .apply("FIND_IN_SET({0}, tag_ids)", tagId));
+    }
+
 }

+ 86 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/group/MemberGroupService.java

@@ -0,0 +1,86 @@
+package cn.iocoder.yudao.module.member.service.group;
+
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupCreateReqVO;
+import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupPageReqVO;
+import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupUpdateReqVO;
+import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO;
+
+import javax.validation.Valid;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 用户分组 Service 接口
+ *
+ * @author owen
+ */
+public interface MemberGroupService {
+
+    /**
+     * 创建用户分组
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createGroup(@Valid MemberGroupCreateReqVO createReqVO);
+
+    /**
+     * 更新用户分组
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateGroup(@Valid MemberGroupUpdateReqVO updateReqVO);
+
+    /**
+     * 删除用户分组
+     *
+     * @param id 编号
+     */
+    void deleteGroup(Long id);
+
+    /**
+     * 获得用户分组
+     *
+     * @param id 编号
+     * @return 用户分组
+     */
+    MemberGroupDO getGroup(Long id);
+
+    /**
+     * 获得用户分组列表
+     *
+     * @param ids 编号
+     * @return 用户分组列表
+     */
+    List<MemberGroupDO> getGroupList(Collection<Long> ids);
+
+    /**
+     * 获得用户分组分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 用户分组分页
+     */
+    PageResult<MemberGroupDO> getGroupPage(MemberGroupPageReqVO pageReqVO);
+
+
+    /**
+     * 获得指定状态的用户分组列表
+     *
+     * @param status 状态
+     * @return 用户分组列表
+     */
+    List<MemberGroupDO> getGroupListByStatus(Integer status);
+
+
+    /**
+     * 获得开启状态的用户分组列表
+     *
+     * @return 用户分组列表
+     */
+    default List<MemberGroupDO> getEnableGroupList() {
+        return getGroupListByStatus(CommonStatusEnum.ENABLE.getStatus());
+    }
+
+}

+ 102 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/group/MemberGroupServiceImpl.java

@@ -0,0 +1,102 @@
+package cn.iocoder.yudao.module.member.service.group;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.ListUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupCreateReqVO;
+import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupPageReqVO;
+import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupUpdateReqVO;
+import cn.iocoder.yudao.module.member.convert.group.MemberGroupConvert;
+import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO;
+import cn.iocoder.yudao.module.member.dal.mysql.group.MemberGroupMapper;
+import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.Collection;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.GROUP_HAS_USER;
+import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.GROUP_NOT_EXISTS;
+
+/**
+ * 用户分组 Service 实现类
+ *
+ * @author owen
+ */
+@Service
+@Validated
+public class MemberGroupServiceImpl implements MemberGroupService {
+
+    @Resource
+    private MemberGroupMapper groupMapper;
+    @Resource
+    private MemberUserMapper memberUserMapper;
+
+    @Override
+    public Long createGroup(MemberGroupCreateReqVO createReqVO) {
+        // 插入
+        MemberGroupDO group = MemberGroupConvert.INSTANCE.convert(createReqVO);
+        groupMapper.insert(group);
+        // 返回
+        return group.getId();
+    }
+
+    @Override
+    public void updateGroup(MemberGroupUpdateReqVO updateReqVO) {
+        // 校验存在
+        validateGroupExists(updateReqVO.getId());
+        // 更新
+        MemberGroupDO updateObj = MemberGroupConvert.INSTANCE.convert(updateReqVO);
+        groupMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteGroup(Long id) {
+        // 校验存在
+        validateGroupExists(id);
+        // 校验分组下是否有用户
+        validateGroupHasUser(id);
+        // 删除
+        groupMapper.deleteById(id);
+    }
+
+    void validateGroupExists(Long id) {
+        if (groupMapper.selectById(id) == null) {
+            throw exception(GROUP_NOT_EXISTS);
+        }
+    }
+
+    void validateGroupHasUser(Long id) {
+        Long count = memberUserMapper.selectCountByGroupId(id);
+        if (count > 0) {
+            throw exception(GROUP_HAS_USER);
+        }
+    }
+
+    @Override
+    public MemberGroupDO getGroup(Long id) {
+        return groupMapper.selectById(id);
+    }
+
+    @Override
+    public List<MemberGroupDO> getGroupList(Collection<Long> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return ListUtil.empty();
+        }
+        return groupMapper.selectBatchIds(ids);
+    }
+
+    @Override
+    public PageResult<MemberGroupDO> getGroupPage(MemberGroupPageReqVO pageReqVO) {
+        return groupMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public List<MemberGroupDO> getGroupListByStatus(Integer status) {
+        return groupMapper.selectListByStatus(status);
+    }
+
+}

+ 0 - 16
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberExperienceLogService.java

@@ -1,7 +1,6 @@
 package cn.iocoder.yudao.module.member.service.level;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogExportReqVO;
 import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogPageReqVO;
 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberExperienceLogDO;
 import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;
@@ -16,13 +15,6 @@ import java.util.List;
  */
 public interface MemberExperienceLogService {
 
-    /**
-     * 删除会员经验记录
-     *
-     * @param id 编号
-     */
-    void deleteExperienceLog(Long id);
-
     /**
      * 获得会员经验记录
      *
@@ -47,14 +39,6 @@ public interface MemberExperienceLogService {
      */
     PageResult<MemberExperienceLogDO> getExperienceLogPage(MemberExperienceLogPageReqVO pageReqVO);
 
-    /**
-     * 获得会员经验记录列表, 用于 Excel 导出
-     *
-     * @param exportReqVO 查询条件
-     * @return 会员经验记录列表
-     */
-    List<MemberExperienceLogDO> getExperienceLogList(MemberExperienceLogExportReqVO exportReqVO);
-
     /**
      * 创建 手动调整 经验变动记录
      *

+ 0 - 22
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberExperienceLogServiceImpl.java

@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.member.service.level;
 
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogExportReqVO;
 import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogPageReqVO;
 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberExperienceLogDO;
 import cn.iocoder.yudao.module.member.dal.mysql.level.MemberExperienceLogMapper;
@@ -14,9 +13,6 @@ import javax.annotation.Resource;
 import java.util.Collection;
 import java.util.List;
 
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.EXPERIENCE_LOG_NOT_EXISTS;
-
 /**
  * 会员经验记录 Service 实现类
  *
@@ -29,19 +25,6 @@ public class MemberExperienceLogServiceImpl implements MemberExperienceLogServic
     @Resource
     private MemberExperienceLogMapper experienceLogMapper;
 
-    @Override
-    public void deleteExperienceLog(Long id) {
-        // 校验存在
-        validateExperienceLogExists(id);
-        // 删除
-        experienceLogMapper.deleteById(id);
-    }
-
-    private void validateExperienceLogExists(Long id) {
-        if (experienceLogMapper.selectById(id) == null) {
-            throw exception(EXPERIENCE_LOG_NOT_EXISTS);
-        }
-    }
 
     @Override
     public MemberExperienceLogDO getExperienceLog(Long id) {
@@ -58,11 +41,6 @@ public class MemberExperienceLogServiceImpl implements MemberExperienceLogServic
         return experienceLogMapper.selectPage(pageReqVO);
     }
 
-    @Override
-    public List<MemberExperienceLogDO> getExperienceLogList(MemberExperienceLogExportReqVO exportReqVO) {
-        return experienceLogMapper.selectList(exportReqVO);
-    }
-
     @Override
     public void createAdjustLog(Long userId, int experience, int totalExperience) {
         // 管理员调整时, 没有业务编号, 记录对应的枚举值

+ 0 - 9
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelLogService.java

@@ -1,7 +1,6 @@
 package cn.iocoder.yudao.module.member.service.level;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogExportReqVO;
 import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogPageReqVO;
 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO;
 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelLogDO;
@@ -48,14 +47,6 @@ public interface MemberLevelLogService {
      */
     PageResult<MemberLevelLogDO> getLevelLogPage(MemberLevelLogPageReqVO pageReqVO);
 
-    /**
-     * 获得会员等级记录列表, 用于 Excel 导出
-     *
-     * @param exportReqVO 查询条件
-     * @return 会员等级记录列表
-     */
-    List<MemberLevelLogDO> getLevelLogList(MemberLevelLogExportReqVO exportReqVO);
-
     /**
      * 创建记录: 取消等级
      *

+ 0 - 6
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelLogServiceImpl.java

@@ -1,7 +1,6 @@
 package cn.iocoder.yudao.module.member.service.level;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogExportReqVO;
 import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogPageReqVO;
 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO;
 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelLogDO;
@@ -58,11 +57,6 @@ public class MemberLevelLogServiceImpl implements MemberLevelLogService {
         return levelLogMapper.selectPage(pageReqVO);
     }
 
-    @Override
-    public List<MemberLevelLogDO> getLevelLogList(MemberLevelLogExportReqVO exportReqVO) {
-        return levelLogMapper.selectList(exportReqVO);
-    }
-
     @Override
     public void createCancelLog(Long userId, String reason) {
         MemberLevelLogDO levelLogDO = new MemberLevelLogDO();

+ 10 - 1
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelServiceImpl.java

@@ -74,9 +74,10 @@ public class MemberLevelServiceImpl implements MemberLevelService {
 
     @Override
     public void deleteLevel(Long id) {
-        // TODO @疯狂:校验是否有用户使用该等级
         // 校验存在
         validateLevelExists(id);
+        // 校验分组下是否有用户
+        validateLevelHasUser(id);
         // 删除
         levelMapper.deleteById(id);
     }
@@ -149,6 +150,14 @@ public class MemberLevelServiceImpl implements MemberLevelService {
         validateExperienceOutRange(list, id, level, experience);
     }
 
+    @VisibleForTesting
+    void validateLevelHasUser(Long id) {
+        Long count = memberUserMapper.selectCountByLevelId(id);
+        if (count > 0) {
+            throw exception(GROUP_HAS_USER);
+        }
+    }
+
     @Override
     public MemberLevelDO getLevel(Long id) {
         return levelMapper.selectById(id);

+ 13 - 2
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/tag/MemberTagServiceImpl.java

@@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.member.controller.admin.tag.vo.MemberTagUpdateReq
 import cn.iocoder.yudao.module.member.convert.tag.MemberTagConvert;
 import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO;
 import cn.iocoder.yudao.module.member.dal.mysql.tag.MemberTagMapper;
+import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
@@ -18,8 +19,7 @@ import java.util.Collection;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.TAG_NAME_EXISTS;
-import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.TAG_NOT_EXISTS;
+import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*;
 
 /**
  * 会员标签 Service 实现类
@@ -32,6 +32,8 @@ public class MemberTagServiceImpl implements MemberTagService {
 
     @Resource
     private MemberTagMapper tagMapper;
+    @Resource
+    private MemberUserMapper memberUserMapper;
 
     @Override
     public Long createTag(MemberTagCreateReqVO createReqVO) {
@@ -59,6 +61,8 @@ public class MemberTagServiceImpl implements MemberTagService {
     public void deleteTag(Long id) {
         // 校验存在
         validateTagExists(id);
+        // 校验标签下是否有用户
+        validateTagHasUser(id);
         // 删除
         tagMapper.deleteById(id);
     }
@@ -87,6 +91,13 @@ public class MemberTagServiceImpl implements MemberTagService {
         }
     }
 
+    void validateTagHasUser(Long id) {
+        Long count = memberUserMapper.selectCountByTagId(id);
+        if (count > 0) {
+            throw exception(TAG_HAS_USER);
+        }
+    }
+
     @Override
     public MemberTagDO getTag(Long id) {
         return tagMapper.selectById(id);

+ 160 - 0
yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/group/MemberGroupServiceImplTest.java

@@ -0,0 +1,160 @@
+package cn.iocoder.yudao.module.member.service.group;
+
+import cn.hutool.core.util.RandomUtil;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
+import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupCreateReqVO;
+import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupPageReqVO;
+import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupUpdateReqVO;
+import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO;
+import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
+import cn.iocoder.yudao.module.member.dal.mysql.group.MemberGroupMapper;
+import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
+import cn.iocoder.yudao.module.system.enums.common.SexEnum;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+
+import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
+import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
+import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.GROUP_HAS_USER;
+import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.GROUP_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link MemberGroupServiceImpl} 的单元测试类
+ *
+ * @author owen
+ */
+@Import(MemberGroupServiceImpl.class)
+public class MemberGroupServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private MemberGroupServiceImpl groupService;
+
+    @Resource
+    private MemberGroupMapper groupMapper;
+    @Resource
+    private MemberUserMapper memberUserMapper;
+
+    @Test
+    public void testCreateGroup_success() {
+        // 准备参数
+        MemberGroupCreateReqVO reqVO = randomPojo(MemberGroupCreateReqVO.class);
+
+        // 调用
+        Long groupId = groupService.createGroup(reqVO);
+        // 断言
+        assertNotNull(groupId);
+        // 校验记录的属性是否正确
+        MemberGroupDO group = groupMapper.selectById(groupId);
+        assertPojoEquals(reqVO, group);
+    }
+
+    @Test
+    public void testUpdateGroup_success() {
+        // mock 数据
+        MemberGroupDO dbGroup = randomPojo(MemberGroupDO.class);
+        groupMapper.insert(dbGroup);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        MemberGroupUpdateReqVO reqVO = randomPojo(MemberGroupUpdateReqVO.class, o -> {
+            o.setId(dbGroup.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        groupService.updateGroup(reqVO);
+        // 校验是否更新正确
+        MemberGroupDO group = groupMapper.selectById(reqVO.getId()); // 获取最新的
+        assertPojoEquals(reqVO, group);
+    }
+
+    @Test
+    public void testUpdateGroup_notExists() {
+        // 准备参数
+        MemberGroupUpdateReqVO reqVO = randomPojo(MemberGroupUpdateReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> groupService.updateGroup(reqVO), GROUP_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteGroup_success() {
+        // mock 数据
+        MemberGroupDO dbGroup = randomPojo(MemberGroupDO.class);
+        groupMapper.insert(dbGroup);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbGroup.getId();
+
+        // 调用
+        groupService.deleteGroup(id);
+        // 校验数据不存在了
+        assertNull(groupMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteGroup_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> groupService.deleteGroup(id), GROUP_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteGroup_hasUser() {
+        // mock 数据
+        MemberGroupDO dbGroup = randomPojo(MemberGroupDO.class);
+        groupMapper.insert(dbGroup);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbGroup.getId();
+
+        // mock 会员数据
+        MemberUserDO dbUser = randomPojo(MemberUserDO.class, o -> {
+            o.setGroupId(id);
+            o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex());
+        });
+        memberUserMapper.insert(dbUser);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> groupService.deleteGroup(id), GROUP_HAS_USER);
+    }
+
+    @Test
+    public void testGetGroupPage() {
+        String name = randomString();
+        int status = CommonStatusEnum.ENABLE.getStatus();
+
+        // mock 数据
+        MemberGroupDO dbGroup = randomPojo(MemberGroupDO.class, o -> { // 等会查询到
+            o.setName(name);
+            o.setStatus(status);
+            o.setCreateTime(buildTime(2023, 2, 18));
+        });
+        groupMapper.insert(dbGroup);
+        // 测试 name 不匹配
+        groupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setName("")));
+        // 测试 status 不匹配
+        groupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
+        // 测试 createTime 不匹配
+        groupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setCreateTime(null)));
+        // 准备参数
+        MemberGroupPageReqVO reqVO = new MemberGroupPageReqVO();
+        reqVO.setName(name);
+        reqVO.setStatus(status);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<MemberGroupDO> pageResult = groupService.getGroupPage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbGroup, pageResult.getList().get(0));
+    }
+
+}

+ 2 - 1
yudao-module-member/yudao-module-member-biz/src/test/resources/sql/clean.sql

@@ -1,4 +1,5 @@
 DELETE FROM "member_user";
 DELETE FROM "member_address";
 DELETE FROM "member_tag";
-DELETE FROM "member_level";
+DELETE FROM "member_level";
+DELETE FROM "member_group";

+ 42 - 16
yudao-module-member/yudao-module-member-biz/src/test/resources/sql/create_tables.sql

@@ -1,19 +1,30 @@
-CREATE TABLE IF NOT EXISTS "member_user"  (
-    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '编号',
-    "nickname" varchar(30)  NOT NULL DEFAULT '' COMMENT '用户昵称',
-    "avatar" varchar(255)  NOT NULL DEFAULT '' COMMENT '头像',
-    "status" tinyint NOT NULL COMMENT '状态',
-    "mobile" varchar(11)  NOT NULL COMMENT '手机号',
-    "password" varchar(100)  NOT NULL DEFAULT '' COMMENT '密码',
+CREATE TABLE IF NOT EXISTS "member_user"
+(
+    "id"          bigint       NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '编号',
+    "nickname"    varchar(30)  NOT NULL DEFAULT '' COMMENT '用户昵称',
+    "name"        varchar(30)  NULL COMMENT '真实名字',
+    sex           tinyint      null comment '性别',
+    birthday      datetime     null comment '出生日期',
+    area_id       int          null comment '所在地',
+    mark          varchar(255) null comment '用户备注',
+    point         int                   default 0 null comment '积分',
+    "avatar"      varchar(255) NOT NULL DEFAULT '' COMMENT '头像',
+    "status"      tinyint      NOT NULL COMMENT '状态',
+    "mobile"      varchar(11)  NOT NULL COMMENT '手机号',
+    "password"    varchar(100) NOT NULL DEFAULT '' COMMENT '密码',
     "register_ip" varchar(32)  NOT NULL COMMENT '注册 IP',
-    "login_ip" varchar(50) NULL DEFAULT '' COMMENT '最后登录IP',
-    "login_date" datetime NULL DEFAULT NULL COMMENT '最后登录时间',
-    "creator" varchar(64)  NULL DEFAULT '' COMMENT '创建者',
-    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-    "updater" varchar(64)  NULL DEFAULT '' COMMENT '更新者',
-    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-    "deleted" bit(1) NOT NULL DEFAULT '0' COMMENT '是否删除',
-    "tenant_id" bigint not null default  '0',
+    "login_ip"    varchar(50)  NULL     DEFAULT '' COMMENT '最后登录IP',
+    "login_date"  datetime     NULL     DEFAULT NULL COMMENT '最后登录时间',
+    "tag_ids"     varchar(255) NULL     DEFAULT NULL COMMENT '用户标签编号列表,以逗号分隔',
+    "level_id"    bigint       NULL     DEFAULT NULL COMMENT '等级编号',
+    "experience"  bigint       NULL     DEFAULT NULL COMMENT '经验',
+    "group_id"    bigint       NULL     DEFAULT NULL COMMENT '用户分组编号',
+    "creator"     varchar(64)  NULL     DEFAULT '' COMMENT '创建者',
+    "create_time" datetime     NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    "updater"     varchar(64)  NULL     DEFAULT '' COMMENT '更新者',
+    "update_time" datetime     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    "deleted"     bit(1)       NOT NULL DEFAULT '0' COMMENT '是否删除',
+    "tenant_id"   bigint       not null default '0',
     PRIMARY KEY ("id")
 ) COMMENT '会员表';
 
@@ -63,4 +74,19 @@ CREATE TABLE IF NOT EXISTS "member_level"
     "tenant_id"      bigint   not null default '0',
     "status"         int      NOT NULL,
     PRIMARY KEY ("id")
-) COMMENT '会员等级';
+) COMMENT '会员等级';
+
+CREATE TABLE IF NOT EXISTS "member_group"
+(
+    "id"          bigint   NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "name"        varchar  NOT NULL,
+    "remark"      varchar  NOT NULL,
+    "status"      varchar  NOT NULL,
+    "creator"     varchar           DEFAULT '',
+    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater"     varchar           DEFAULT '',
+    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    "deleted"     bit      NOT NULL DEFAULT FALSE,
+    "tenant_id"      bigint   not null default '0',
+    PRIMARY KEY ("id")
+) COMMENT '用户分组';