会员: 增加会员分组功能

This commit is contained in:
owen 2023-08-22 00:43:36 +08:00
parent de83531285
commit 8ae3401452
25 changed files with 807 additions and 29 deletions

View File

@ -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如果你是 OraclePostgreSQLSQLServer 的话需要手动修改 @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);

View File

@ -52,4 +52,9 @@ public interface ErrorCodeConstants {
ErrorCode LEVEL_LOG_NOT_EXISTS = new ErrorCode(1004007100, "会员等级记录不存在"); ErrorCode LEVEL_LOG_NOT_EXISTS = new ErrorCode(1004007100, "会员等级记录不存在");
ErrorCode EXPERIENCE_LOG_NOT_EXISTS = new ErrorCode(1004007200, "会员经验记录不存在"); ErrorCode EXPERIENCE_LOG_NOT_EXISTS = new ErrorCode(1004007200, "会员经验记录不存在");
ErrorCode LEVEL_REASON_NOT_EXISTS = new ErrorCode(1004007300, "会员等级调整原因不能为空"); ErrorCode LEVEL_REASON_NOT_EXISTS = new ErrorCode(1004007300, "会员等级调整原因不能为空");
//========== 用户分组 1004011000 ==========
ErrorCode GROUP_NOT_EXISTS = new ErrorCode(1004011000, "用户分组不存在");
ErrorCode GROUP_HAS_USER = new ErrorCode(1004011001, "用户分组下存在用户,无法删除");
} }

View File

@ -0,0 +1,91 @@
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.Collection;
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")
@Operation(summary = "获得用户分组列表")
@Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
@PreAuthorize("@ss.hasPermission('member:group:query')")
public CommonResult<List<MemberGroupRespVO>> getGroupList(@RequestParam("ids") Collection<Long> ids) {
List<MemberGroupDO> list = groupService.getGroupList(ids);
return success(MemberGroupConvert.INSTANCE.convertList(list));
}
@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));
}
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.member.controller.admin.group.vo;
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 = "状态不能为空")
private Integer status;
}

View File

@ -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 {
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.member.controller.admin.level.vo;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
/** /**
@ -10,9 +9,8 @@ import lombok.ToString;
*/ */
@Schema(description = "管理后台 - 会员等级 Response VO") @Schema(description = "管理后台 - 会员等级 Response VO")
@Data @Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true) @ToString(callSuper = true)
public class MemberLevelSimpleRespVO extends MemberLevelBaseVO { public class MemberLevelSimpleRespVO {
@Schema(description = "编号", example = "6103") @Schema(description = "编号", example = "6103")
private Long id; private Long id;

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
}

View File

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

View File

@ -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") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 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;
}

View File

@ -124,6 +124,10 @@ public class MemberUserDO extends TenantBaseDO {
* 会员经验 * 会员经验
*/ */
private Integer experience; private Integer experience;
/**
* 用户分组编号
*/
private Long groupId;
// TODO 积分等等 // TODO 积分等等
} }

View File

@ -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);
}
}

View File

@ -45,6 +45,7 @@ public interface MemberUserMapper extends BaseMapperX<MemberUserDO> {
.likeIfPresent(MemberUserDO::getNickname, reqVO.getNickname()) .likeIfPresent(MemberUserDO::getNickname, reqVO.getNickname())
.betweenIfPresent(MemberUserDO::getCreateTime, reqVO.getCreateTime()) .betweenIfPresent(MemberUserDO::getCreateTime, reqVO.getCreateTime())
.eqIfPresent(MemberUserDO::getLevelId, reqVO.getLevelId()) .eqIfPresent(MemberUserDO::getLevelId, reqVO.getLevelId())
.eqIfPresent(MemberUserDO::getGroupId, reqVO.getGroupId())
.apply(StrUtil.isNotEmpty(tagIdSql), tagIdSql) .apply(StrUtil.isNotEmpty(tagIdSql), tagIdSql)
.orderByDesc(MemberUserDO::getId)); .orderByDesc(MemberUserDO::getId));
} }
@ -61,4 +62,8 @@ public interface MemberUserMapper extends BaseMapperX<MemberUserDO> {
.set(MemberUserDO::getExperience, 0) .set(MemberUserDO::getExperience, 0)
.set(MemberUserDO::getLevelId, null)); .set(MemberUserDO::getLevelId, null));
} }
default Long selectCountByGroupId(Long groupId) {
return selectCount(MemberUserDO::getGroupId, groupId);
}
} }

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

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

View File

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