Commit 8ffbc15cec03226981f924a390992d331bd49768

Authored by owen
1 parent f884054d

会员:后台调整会员等级

Showing 21 changed files with 413 additions and 61 deletions
sql/mysql/member_level.sql
@@ -9,7 +9,7 @@ create table member_level @@ -9,7 +9,7 @@ create table member_level
9 name varchar(30) default '' not null comment '等级名称', 9 name varchar(30) default '' not null comment '等级名称',
10 experience int default 0 not null comment '升级经验', 10 experience int default 0 not null comment '升级经验',
11 level int default 0 not null comment '等级', 11 level int default 0 not null comment '等级',
12 - discount int(4) default 100 not null comment '享受折扣', 12 + discount tinyint default 100 not null comment '享受折扣',
13 icon varchar(255) default '' not null comment '等级图标', 13 icon varchar(255) default '' not null comment '等级图标',
14 background_url varchar(255) default '' not null comment '等级背景图', 14 background_url varchar(255) default '' not null comment '等级背景图',
15 status tinyint default 0 not null comment '状态', 15 status tinyint default 0 not null comment '状态',
@@ -28,9 +28,9 @@ create table member_level_log @@ -28,9 +28,9 @@ create table member_level_log
28 user_id bigint default 0 not null comment '用户编号', 28 user_id bigint default 0 not null comment '用户编号',
29 level_id bigint default 0 not null comment '等级编号', 29 level_id bigint default 0 not null comment '等级编号',
30 level int default 0 not null comment '会员等级', 30 level int default 0 not null comment '会员等级',
31 - discount int(4) default 100 not null comment '享受折扣',  
32 - experience int(4) default 0 not null comment '升级经验',  
33 - user_experience int(4) default 0 not null comment '会员此时的经验', 31 + discount tinyint default 100 not null comment '享受折扣',
  32 + experience int default 0 not null comment '升级经验',
  33 + user_experience int default 0 not null comment '会员此时的经验',
34 remark varchar(255) default '' not null comment '备注', 34 remark varchar(255) default '' not null comment '备注',
35 description varchar(255) default '' not null comment '描述', 35 description varchar(255) default '' not null comment '描述',
36 creator varchar(64) default '' null comment '创建者', 36 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 @@ -68,54 +68,27 @@ create index idx_user_biz_type on member_experience_log (user_id, biz_type) comm
68 68
69 -- 增加字典 69 -- 增加字典
70 insert system_dict_type(name, type) values ('会员经验业务类型', 'member_experience_biz_type'); 70 insert system_dict_type(name, type) values ('会员经验业务类型', 'member_experience_biz_type');
71 -insert system_dict_data(dict_type, label, value, sort) values ('member_experience_biz_type', '系统', '0', 0);  
72 -insert system_dict_data(dict_type, label, value, sort) values ('member_experience_biz_type', '订单', '1', 1);  
73 -insert system_dict_data(dict_type, label, value, sort) values ('member_experience_biz_type', '签到', '2', 2); 71 +insert system_dict_data(dict_type, label, value, sort) values ('member_experience_biz_type', '管理员调整', '0', 0);
  72 +insert system_dict_data(dict_type, label, value, sort) values ('member_experience_biz_type', '邀新奖励', '1', 1);
  73 +insert system_dict_data(dict_type, label, value, sort) values ('member_experience_biz_type', '下单奖励', '2', 2);
  74 +insert system_dict_data(dict_type, label, value, sort) values ('member_experience_biz_type', '退单扣除', '3', 3);
  75 +insert system_dict_data(dict_type, label, value, sort) values ('member_experience_biz_type', '签到奖励', '4', 4);
  76 +insert system_dict_data(dict_type, label, value, sort) values ('member_experience_biz_type', '抽奖奖励', '5', 5);
74 77
75 -- 菜单 SQL 78 -- 菜单 SQL
76 -INSERT INTO system_menu(  
77 - name, permission, type, sort, parent_id,  
78 - path, icon, component, status, component_name  
79 -)  
80 -VALUES (  
81 - '会员等级', '', 2, 3, 2262,  
82 - 'level', '', 'member/level/index', 0, 'MemberLevel'  
83 -); 79 +INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status, component_name)
  80 +VALUES ('会员等级', '', 2, 3, 2262, 'level', '', 'member/level/index', 0, 'MemberLevel');
84 81
85 -- 按钮父菜单ID 82 -- 按钮父菜单ID
86 -- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 83 -- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
87 SELECT @parentId := LAST_INSERT_ID(); 84 SELECT @parentId := LAST_INSERT_ID();
88 85
89 -- 按钮 SQL 86 -- 按钮 SQL
90 -INSERT INTO system_menu(  
91 - name, permission, type, sort, parent_id,  
92 - path, icon, component, status  
93 -)  
94 -VALUES (  
95 - '会员等级查询', 'member:level:query', 3, 1, @parentId,  
96 - '', '', '', 0  
97 -);  
98 -INSERT INTO system_menu(  
99 - name, permission, type, sort, parent_id,  
100 - path, icon, component, status  
101 -)  
102 -VALUES (  
103 - '会员等级创建', 'member:level:create', 3, 2, @parentId,  
104 - '', '', '', 0  
105 -);  
106 -INSERT INTO system_menu(  
107 - name, permission, type, sort, parent_id,  
108 - path, icon, component, status  
109 -)  
110 -VALUES (  
111 - '会员等级更新', 'member:level:update', 3, 3, @parentId,  
112 - '', '', '', 0  
113 -);  
114 -INSERT INTO system_menu(  
115 - name, permission, type, sort, parent_id,  
116 - path, icon, component, status  
117 -)  
118 -VALUES (  
119 - '会员等级删除', 'member:level:delete', 3, 4, @parentId,  
120 - '', '', '', 0  
121 -); 87 +INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
  88 +VALUES ('会员等级查询', 'member:level:query', 3, 1, @parentId, '', '', '', 0);
  89 +INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
  90 +VALUES ('会员等级创建', 'member:level:create', 3, 2, @parentId, '', '', '', 0);
  91 +INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
  92 +VALUES ('会员等级更新', 'member:level:update', 3, 3, @parentId, '', '', '', 0);
  93 +INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
  94 +VALUES ('会员等级删除', 'member:level:delete', 3, 4, @parentId, '', '', '', 0);
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/level/MemberLevelApi.java 0 → 100644
  1 +package cn.iocoder.yudao.module.member.api.level;
  2 +
  3 +import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;
  4 +
  5 +/**
  6 + * 会员等级 API接口
  7 + *
  8 + * @author owen
  9 + */
  10 +public interface MemberLevelApi {
  11 +
  12 + /**
  13 + * 增加会员经验
  14 + *
  15 + * @param userId 会员ID
  16 + * @param experience 经验
  17 + * @param bizType 业务类型
  18 + * @param bizId 业务编号
  19 + */
  20 + void plusExperience(Long userId, Integer experience, MemberExperienceBizTypeEnum bizType, String bizId);
  21 +}
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java
@@ -51,4 +51,5 @@ public interface ErrorCodeConstants { @@ -51,4 +51,5 @@ public interface ErrorCodeConstants {
51 51
52 ErrorCode LEVEL_LOG_NOT_EXISTS = new ErrorCode(1004007100, "会员等级记录不存在"); 52 ErrorCode LEVEL_LOG_NOT_EXISTS = new ErrorCode(1004007100, "会员等级记录不存在");
53 ErrorCode EXPERIENCE_LOG_NOT_EXISTS = new ErrorCode(1004007200, "会员经验记录不存在"); 53 ErrorCode EXPERIENCE_LOG_NOT_EXISTS = new ErrorCode(1004007200, "会员经验记录不存在");
  54 + ErrorCode LEVEL_REASON_NOT_EXISTS = new ErrorCode(1004007300, "会员等级调整原因不能为空");
54 } 55 }
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/MemberExperienceBizTypeEnum.java
@@ -12,13 +12,17 @@ import lombok.Getter; @@ -12,13 +12,17 @@ import lombok.Getter;
12 @AllArgsConstructor 12 @AllArgsConstructor
13 public enum MemberExperienceBizTypeEnum { 13 public enum MemberExperienceBizTypeEnum {
14 /** 14 /**
15 - * 15 + * 管理员调整、邀请新用户、下单、退单、签到、抽奖
16 */ 16 */
17 - SYSTEM(0, "系统"),  
18 - ORDER(1, "订单"),  
19 - SIGN_IN(2, "签到"), 17 + ADMIN(0, "管理员调整","管理员调整获得{}经验"),
  18 + INVITE_REGISTER(1, "邀新奖励","邀请好友获得{}经验"),
  19 + ORDER(2, "下单奖励", "下单获得{}经验"),
  20 + REFUND(3, "退单扣除","退单获得{}经验"),
  21 + SIGN_IN(4, "签到奖励","签到获得{}经验"),
  22 + LOTTERY(5, "抽奖奖励","抽奖获得{}经验"),
20 ; 23 ;
21 24
22 private final int value; 25 private final int value;
23 - private final String name; 26 + private final String title;
  27 + private final String desc;
24 } 28 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/level/MemberLevelApiImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.member.api.level;
  2 +
  3 +import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;
  4 +import cn.iocoder.yudao.module.member.service.level.MemberLevelService;
  5 +import org.springframework.stereotype.Service;
  6 +import org.springframework.validation.annotation.Validated;
  7 +
  8 +import javax.annotation.Resource;
  9 +
  10 +/**
  11 + * 会员等级 API 实现类
  12 + *
  13 + * @author owen
  14 + */
  15 +@Service
  16 +@Validated
  17 +public class MemberLevelApiImpl implements MemberLevelApi {
  18 +
  19 + @Resource
  20 + private MemberLevelService memberLevelService;
  21 +
  22 + public void plusExperience(Long userId, Integer experience, MemberExperienceBizTypeEnum bizType, String bizId) {
  23 + memberLevelService.plusExperience(userId, experience, bizType, bizId);
  24 + }
  25 +}
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/MemberLevelController.java
1 package cn.iocoder.yudao.module.member.controller.admin.level; 1 package cn.iocoder.yudao.module.member.controller.admin.level;
2 2
3 -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;  
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.module.member.controller.admin.level.vo.*; 5 import cn.iocoder.yudao.module.member.controller.admin.level.vo.*;
@@ -77,9 +76,9 @@ public class MemberLevelController { @@ -77,9 +76,9 @@ public class MemberLevelController {
77 76
78 @GetMapping("/list-all-simple") 77 @GetMapping("/list-all-simple")
79 @Operation(summary = "获取会员等级精简信息列表", description = "只包含被开启的会员等级,主要用于前端的下拉选项") 78 @Operation(summary = "获取会员等级精简信息列表", description = "只包含被开启的会员等级,主要用于前端的下拉选项")
80 - public CommonResult<List<MemberLevelSimpleRespVO>> getSimpleUserList() { 79 + public CommonResult<List<MemberLevelSimpleRespVO>> getSimpleLevelList() {
81 // 获用户列表,只要开启状态的 80 // 获用户列表,只要开启状态的
82 - List<MemberLevelDO> list = levelService.getLevelListByStatus(CommonStatusEnum.ENABLE.getStatus()); 81 + List<MemberLevelDO> list = levelService.getEnableLevelList();
83 // 排序后,返回给前端 82 // 排序后,返回给前端
84 return success(MemberLevelConvert.INSTANCE.convertSimpleList(list)); 83 return success(MemberLevelConvert.INSTANCE.convertSimpleList(list));
85 } 84 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/MemberUserController.java
@@ -7,8 +7,10 @@ import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReq @@ -7,8 +7,10 @@ import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReq
7 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserRespVO; 7 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserRespVO;
8 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO; 8 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO;
9 import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert; 9 import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert;
  10 +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO;
10 import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO; 11 import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO;
11 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; 12 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
  13 +import cn.iocoder.yudao.module.member.service.level.MemberLevelService;
12 import cn.iocoder.yudao.module.member.service.tag.MemberTagService; 14 import cn.iocoder.yudao.module.member.service.tag.MemberTagService;
13 import cn.iocoder.yudao.module.member.service.user.MemberUserService; 15 import cn.iocoder.yudao.module.member.service.user.MemberUserService;
14 import io.swagger.v3.oas.annotations.Operation; 16 import io.swagger.v3.oas.annotations.Operation;
@@ -38,6 +40,8 @@ public class MemberUserController { @@ -38,6 +40,8 @@ public class MemberUserController {
38 private MemberUserService memberUserService; 40 private MemberUserService memberUserService;
39 @Resource 41 @Resource
40 private MemberTagService memberTagService; 42 private MemberTagService memberTagService;
  43 + @Resource
  44 + private MemberLevelService memberLevelService;
41 45
42 @PutMapping("/update") 46 @PutMapping("/update")
43 @Operation(summary = "更新会员用户") 47 @Operation(summary = "更新会员用户")
@@ -72,7 +76,11 @@ public class MemberUserController { @@ -72,7 +76,11 @@ public class MemberUserController {
72 .flatMap(Collection::stream) 76 .flatMap(Collection::stream)
73 .collect(Collectors.toSet()); 77 .collect(Collectors.toSet());
74 List<MemberTagDO> tags = memberTagService.getTagList(tagIds); 78 List<MemberTagDO> tags = memberTagService.getTagList(tagIds);
75 - return success(MemberUserConvert.INSTANCE.convertPage(pageResult, tags)); 79 +
  80 + // 处理会员级别返显
  81 + List<MemberLevelDO> levels = memberLevelService.getEnableLevelList();
  82 +
  83 + return success(MemberUserConvert.INSTANCE.convertPage(pageResult, tags, levels));
76 } 84 }
77 85
78 } 86 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserBaseVO.java
@@ -53,4 +53,7 @@ public class MemberUserBaseVO { @@ -53,4 +53,7 @@ public class MemberUserBaseVO {
53 @Schema(description = "会员标签", example = "[1, 2]") 53 @Schema(description = "会员标签", example = "[1, 2]")
54 private List<Long> tagIds; 54 private List<Long> tagIds;
55 55
  56 + @Schema(description = "会员等级编号", example = "1")
  57 + private Long levelId;
  58 +
56 } 59 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserPageReqVO.java
@@ -32,7 +32,10 @@ public class MemberUserPageReqVO extends PageParam { @@ -32,7 +32,10 @@ public class MemberUserPageReqVO extends PageParam {
32 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) 32 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
33 private LocalDateTime[] createTime; 33 private LocalDateTime[] createTime;
34 34
35 - @Schema(description = "会员标签", example = "[1, 2]") 35 + @Schema(description = "会员标签编号列表", example = "[1, 2]")
36 private List<Long> tagIds; 36 private List<Long> tagIds;
37 37
  38 + @Schema(description = "会员等级标号", example = "1")
  39 + private Long levelId;
  40 +
38 } 41 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserRespVO.java
@@ -35,4 +35,7 @@ public class MemberUserRespVO extends MemberUserBaseVO { @@ -35,4 +35,7 @@ public class MemberUserRespVO extends MemberUserBaseVO {
35 @Schema(description = "会员标签", example = "[红色, 快乐]") 35 @Schema(description = "会员标签", example = "[红色, 快乐]")
36 private List<String> tagNames; 36 private List<String> tagNames;
37 37
  38 + @Schema(description = "会员等级", example = "黄金会员")
  39 + private String levelName;
  40 +
38 } 41 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserUpdateReqVO.java
@@ -17,4 +17,7 @@ public class MemberUserUpdateReqVO extends MemberUserBaseVO { @@ -17,4 +17,7 @@ public class MemberUserUpdateReqVO extends MemberUserBaseVO {
17 @NotNull(message = "编号不能为空") 17 @NotNull(message = "编号不能为空")
18 private Long id; 18 private Long id;
19 19
  20 + @Schema(description = "会员级别修改原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "推广需要")
  21 + private String levelReason;
  22 +
20 } 23 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/user/MemberUserConvert.java
1 package cn.iocoder.yudao.module.member.convert.user; 1 package cn.iocoder.yudao.module.member.convert.user;
2 2
  3 +import cn.hutool.core.map.MapUtil;
  4 +import cn.hutool.core.util.StrUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; 6 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
5 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserRespVO; 7 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserRespVO;
6 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO; 8 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO;
7 import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserInfoRespVO; 9 import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserInfoRespVO;
  10 +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO;
8 import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO; 11 import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO;
9 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; 12 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
10 import org.mapstruct.Mapper; 13 import org.mapstruct.Mapper;
@@ -34,11 +37,18 @@ public interface MemberUserConvert { @@ -34,11 +37,18 @@ public interface MemberUserConvert {
34 MemberUserRespVO convert03(MemberUserDO bean); 37 MemberUserRespVO convert03(MemberUserDO bean);
35 38
36 default PageResult<MemberUserRespVO> convertPage(PageResult<MemberUserDO> pageResult, 39 default PageResult<MemberUserRespVO> convertPage(PageResult<MemberUserDO> pageResult,
37 - List<MemberTagDO> tags) { 40 + List<MemberTagDO> tags,
  41 + List<MemberLevelDO> levels) {
38 PageResult<MemberUserRespVO> result = convertPage(pageResult); 42 PageResult<MemberUserRespVO> result = convertPage(pageResult);
  43 +
  44 + // 处理关联数据
39 Map<Long, String> tagMap = convertMap(tags, MemberTagDO::getId, MemberTagDO::getName); 45 Map<Long, String> tagMap = convertMap(tags, MemberTagDO::getId, MemberTagDO::getName);
  46 + Map<Long, String> levelMap = convertMap(levels, MemberLevelDO::getId, MemberLevelDO::getName);
  47 +
  48 + // 填充关联数据
40 for (MemberUserRespVO vo : result.getList()) { 49 for (MemberUserRespVO vo : result.getList()) {
41 vo.setTagNames(convertList(vo.getTagIds(), tagMap::get)); 50 vo.setTagNames(convertList(vo.getTagIds(), tagMap::get));
  51 + vo.setLevelName(MapUtil.getStr(levelMap, vo.getLevelId(), StrUtil.EMPTY));
42 } 52 }
43 return result; 53 return result;
44 } 54 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/level/MemberLevelMapper.java
@@ -26,6 +26,8 @@ public interface MemberLevelMapper extends BaseMapperX&lt;MemberLevelDO&gt; { @@ -26,6 +26,8 @@ public interface MemberLevelMapper extends BaseMapperX&lt;MemberLevelDO&gt; {
26 26
27 27
28 default List<MemberLevelDO> selectListByStatus(Integer status) { 28 default List<MemberLevelDO> selectListByStatus(Integer status) {
29 - return selectList(MemberLevelDO::getStatus, status); 29 + return selectList(new LambdaQueryWrapperX<MemberLevelDO>()
  30 + .eq(MemberLevelDO::getStatus, status)
  31 + .orderByAsc(MemberLevelDO::getLevel));
30 } 32 }
31 } 33 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java
@@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; @@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
7 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 7 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
8 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO; 8 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO;
9 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; 9 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
  10 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
10 import org.apache.ibatis.annotations.Mapper; 11 import org.apache.ibatis.annotations.Mapper;
11 12
12 import java.util.List; 13 import java.util.List;
@@ -47,4 +48,16 @@ public interface MemberUserMapper extends BaseMapperX&lt;MemberUserDO&gt; { @@ -47,4 +48,16 @@ public interface MemberUserMapper extends BaseMapperX&lt;MemberUserDO&gt; {
47 .orderByDesc(MemberUserDO::getId)); 48 .orderByDesc(MemberUserDO::getId));
48 } 49 }
49 50
  51 + /**
  52 + * 取消会员的等级
  53 + *
  54 + * @param userId 会员编号
  55 + * @return 受影响的行数
  56 + */
  57 + default int cancelUserLevel(Long userId) {
  58 + return update(null, new LambdaUpdateWrapper<MemberUserDO>()
  59 + .eq(MemberUserDO::getId, userId)
  60 + .set(MemberUserDO::getExperience, 0)
  61 + .set(MemberUserDO::getLevelId, null));
  62 + }
50 } 63 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberExperienceLogService.java
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogExportReqVO; 4 import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogExportReqVO;
5 import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogPageReqVO; 5 import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogPageReqVO;
6 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberExperienceLogDO; 6 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberExperienceLogDO;
  7 +import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;
7 8
8 import java.util.Collection; 9 import java.util.Collection;
9 import java.util.List; 10 import java.util.List;
@@ -54,4 +55,23 @@ public interface MemberExperienceLogService { @@ -54,4 +55,23 @@ public interface MemberExperienceLogService {
54 */ 55 */
55 List<MemberExperienceLogDO> getExperienceLogList(MemberExperienceLogExportReqVO exportReqVO); 56 List<MemberExperienceLogDO> getExperienceLogList(MemberExperienceLogExportReqVO exportReqVO);
56 57
  58 + /**
  59 + * 创建 手动调整 经验变动记录
  60 + *
  61 + * @param userId 会员编号
  62 + * @param experience 变动经验值
  63 + * @param totalExperience 会员当前的经验
  64 + */
  65 + void createAdjustLog(Long userId, int experience, int totalExperience);
  66 +
  67 + /**
  68 + * 根据业务类型, 创建 经验变动记录
  69 + *
  70 + * @param userId 会员编号
  71 + * @param experience 变动经验值
  72 + * @param totalExperience 会员当前的经验
  73 + * @param bizType 业务类型
  74 + * @param bizId 业务ID
  75 + */
  76 + void createBizLog(Long userId, int experience, int totalExperience, MemberExperienceBizTypeEnum bizType, String bizId);
57 } 77 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberExperienceLogServiceImpl.java
1 package cn.iocoder.yudao.module.member.service.level; 1 package cn.iocoder.yudao.module.member.service.level;
2 2
  3 +import cn.hutool.core.util.StrUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogExportReqVO; 5 import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogExportReqVO;
5 import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogPageReqVO; 6 import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceLogPageReqVO;
6 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberExperienceLogDO; 7 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberExperienceLogDO;
7 import cn.iocoder.yudao.module.member.dal.mysql.level.MemberExperienceLogMapper; 8 import cn.iocoder.yudao.module.member.dal.mysql.level.MemberExperienceLogMapper;
  9 +import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;
8 import org.springframework.stereotype.Service; 10 import org.springframework.stereotype.Service;
9 import org.springframework.validation.annotation.Validated; 11 import org.springframework.validation.annotation.Validated;
10 12
@@ -61,4 +63,24 @@ public class MemberExperienceLogServiceImpl implements MemberExperienceLogServic @@ -61,4 +63,24 @@ public class MemberExperienceLogServiceImpl implements MemberExperienceLogServic
61 return experienceLogMapper.selectList(exportReqVO); 63 return experienceLogMapper.selectList(exportReqVO);
62 } 64 }
63 65
  66 + @Override
  67 + public void createAdjustLog(Long userId, int experience, int totalExperience) {
  68 + // 管理员调整时, 没有业务编号, 记录对应的枚举值
  69 + String bizId = MemberExperienceBizTypeEnum.ADMIN.getValue() + "";
  70 + this.createBizLog(userId, experience, totalExperience, MemberExperienceBizTypeEnum.ADMIN, bizId);
  71 + }
  72 +
  73 + @Override
  74 + public void createBizLog(Long userId, int experience, int totalExperience, MemberExperienceBizTypeEnum bizType, String bizId) {
  75 + MemberExperienceLogDO experienceLogDO = new MemberExperienceLogDO();
  76 + experienceLogDO.setUserId(userId);
  77 + experienceLogDO.setExperience(experience);
  78 + experienceLogDO.setTotalExperience(totalExperience);
  79 + experienceLogDO.setBizId(bizId);
  80 + experienceLogDO.setBizType(bizType.getValue());
  81 + experienceLogDO.setTitle(bizType.getTitle());
  82 + experienceLogDO.setDescription(StrUtil.format(bizType.getDesc(), experience));
  83 + experienceLogMapper.insert(experienceLogDO);
  84 + }
  85 +
64 } 86 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelLogService.java
@@ -3,7 +3,9 @@ package cn.iocoder.yudao.module.member.service.level; @@ -3,7 +3,9 @@ package cn.iocoder.yudao.module.member.service.level;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogExportReqVO; 4 import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogExportReqVO;
5 import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogPageReqVO; 5 import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogPageReqVO;
  6 +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO;
6 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelLogDO; 7 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelLogDO;
  8 +import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
7 9
8 import java.util.Collection; 10 import java.util.Collection;
9 import java.util.List; 11 import java.util.List;
@@ -54,4 +56,29 @@ public interface MemberLevelLogService { @@ -54,4 +56,29 @@ public interface MemberLevelLogService {
54 */ 56 */
55 List<MemberLevelLogDO> getLevelLogList(MemberLevelLogExportReqVO exportReqVO); 57 List<MemberLevelLogDO> getLevelLogList(MemberLevelLogExportReqVO exportReqVO);
56 58
  59 + /**
  60 + * 创建记录: 取消等级
  61 + *
  62 + * @param userId 会员编号
  63 + * @param reason 调整原因
  64 + */
  65 + void createCancelLog(Long userId, String reason);
  66 +
  67 + /**
  68 + * 创建记录: 手动调整
  69 + *
  70 + * @param user 会员
  71 + * @param level 等级
  72 + * @param experience 变动经验值
  73 + * @param reason 调整原因
  74 + */
  75 + void createAdjustLog(MemberUserDO user, MemberLevelDO level, int experience, String reason);
  76 +
  77 + /**
  78 + * 创建记录: 自动升级
  79 + *
  80 + * @param user 会员
  81 + * @param level 等级
  82 + */
  83 + void createAutoUpgradeLog(MemberUserDO user, MemberLevelDO level);
57 } 84 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelLogServiceImpl.java
@@ -3,7 +3,9 @@ package cn.iocoder.yudao.module.member.service.level; @@ -3,7 +3,9 @@ package cn.iocoder.yudao.module.member.service.level;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogExportReqVO; 4 import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogExportReqVO;
5 import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogPageReqVO; 5 import cn.iocoder.yudao.module.member.controller.admin.level.vo.log.MemberLevelLogPageReqVO;
  6 +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO;
6 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelLogDO; 7 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelLogDO;
  8 +import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
7 import cn.iocoder.yudao.module.member.dal.mysql.level.MemberLevelLogMapper; 9 import cn.iocoder.yudao.module.member.dal.mysql.level.MemberLevelLogMapper;
8 import org.springframework.stereotype.Service; 10 import org.springframework.stereotype.Service;
9 import org.springframework.validation.annotation.Validated; 11 import org.springframework.validation.annotation.Validated;
@@ -61,4 +63,53 @@ public class MemberLevelLogServiceImpl implements MemberLevelLogService { @@ -61,4 +63,53 @@ public class MemberLevelLogServiceImpl implements MemberLevelLogService {
61 return levelLogMapper.selectList(exportReqVO); 63 return levelLogMapper.selectList(exportReqVO);
62 } 64 }
63 65
  66 + @Override
  67 + public void createCancelLog(Long userId, String reason) {
  68 + MemberLevelLogDO levelLogDO = new MemberLevelLogDO();
  69 + levelLogDO.setUserId(userId);
  70 + levelLogDO.setRemark(reason);
  71 + levelLogDO.setDescription("管理员取消");
  72 + levelLogMapper.insert(levelLogDO);
  73 +
  74 + // 给会员发送等级变动消息
  75 + notifyMember(userId, levelLogDO);
  76 + }
  77 +
  78 + @Override
  79 + public void createAdjustLog(MemberUserDO user, MemberLevelDO level, int experience, String reason) {
  80 + MemberLevelLogDO levelLogDO = new MemberLevelLogDO();
  81 + levelLogDO.setUserId(user.getId());
  82 + levelLogDO.setLevelId(level.getId());
  83 + levelLogDO.setLevel(level.getLevel());
  84 + levelLogDO.setDiscount(level.getDiscount());
  85 + levelLogDO.setUserExperience(level.getExperience());
  86 + levelLogDO.setExperience(experience);
  87 + levelLogDO.setRemark(reason);
  88 + levelLogDO.setDescription("管理员调整为:" + level.getName());
  89 + levelLogMapper.insert(levelLogDO);
  90 +
  91 + // 给会员发送等级变动消息
  92 + notifyMember(user.getId(), levelLogDO);
  93 + }
  94 +
  95 + @Override
  96 + public void createAutoUpgradeLog(MemberUserDO user, MemberLevelDO level) {
  97 + MemberLevelLogDO levelLogDO = new MemberLevelLogDO();
  98 + levelLogDO.setUserId(user.getId());
  99 + levelLogDO.setLevelId(level.getId());
  100 + levelLogDO.setLevel(level.getLevel());
  101 + levelLogDO.setDiscount(level.getDiscount());
  102 + levelLogDO.setExperience(level.getExperience());
  103 + levelLogDO.setUserExperience(user.getExperience());
  104 + levelLogDO.setDescription("成为:" + level.getName());
  105 + levelLogMapper.insert(levelLogDO);
  106 +
  107 + // 给会员发送等级变动消息
  108 + notifyMember(user.getId(), levelLogDO);
  109 + }
  110 +
  111 + private void notifyMember(Long userId, MemberLevelLogDO level) {
  112 + //todo: 给会员发消息
  113 + }
  114 +
64 } 115 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelService.java
1 package cn.iocoder.yudao.module.member.service.level; 1 package cn.iocoder.yudao.module.member.service.level;
2 2
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelCreateReqVO; 5 import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelCreateReqVO;
5 import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelPageReqVO; 6 import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelPageReqVO;
6 import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelUpdateReqVO; 7 import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelUpdateReqVO;
7 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO; 8 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO;
  9 +import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
  10 +import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;
8 11
  12 +import javax.annotation.Nullable;
9 import javax.validation.Valid; 13 import javax.validation.Valid;
10 import java.util.Collection; 14 import java.util.Collection;
11 import java.util.List; 15 import java.util.List;
@@ -71,4 +75,33 @@ public interface MemberLevelService { @@ -71,4 +75,33 @@ public interface MemberLevelService {
71 * @return 会员等级列表 75 * @return 会员等级列表
72 */ 76 */
73 List<MemberLevelDO> getLevelListByStatus(Integer status); 77 List<MemberLevelDO> getLevelListByStatus(Integer status);
  78 +
  79 +
  80 + /**
  81 + * 获得开启状态的会员等级列表
  82 + *
  83 + * @return 会员等级列表
  84 + */
  85 + default List<MemberLevelDO> getEnableLevelList() {
  86 + return getLevelListByStatus(CommonStatusEnum.ENABLE.getStatus());
  87 + }
  88 +
  89 + /**
  90 + * 修改会员的等级
  91 + *
  92 + * @param user 会员
  93 + * @param levelId 要修改的等级编号,编号为空时,代表取消会员的等级
  94 + * @param levelReason 修改原因
  95 + */
  96 + void updateUserLevel(MemberUserDO user, @Nullable Long levelId, String levelReason);
  97 +
  98 + /**
  99 + * 增加会员经验
  100 + *
  101 + * @param userId 会员ID
  102 + * @param experience 经验
  103 + * @param bizType 业务类型
  104 + * @param bizId 业务编号
  105 + */
  106 + void plusExperience(Long userId, Integer experience, MemberExperienceBizTypeEnum bizType, String bizId);
74 } 107 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelServiceImpl.java
1 package cn.iocoder.yudao.module.member.service.level; 1 package cn.iocoder.yudao.module.member.service.level;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.NumberUtil;
3 import cn.hutool.core.util.ObjUtil; 5 import cn.hutool.core.util.ObjUtil;
  6 +import cn.hutool.core.util.ObjectUtil;
4 import cn.hutool.core.util.StrUtil; 7 import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelCreateReqVO; 9 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 @@ -8,14 +11,19 @@ import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelPageR
8 import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelUpdateReqVO; 11 import cn.iocoder.yudao.module.member.controller.admin.level.vo.MemberLevelUpdateReqVO;
9 import cn.iocoder.yudao.module.member.convert.level.MemberLevelConvert; 12 import cn.iocoder.yudao.module.member.convert.level.MemberLevelConvert;
10 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO; 13 import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO;
  14 +import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
11 import cn.iocoder.yudao.module.member.dal.mysql.level.MemberLevelMapper; 15 import cn.iocoder.yudao.module.member.dal.mysql.level.MemberLevelMapper;
  16 +import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
  17 +import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;
12 import com.google.common.annotations.VisibleForTesting; 18 import com.google.common.annotations.VisibleForTesting;
13 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
14 import org.springframework.stereotype.Service; 20 import org.springframework.stereotype.Service;
  21 +import org.springframework.transaction.annotation.Transactional;
15 import org.springframework.validation.annotation.Validated; 22 import org.springframework.validation.annotation.Validated;
16 23
17 import javax.annotation.Resource; 24 import javax.annotation.Resource;
18 import java.util.Collection; 25 import java.util.Collection;
  26 +import java.util.Comparator;
19 import java.util.List; 27 import java.util.List;
20 28
21 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 29 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -33,6 +41,12 @@ public class MemberLevelServiceImpl implements MemberLevelService { @@ -33,6 +41,12 @@ public class MemberLevelServiceImpl implements MemberLevelService {
33 41
34 @Resource 42 @Resource
35 private MemberLevelMapper levelMapper; 43 private MemberLevelMapper levelMapper;
  44 + @Resource
  45 + private MemberLevelLogService memberLevelLogService;
  46 + @Resource
  47 + private MemberExperienceLogService memberExperienceLogService;
  48 + @Resource
  49 + private MemberUserMapper memberUserMapper;
36 50
37 @Override 51 @Override
38 public Long createLevel(MemberLevelCreateReqVO createReqVO) { 52 public Long createLevel(MemberLevelCreateReqVO createReqVO) {
@@ -153,4 +167,113 @@ public class MemberLevelServiceImpl implements MemberLevelService { @@ -153,4 +167,113 @@ public class MemberLevelServiceImpl implements MemberLevelService {
153 public List<MemberLevelDO> getLevelListByStatus(Integer status) { 167 public List<MemberLevelDO> getLevelListByStatus(Integer status) {
154 return levelMapper.selectListByStatus(status); 168 return levelMapper.selectListByStatus(status);
155 } 169 }
  170 +
  171 + @Transactional(rollbackFor = Exception.class)
  172 + @Override
  173 + public void updateUserLevel(MemberUserDO user, Long levelId, String reason) {
  174 + // 未调整的情况1
  175 + if (user.getLevelId() == null && levelId == null) {
  176 + return;
  177 + }
  178 + // 未调整的情况2
  179 + if (ObjUtil.equal(user.getLevelId(), levelId)) {
  180 + return;
  181 + }
  182 +
  183 + // 需要后台用户填写为什么调整会员的等级
  184 + if (StrUtil.isBlank(reason)) {
  185 + throw exception(LEVEL_REASON_NOT_EXISTS);
  186 + }
  187 +
  188 + int experience;
  189 + int totalExperience = 0;
  190 + // 记录等级变动
  191 + if (levelId == null) {
  192 + experience = -user.getExperience();
  193 +
  194 + // 取消了会员的等级
  195 + memberLevelLogService.createCancelLog(user.getId(), reason);
  196 + memberUserMapper.cancelUserLevel(user.getId());
  197 + } else {
  198 + MemberLevelDO level = validateLevelExists(levelId);
  199 + // 变动经验值 = 等级的升级经验 - 会员当前的经验;正数为增加经验,负数为扣减经验
  200 + experience = level.getExperience() - user.getExperience();
  201 + // 会员当前的经验 = 等级的升级经验
  202 + totalExperience = level.getExperience();
  203 +
  204 + memberLevelLogService.createAdjustLog(user, level, experience, reason);
  205 +
  206 + // 更新会员表上的等级编号、经验值
  207 + updateUserLevelIdAndExperience(user.getId(), levelId, totalExperience);
  208 + }
  209 +
  210 +
  211 + // 记录会员经验变动
  212 + memberExperienceLogService.createAdjustLog(user.getId(), experience, totalExperience);
  213 + }
  214 +
  215 + @Transactional(rollbackFor = Exception.class)
  216 + @Override
  217 + public void plusExperience(Long userId, Integer experience, MemberExperienceBizTypeEnum bizType, String bizId) {
  218 + if (experience == 0) {
  219 + return;
  220 + }
  221 +
  222 + MemberUserDO user = memberUserMapper.selectById(userId);
  223 + if (user.getExperience() == null) {
  224 + user.setExperience(0);
  225 + }
  226 +
  227 + // 防止扣出负数
  228 + int userExperience = NumberUtil.max(user.getExperience() + experience, 0);
  229 +
  230 + // 创建经验记录
  231 + memberExperienceLogService.createBizLog(userId, experience, userExperience, bizType, bizId);
  232 +
  233 + // 计算会员等级
  234 + Long levelId = calcLevel(user, userExperience);
  235 +
  236 + // 更新会员表上的等级编号、经验值
  237 + updateUserLevelIdAndExperience(user.getId(), levelId, userExperience);
  238 + }
  239 +
  240 + private void updateUserLevelIdAndExperience(Long userId, Long levelId, Integer experience) {
  241 + memberUserMapper.updateById(new MemberUserDO()
  242 + .setId(userId)
  243 + .setLevelId(levelId).setExperience(experience)
  244 + );
  245 + }
  246 +
  247 + /**
  248 + * 计算会员等级
  249 + *
  250 + * @param user 会员
  251 + * @param userExperience 会员当前的经验值
  252 + * @return 会员等级编号,null表示无变化
  253 + */
  254 + private Long calcLevel(MemberUserDO user, int userExperience) {
  255 + List<MemberLevelDO> list = getEnableLevelList();
  256 + if (CollUtil.isEmpty(list)) {
  257 + log.warn("计算会员等级失败:会员等级配置不存在");
  258 + return null;
  259 + }
  260 +
  261 + MemberLevelDO matchLevel = list.stream()
  262 + .filter(level -> userExperience >= level.getExperience())
  263 + .max(Comparator.nullsFirst(Comparator.comparing(MemberLevelDO::getLevel)))
  264 + .orElse(null);
  265 + if (matchLevel == null) {
  266 + log.warn("计算会员等级失败:未找到会员{}经验{}对应的等级配置", user.getId(), userExperience);
  267 + return null;
  268 + }
  269 +
  270 + // 等级没有变化
  271 + if (ObjectUtil.equal(matchLevel.getId(), user.getLevelId())) {
  272 + return null;
  273 + }
  274 +
  275 + // 保存等级变更记录
  276 + memberLevelLogService.createAutoUpgradeLog(user, matchLevel);
  277 + return matchLevel.getId();
  278 + }
156 } 279 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java
@@ -8,13 +8,14 @@ import cn.iocoder.yudao.module.infra.api.file.FileApi; @@ -8,13 +8,14 @@ import cn.iocoder.yudao.module.infra.api.file.FileApi;
8 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO; 8 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO;
9 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO; 9 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO;
10 import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserResetPasswordReqVO; 10 import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserResetPasswordReqVO;
  11 +import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateMobileReqVO;
11 import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdatePasswordReqVO; 12 import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdatePasswordReqVO;
12 import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateReqVO; 13 import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateReqVO;
13 -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateMobileReqVO;  
14 import cn.iocoder.yudao.module.member.convert.auth.AuthConvert; 14 import cn.iocoder.yudao.module.member.convert.auth.AuthConvert;
15 import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert; 15 import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert;
16 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; 16 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
17 import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper; 17 import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
  18 +import cn.iocoder.yudao.module.member.service.level.MemberLevelService;
18 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; 19 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
19 import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; 20 import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
20 import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; 21 import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
@@ -55,6 +56,9 @@ public class MemberUserServiceImpl implements MemberUserService { @@ -55,6 +56,9 @@ public class MemberUserServiceImpl implements MemberUserService {
55 @Resource 56 @Resource
56 private PasswordEncoder passwordEncoder; 57 private PasswordEncoder passwordEncoder;
57 58
  59 + @Resource
  60 + private MemberLevelService memberLevelService;
  61 +
58 @Override 62 @Override
59 public MemberUserDO getUserByMobile(String mobile) { 63 public MemberUserDO getUserByMobile(String mobile) {
60 return memberUserMapper.selectByMobile(mobile); 64 return memberUserMapper.selectByMobile(mobile);
@@ -180,16 +184,20 @@ public class MemberUserServiceImpl implements MemberUserService { @@ -180,16 +184,20 @@ public class MemberUserServiceImpl implements MemberUserService {
180 return passwordEncoder.encode(password); 184 return passwordEncoder.encode(password);
181 } 185 }
182 186
  187 + @Transactional(rollbackFor = Exception.class)
183 @Override 188 @Override
184 public void updateUser(MemberUserUpdateReqVO updateReqVO) { 189 public void updateUser(MemberUserUpdateReqVO updateReqVO) {
185 // 校验存在 190 // 校验存在
186 - validateUserExists(updateReqVO.getId()); 191 + MemberUserDO user = validateUserExists(updateReqVO.getId());
187 // 校验手机唯一 192 // 校验手机唯一
188 validateMobileUnique(updateReqVO.getId(), updateReqVO.getMobile()); 193 validateMobileUnique(updateReqVO.getId(), updateReqVO.getMobile());
189 194
190 // 更新 195 // 更新
191 MemberUserDO updateObj = MemberUserConvert.INSTANCE.convert(updateReqVO); 196 MemberUserDO updateObj = MemberUserConvert.INSTANCE.convert(updateReqVO);
192 memberUserMapper.updateById(updateObj); 197 memberUserMapper.updateById(updateObj);
  198 +
  199 + // 会员级别修改
  200 + memberLevelService.updateUserLevel(user, updateReqVO.getLevelId(), updateReqVO.getLevelReason());
193 } 201 }
194 202
195 @VisibleForTesting 203 @VisibleForTesting