会员:后台调整会员等级

This commit is contained in:
owen 2023-08-21 21:31:16 +08:00
parent f884054d2c
commit 8ffbc15cec
21 changed files with 413 additions and 61 deletions

View File

@ -9,7 +9,7 @@ create table member_level
name varchar(30) default '' not null comment '等级名称', name varchar(30) default '' not null comment '等级名称',
experience int default 0 not null comment '升级经验', experience int default 0 not null comment '升级经验',
level int default 0 not null comment '等级', level int default 0 not null comment '等级',
discount int(4) default 100 not null comment '享受折扣', discount tinyint default 100 not null comment '享受折扣',
icon varchar(255) default '' not null comment '等级图标', icon varchar(255) default '' not null comment '等级图标',
background_url varchar(255) default '' not null comment '等级背景图', background_url varchar(255) default '' not null comment '等级背景图',
status tinyint default 0 not null comment '状态', status tinyint default 0 not null comment '状态',
@ -28,9 +28,9 @@ create table member_level_log
user_id bigint default 0 not null comment '用户编号', user_id bigint default 0 not null comment '用户编号',
level_id bigint default 0 not null comment '等级编号', level_id bigint default 0 not null comment '等级编号',
level int default 0 not null comment '会员等级', level int default 0 not null comment '会员等级',
discount int(4) default 100 not null comment '享受折扣', discount tinyint default 100 not null comment '享受折扣',
experience int(4) default 0 not null comment '升级经验', experience int default 0 not null comment '升级经验',
user_experience int(4) default 0 not null comment '会员此时的经验', user_experience int default 0 not null comment '会员此时的经验',
remark varchar(255) default '' not null comment '备注', remark varchar(255) default '' not null comment '备注',
description varchar(255) default '' not null comment '描述', description varchar(255) default '' not null comment '描述',
creator varchar(64) default '' null comment '创建者', creator varchar(64) default '' null comment '创建者',
@ -68,54 +68,27 @@ create index idx_user_biz_type on member_experience_log (user_id, biz_type) comm
-- 增加字典 -- 增加字典
insert system_dict_type(name, type) values ('会员经验业务类型', 'member_experience_biz_type'); insert system_dict_type(name, type) values ('会员经验业务类型', 'member_experience_biz_type');
insert system_dict_data(dict_type, label, value, sort) values ('member_experience_biz_type', '系统', '0', 0); insert system_dict_data(dict_type, label, value, sort) values ('member_experience_biz_type', '管理员调整', '0', 0);
insert system_dict_data(dict_type, label, value, sort) values ('member_experience_biz_type', '订单', '1', 1); insert system_dict_data(dict_type, label, value, sort) values ('member_experience_biz_type', '邀新奖励', '1', 1);
insert system_dict_data(dict_type, label, value, sort) values ('member_experience_biz_type', '签到', '2', 2); insert system_dict_data(dict_type, label, value, sort) values ('member_experience_biz_type', '下单奖励', '2', 2);
insert system_dict_data(dict_type, label, value, sort) values ('member_experience_biz_type', '退单扣除', '3', 3);
insert system_dict_data(dict_type, label, value, sort) values ('member_experience_biz_type', '签到奖励', '4', 4);
insert system_dict_data(dict_type, label, value, sort) values ('member_experience_biz_type', '抽奖奖励', '5', 5);
-- 菜单 SQL -- 菜单 SQL
INSERT INTO system_menu( INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status, component_name)
name, permission, type, sort, parent_id, VALUES ('会员等级', '', 2, 3, 2262, 'level', '', 'member/level/index', 0, 'MemberLevel');
path, icon, component, status, component_name
)
VALUES (
'会员等级', '', 2, 3, 2262,
'level', '', 'member/level/index', 0, 'MemberLevel'
);
-- 按钮父菜单ID -- 按钮父菜单ID
-- 暂时只支持 MySQL如果你是 OraclePostgreSQLSQLServer 的话需要手动修改 @parentId 的部分的代码 -- 暂时只支持 MySQL如果你是 OraclePostgreSQLSQLServer 的话需要手动修改 @parentId 的部分的代码
SELECT @parentId := LAST_INSERT_ID(); SELECT @parentId := LAST_INSERT_ID();
-- 按钮 SQL -- 按钮 SQL
INSERT INTO system_menu( INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
name, permission, type, sort, parent_id, VALUES ('会员等级查询', 'member:level:query', 3, 1, @parentId, '', '', '', 0);
path, icon, component, status INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
) VALUES ('会员等级创建', 'member:level:create', 3, 2, @parentId, '', '', '', 0);
VALUES ( INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
'会员等级查询', 'member:level:query', 3, 1, @parentId, VALUES ('会员等级更新', 'member:level:update', 3, 3, @parentId, '', '', '', 0);
'', '', '', 0 INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
); VALUES ('会员等级删除', 'member:level:delete', 3, 4, @parentId, '', '', '', 0);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'会员等级创建', 'member:level:create', 3, 2, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'会员等级更新', 'member:level:update', 3, 3, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'会员等级删除', 'member:level:delete', 3, 4, @parentId,
'', '', '', 0
);

View File

@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.member.api.level;
import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;
/**
* 会员等级 API接口
*
* @author owen
*/
public interface MemberLevelApi {
/**
* 增加会员经验
*
* @param userId 会员ID
* @param experience 经验
* @param bizType 业务类型
* @param bizId 业务编号
*/
void plusExperience(Long userId, Integer experience, MemberExperienceBizTypeEnum bizType, String bizId);
}

View File

@ -51,4 +51,5 @@ 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, "会员等级调整原因不能为空");
} }

View File

@ -12,13 +12,17 @@ import lombok.Getter;
@AllArgsConstructor @AllArgsConstructor
public enum MemberExperienceBizTypeEnum { public enum MemberExperienceBizTypeEnum {
/** /**
* * 管理员调整邀请新用户下单退单签到抽奖
*/ */
SYSTEM(0, "系统"), ADMIN(0, "管理员调整","管理员调整获得{}经验"),
ORDER(1, "订单"), INVITE_REGISTER(1, "邀新奖励","邀请好友获得{}经验"),
SIGN_IN(2, "签到"), ORDER(2, "下单奖励", "下单获得{}经验"),
REFUND(3, "退单扣除","退单获得{}经验"),
SIGN_IN(4, "签到奖励","签到获得{}经验"),
LOTTERY(5, "抽奖奖励","抽奖获得{}经验"),
; ;
private final int value; private final int value;
private final String name; private final String title;
private final String desc;
} }

View File

@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.member.api.level;
import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;
import cn.iocoder.yudao.module.member.service.level.MemberLevelService;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
/**
* 会员等级 API 实现类
*
* @author owen
*/
@Service
@Validated
public class MemberLevelApiImpl implements MemberLevelApi {
@Resource
private MemberLevelService memberLevelService;
public void plusExperience(Long userId, Integer experience, MemberExperienceBizTypeEnum bizType, String bizId) {
memberLevelService.plusExperience(userId, experience, bizType, bizId);
}
}

View File

@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.member.controller.admin.level; package cn.iocoder.yudao.module.member.controller.admin.level;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.*; import cn.iocoder.yudao.module.member.controller.admin.level.vo.*;
@ -77,9 +76,9 @@ public class MemberLevelController {
@GetMapping("/list-all-simple") @GetMapping("/list-all-simple")
@Operation(summary = "获取会员等级精简信息列表", description = "只包含被开启的会员等级,主要用于前端的下拉选项") @Operation(summary = "获取会员等级精简信息列表", description = "只包含被开启的会员等级,主要用于前端的下拉选项")
public CommonResult<List<MemberLevelSimpleRespVO>> getSimpleUserList() { public CommonResult<List<MemberLevelSimpleRespVO>> getSimpleLevelList() {
// 获用户列表只要开启状态的 // 获用户列表只要开启状态的
List<MemberLevelDO> list = levelService.getLevelListByStatus(CommonStatusEnum.ENABLE.getStatus()); List<MemberLevelDO> list = levelService.getEnableLevelList();
// 排序后返回给前端 // 排序后返回给前端
return success(MemberLevelConvert.INSTANCE.convertSimpleList(list)); return success(MemberLevelConvert.INSTANCE.convertSimpleList(list));
} }

View File

@ -7,8 +7,10 @@ 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.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.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;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
@ -38,6 +40,8 @@ public class MemberUserController {
private MemberUserService memberUserService; private MemberUserService memberUserService;
@Resource @Resource
private MemberTagService memberTagService; private MemberTagService memberTagService;
@Resource
private MemberLevelService memberLevelService;
@PutMapping("/update") @PutMapping("/update")
@Operation(summary = "更新会员用户") @Operation(summary = "更新会员用户")
@ -72,7 +76,11 @@ public class MemberUserController {
.flatMap(Collection::stream) .flatMap(Collection::stream)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
List<MemberTagDO> tags = memberTagService.getTagList(tagIds); List<MemberTagDO> tags = memberTagService.getTagList(tagIds);
return success(MemberUserConvert.INSTANCE.convertPage(pageResult, tags));
// 处理会员级别返显
List<MemberLevelDO> levels = memberLevelService.getEnableLevelList();
return success(MemberUserConvert.INSTANCE.convertPage(pageResult, tags, levels));
} }
} }

View File

@ -53,4 +53,7 @@ public class MemberUserBaseVO {
@Schema(description = "会员标签", example = "[1, 2]") @Schema(description = "会员标签", example = "[1, 2]")
private List<Long> tagIds; private List<Long> tagIds;
@Schema(description = "会员等级编号", example = "1")
private Long levelId;
} }

View File

@ -32,7 +32,10 @@ public class MemberUserPageReqVO extends PageParam {
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime; private LocalDateTime[] createTime;
@Schema(description = "会员标签", example = "[1, 2]") @Schema(description = "会员标签编号列表", example = "[1, 2]")
private List<Long> tagIds; private List<Long> tagIds;
@Schema(description = "会员等级标号", example = "1")
private Long levelId;
} }

View File

@ -35,4 +35,7 @@ public class MemberUserRespVO extends MemberUserBaseVO {
@Schema(description = "会员标签", example = "[红色, 快乐]") @Schema(description = "会员标签", example = "[红色, 快乐]")
private List<String> tagNames; private List<String> tagNames;
@Schema(description = "会员等级", example = "黄金会员")
private String levelName;
} }

View File

@ -17,4 +17,7 @@ public class MemberUserUpdateReqVO extends MemberUserBaseVO {
@NotNull(message = "编号不能为空") @NotNull(message = "编号不能为空")
private Long id; private Long id;
@Schema(description = "会员级别修改原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "推广需要")
private String levelReason;
} }

View File

@ -1,10 +1,13 @@
package cn.iocoder.yudao.module.member.convert.user; package cn.iocoder.yudao.module.member.convert.user;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; 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.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 org.mapstruct.Mapper; import org.mapstruct.Mapper;
@ -34,11 +37,18 @@ public interface MemberUserConvert {
MemberUserRespVO convert03(MemberUserDO bean); MemberUserRespVO convert03(MemberUserDO bean);
default PageResult<MemberUserRespVO> convertPage(PageResult<MemberUserDO> pageResult, default PageResult<MemberUserRespVO> convertPage(PageResult<MemberUserDO> pageResult,
List<MemberTagDO> tags) { List<MemberTagDO> tags,
List<MemberLevelDO> levels) {
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);
// 填充关联数据
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));
} }
return result; return result;
} }

View File

@ -26,6 +26,8 @@ public interface MemberLevelMapper extends BaseMapperX<MemberLevelDO> {
default List<MemberLevelDO> selectListByStatus(Integer status) { default List<MemberLevelDO> selectListByStatus(Integer status) {
return selectList(MemberLevelDO::getStatus, status); return selectList(new LambdaQueryWrapperX<MemberLevelDO>()
.eq(MemberLevelDO::getStatus, status)
.orderByAsc(MemberLevelDO::getLevel));
} }
} }

View File

@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO; import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import java.util.List; import java.util.List;
@ -47,4 +48,16 @@ public interface MemberUserMapper extends BaseMapperX<MemberUserDO> {
.orderByDesc(MemberUserDO::getId)); .orderByDesc(MemberUserDO::getId));
} }
/**
* 取消会员的等级
*
* @param userId 会员编号
* @return 受影响的行数
*/
default int cancelUserLevel(Long userId) {
return update(null, new LambdaUpdateWrapper<MemberUserDO>()
.eq(MemberUserDO::getId, userId)
.set(MemberUserDO::getExperience, 0)
.set(MemberUserDO::getLevelId, null));
}
} }

View File

@ -4,6 +4,7 @@ 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.MemberExperienceLogExportReqVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogPageReqVO; 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.dataobject.level.MemberExperienceLogDO;
import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -54,4 +55,23 @@ public interface MemberExperienceLogService {
*/ */
List<MemberExperienceLogDO> getExperienceLogList(MemberExperienceLogExportReqVO exportReqVO); List<MemberExperienceLogDO> getExperienceLogList(MemberExperienceLogExportReqVO exportReqVO);
/**
* 创建 手动调整 经验变动记录
*
* @param userId 会员编号
* @param experience 变动经验值
* @param totalExperience 会员当前的经验
*/
void createAdjustLog(Long userId, int experience, int totalExperience);
/**
* 根据业务类型, 创建 经验变动记录
*
* @param userId 会员编号
* @param experience 变动经验值
* @param totalExperience 会员当前的经验
* @param bizType 业务类型
* @param bizId 业务ID
*/
void createBizLog(Long userId, int experience, int totalExperience, MemberExperienceBizTypeEnum bizType, String bizId);
} }

View File

@ -1,10 +1,12 @@
package cn.iocoder.yudao.module.member.service.level; 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.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.MemberExperienceLogExportReqVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogPageReqVO; 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.dataobject.level.MemberExperienceLogDO;
import cn.iocoder.yudao.module.member.dal.mysql.level.MemberExperienceLogMapper; import cn.iocoder.yudao.module.member.dal.mysql.level.MemberExperienceLogMapper;
import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -61,4 +63,24 @@ public class MemberExperienceLogServiceImpl implements MemberExperienceLogServic
return experienceLogMapper.selectList(exportReqVO); return experienceLogMapper.selectList(exportReqVO);
} }
@Override
public void createAdjustLog(Long userId, int experience, int totalExperience) {
// 管理员调整时, 没有业务编号, 记录对应的枚举值
String bizId = MemberExperienceBizTypeEnum.ADMIN.getValue() + "";
this.createBizLog(userId, experience, totalExperience, MemberExperienceBizTypeEnum.ADMIN, bizId);
}
@Override
public void createBizLog(Long userId, int experience, int totalExperience, MemberExperienceBizTypeEnum bizType, String bizId) {
MemberExperienceLogDO experienceLogDO = new MemberExperienceLogDO();
experienceLogDO.setUserId(userId);
experienceLogDO.setExperience(experience);
experienceLogDO.setTotalExperience(totalExperience);
experienceLogDO.setBizId(bizId);
experienceLogDO.setBizType(bizType.getValue());
experienceLogDO.setTitle(bizType.getTitle());
experienceLogDO.setDescription(StrUtil.format(bizType.getDesc(), experience));
experienceLogMapper.insert(experienceLogDO);
}
} }

View File

@ -3,7 +3,9 @@ package cn.iocoder.yudao.module.member.service.level;
import cn.iocoder.yudao.framework.common.pojo.PageResult; 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.MemberLevelLogExportReqVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogPageReqVO; 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; import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelLogDO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -54,4 +56,29 @@ public interface MemberLevelLogService {
*/ */
List<MemberLevelLogDO> getLevelLogList(MemberLevelLogExportReqVO exportReqVO); List<MemberLevelLogDO> getLevelLogList(MemberLevelLogExportReqVO exportReqVO);
/**
* 创建记录 取消等级
*
* @param userId 会员编号
* @param reason 调整原因
*/
void createCancelLog(Long userId, String reason);
/**
* 创建记录 手动调整
*
* @param user 会员
* @param level 等级
* @param experience 变动经验值
* @param reason 调整原因
*/
void createAdjustLog(MemberUserDO user, MemberLevelDO level, int experience, String reason);
/**
* 创建记录 自动升级
*
* @param user 会员
* @param level 等级
*/
void createAutoUpgradeLog(MemberUserDO user, MemberLevelDO level);
} }

View File

@ -3,7 +3,9 @@ package cn.iocoder.yudao.module.member.service.level;
import cn.iocoder.yudao.framework.common.pojo.PageResult; 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.MemberLevelLogExportReqVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogPageReqVO; 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; import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelLogDO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.dal.mysql.level.MemberLevelLogMapper; import cn.iocoder.yudao.module.member.dal.mysql.level.MemberLevelLogMapper;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -61,4 +63,53 @@ public class MemberLevelLogServiceImpl implements MemberLevelLogService {
return levelLogMapper.selectList(exportReqVO); return levelLogMapper.selectList(exportReqVO);
} }
@Override
public void createCancelLog(Long userId, String reason) {
MemberLevelLogDO levelLogDO = new MemberLevelLogDO();
levelLogDO.setUserId(userId);
levelLogDO.setRemark(reason);
levelLogDO.setDescription("管理员取消");
levelLogMapper.insert(levelLogDO);
// 给会员发送等级变动消息
notifyMember(userId, levelLogDO);
}
@Override
public void createAdjustLog(MemberUserDO user, MemberLevelDO level, int experience, String reason) {
MemberLevelLogDO levelLogDO = new MemberLevelLogDO();
levelLogDO.setUserId(user.getId());
levelLogDO.setLevelId(level.getId());
levelLogDO.setLevel(level.getLevel());
levelLogDO.setDiscount(level.getDiscount());
levelLogDO.setUserExperience(level.getExperience());
levelLogDO.setExperience(experience);
levelLogDO.setRemark(reason);
levelLogDO.setDescription("管理员调整为:" + level.getName());
levelLogMapper.insert(levelLogDO);
// 给会员发送等级变动消息
notifyMember(user.getId(), levelLogDO);
}
@Override
public void createAutoUpgradeLog(MemberUserDO user, MemberLevelDO level) {
MemberLevelLogDO levelLogDO = new MemberLevelLogDO();
levelLogDO.setUserId(user.getId());
levelLogDO.setLevelId(level.getId());
levelLogDO.setLevel(level.getLevel());
levelLogDO.setDiscount(level.getDiscount());
levelLogDO.setExperience(level.getExperience());
levelLogDO.setUserExperience(user.getExperience());
levelLogDO.setDescription("成为:" + level.getName());
levelLogMapper.insert(levelLogDO);
// 给会员发送等级变动消息
notifyMember(user.getId(), levelLogDO);
}
private void notifyMember(Long userId, MemberLevelLogDO level) {
//todo: 给会员发消息
}
} }

View File

@ -1,11 +1,15 @@
package cn.iocoder.yudao.module.member.service.level; package cn.iocoder.yudao.module.member.service.level;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelCreateReqVO; import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelCreateReqVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelPageReqVO; import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelPageReqVO;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelUpdateReqVO; import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelUpdateReqVO;
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.user.MemberUserDO;
import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;
import javax.annotation.Nullable;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -71,4 +75,33 @@ public interface MemberLevelService {
* @return 会员等级列表 * @return 会员等级列表
*/ */
List<MemberLevelDO> getLevelListByStatus(Integer status); List<MemberLevelDO> getLevelListByStatus(Integer status);
/**
* 获得开启状态的会员等级列表
*
* @return 会员等级列表
*/
default List<MemberLevelDO> getEnableLevelList() {
return getLevelListByStatus(CommonStatusEnum.ENABLE.getStatus());
}
/**
* 修改会员的等级
*
* @param user 会员
* @param levelId 要修改的等级编号编号为空时代表取消会员的等级
* @param levelReason 修改原因
*/
void updateUserLevel(MemberUserDO user, @Nullable Long levelId, String levelReason);
/**
* 增加会员经验
*
* @param userId 会员ID
* @param experience 经验
* @param bizType 业务类型
* @param bizId 业务编号
*/
void plusExperience(Long userId, Integer experience, MemberExperienceBizTypeEnum bizType, String bizId);
} }

View File

@ -1,6 +1,9 @@
package cn.iocoder.yudao.module.member.service.level; package cn.iocoder.yudao.module.member.service.level;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelCreateReqVO; import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelCreateReqVO;
@ -8,14 +11,19 @@ import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelPageR
import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelUpdateReqVO; import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelUpdateReqVO;
import cn.iocoder.yudao.module.member.convert.level.MemberLevelConvert; import cn.iocoder.yudao.module.member.convert.level.MemberLevelConvert;
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.user.MemberUserDO;
import cn.iocoder.yudao.module.member.dal.mysql.level.MemberLevelMapper; import cn.iocoder.yudao.module.member.dal.mysql.level.MemberLevelMapper;
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator;
import java.util.List; import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@ -33,6 +41,12 @@ public class MemberLevelServiceImpl implements MemberLevelService {
@Resource @Resource
private MemberLevelMapper levelMapper; private MemberLevelMapper levelMapper;
@Resource
private MemberLevelLogService memberLevelLogService;
@Resource
private MemberExperienceLogService memberExperienceLogService;
@Resource
private MemberUserMapper memberUserMapper;
@Override @Override
public Long createLevel(MemberLevelCreateReqVO createReqVO) { public Long createLevel(MemberLevelCreateReqVO createReqVO) {
@ -153,4 +167,113 @@ public class MemberLevelServiceImpl implements MemberLevelService {
public List<MemberLevelDO> getLevelListByStatus(Integer status) { public List<MemberLevelDO> getLevelListByStatus(Integer status) {
return levelMapper.selectListByStatus(status); return levelMapper.selectListByStatus(status);
} }
@Transactional(rollbackFor = Exception.class)
@Override
public void updateUserLevel(MemberUserDO user, Long levelId, String reason) {
// 未调整的情况1
if (user.getLevelId() == null && levelId == null) {
return;
}
// 未调整的情况2
if (ObjUtil.equal(user.getLevelId(), levelId)) {
return;
}
// 需要后台用户填写为什么调整会员的等级
if (StrUtil.isBlank(reason)) {
throw exception(LEVEL_REASON_NOT_EXISTS);
}
int experience;
int totalExperience = 0;
// 记录等级变动
if (levelId == null) {
experience = -user.getExperience();
// 取消了会员的等级
memberLevelLogService.createCancelLog(user.getId(), reason);
memberUserMapper.cancelUserLevel(user.getId());
} else {
MemberLevelDO level = validateLevelExists(levelId);
// 变动经验值 = 等级的升级经验 - 会员当前的经验正数为增加经验负数为扣减经验
experience = level.getExperience() - user.getExperience();
// 会员当前的经验 = 等级的升级经验
totalExperience = level.getExperience();
memberLevelLogService.createAdjustLog(user, level, experience, reason);
// 更新会员表上的等级编号经验值
updateUserLevelIdAndExperience(user.getId(), levelId, totalExperience);
}
// 记录会员经验变动
memberExperienceLogService.createAdjustLog(user.getId(), experience, totalExperience);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void plusExperience(Long userId, Integer experience, MemberExperienceBizTypeEnum bizType, String bizId) {
if (experience == 0) {
return;
}
MemberUserDO user = memberUserMapper.selectById(userId);
if (user.getExperience() == null) {
user.setExperience(0);
}
// 防止扣出负数
int userExperience = NumberUtil.max(user.getExperience() + experience, 0);
// 创建经验记录
memberExperienceLogService.createBizLog(userId, experience, userExperience, bizType, bizId);
// 计算会员等级
Long levelId = calcLevel(user, userExperience);
// 更新会员表上的等级编号经验值
updateUserLevelIdAndExperience(user.getId(), levelId, userExperience);
}
private void updateUserLevelIdAndExperience(Long userId, Long levelId, Integer experience) {
memberUserMapper.updateById(new MemberUserDO()
.setId(userId)
.setLevelId(levelId).setExperience(experience)
);
}
/**
* 计算会员等级
*
* @param user 会员
* @param userExperience 会员当前的经验值
* @return 会员等级编号null表示无变化
*/
private Long calcLevel(MemberUserDO user, int userExperience) {
List<MemberLevelDO> list = getEnableLevelList();
if (CollUtil.isEmpty(list)) {
log.warn("计算会员等级失败:会员等级配置不存在");
return null;
}
MemberLevelDO matchLevel = list.stream()
.filter(level -> userExperience >= level.getExperience())
.max(Comparator.nullsFirst(Comparator.comparing(MemberLevelDO::getLevel)))
.orElse(null);
if (matchLevel == null) {
log.warn("计算会员等级失败:未找到会员{}经验{}对应的等级配置", user.getId(), userExperience);
return null;
}
// 等级没有变化
if (ObjectUtil.equal(matchLevel.getId(), user.getLevelId())) {
return null;
}
// 保存等级变更记录
memberLevelLogService.createAutoUpgradeLog(user, matchLevel);
return matchLevel.getId();
}
} }

View File

@ -8,13 +8,14 @@ import cn.iocoder.yudao.module.infra.api.file.FileApi;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO; import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO;
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.AppMemberUserResetPasswordReqVO; import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserResetPasswordReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateMobileReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdatePasswordReqVO; import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdatePasswordReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateReqVO; import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateMobileReqVO;
import cn.iocoder.yudao.module.member.convert.auth.AuthConvert; import cn.iocoder.yudao.module.member.convert.auth.AuthConvert;
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.user.MemberUserDO; import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper; import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
import cn.iocoder.yudao.module.member.service.level.MemberLevelService;
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
@ -55,6 +56,9 @@ public class MemberUserServiceImpl implements MemberUserService {
@Resource @Resource
private PasswordEncoder passwordEncoder; private PasswordEncoder passwordEncoder;
@Resource
private MemberLevelService memberLevelService;
@Override @Override
public MemberUserDO getUserByMobile(String mobile) { public MemberUserDO getUserByMobile(String mobile) {
return memberUserMapper.selectByMobile(mobile); return memberUserMapper.selectByMobile(mobile);
@ -180,16 +184,20 @@ public class MemberUserServiceImpl implements MemberUserService {
return passwordEncoder.encode(password); return passwordEncoder.encode(password);
} }
@Transactional(rollbackFor = Exception.class)
@Override @Override
public void updateUser(MemberUserUpdateReqVO updateReqVO) { public void updateUser(MemberUserUpdateReqVO updateReqVO) {
// 校验存在 // 校验存在
validateUserExists(updateReqVO.getId()); MemberUserDO user = validateUserExists(updateReqVO.getId());
// 校验手机唯一 // 校验手机唯一
validateMobileUnique(updateReqVO.getId(), updateReqVO.getMobile()); validateMobileUnique(updateReqVO.getId(), updateReqVO.getMobile());
// 更新 // 更新
MemberUserDO updateObj = MemberUserConvert.INSTANCE.convert(updateReqVO); MemberUserDO updateObj = MemberUserConvert.INSTANCE.convert(updateReqVO);
memberUserMapper.updateById(updateObj); memberUserMapper.updateById(updateObj);
// 会员级别修改
memberLevelService.updateUserLevel(user, updateReqVO.getLevelId(), updateReqVO.getLevelReason());
} }
@VisibleForTesting @VisibleForTesting