diff --git a/pom.xml b/pom.xml index 17601bfea..65000e402 100644 --- a/pom.xml +++ b/pom.xml @@ -12,10 +12,10 @@ yudao-gateway yudao-framework - - yudao-module-bpm yudao-module-system yudao-module-infra + yudao-module-member + yudao-module-bpm yudao-module-pay yudao-module-report yudao-module-mp diff --git a/yudao-module-member/pom.xml b/yudao-module-member/pom.xml new file mode 100644 index 000000000..f481f6b26 --- /dev/null +++ b/yudao-module-member/pom.xml @@ -0,0 +1,24 @@ + + + + cn.iocoder.cloud + yudao + ${revision} + + 4.0.0 + + yudao-module-member-api + yudao-module-member-biz + + yudao-module-member + pom + + ${project.artifactId} + + member 模块,我们放会员业务。 + 例如说:会员中心等等 + + + diff --git a/yudao-module-member/yudao-module-member-api/pom.xml b/yudao-module-member/yudao-module-member-api/pom.xml new file mode 100644 index 000000000..242df88f5 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/pom.xml @@ -0,0 +1,33 @@ + + + + cn.iocoder.cloud + yudao-module-member + ${revision} + + 4.0.0 + yudao-module-member-api + jar + + ${project.artifactId} + + member 模块 API,暴露给其它模块调用 + + + + + cn.iocoder.cloud + yudao-common + + + + + org.springframework.boot + spring-boot-starter-validation + true + + + + diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApi.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApi.java new file mode 100644 index 000000000..658748819 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApi.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.member.api.address; + +import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; + +/** + * 用户收件地址 API 接口 + * + * @author 芋道源码 + */ +public interface AddressApi { + + /** + * 获得用户收件地址 + * + * @param id 收件地址编号 + * @param userId 用户编号 + * @return 用户收件地址 + */ + AddressRespDTO getAddress(Long id, Long userId); + + /** + * 获得用户默认收件地址 + * + * @param userId 用户编号 + * @return 用户收件地址 + */ + AddressRespDTO getDefaultAddress(Long userId); + +} diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/dto/AddressRespDTO.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/dto/AddressRespDTO.java new file mode 100644 index 000000000..5a5e44ff1 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/dto/AddressRespDTO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.member.api.address.dto; + +import lombok.Data; + +/** + * 用户收件地址 Response DTO + * + * @author 芋道源码 + */ +@Data +public class AddressRespDTO { + + /** + * 编号 + */ + private Long id; + /** + * 用户编号 + */ + private Long userId; + /** + * 收件人名称 + */ + private String name; + /** + * 手机号 + */ + private String mobile; + /** + * 地区编号 + */ + private Integer areaId; + /** + * 收件详细地址 + */ + private String detailAddress; + /** + * 是否默认 + */ + private Boolean defaultStatus; + +} diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/config/MemberConfigApi.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/config/MemberConfigApi.java new file mode 100644 index 000000000..dab7f6877 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/config/MemberConfigApi.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.member.api.config; + +import cn.iocoder.yudao.module.member.api.config.dto.MemberConfigRespDTO; + +/** + * 用户配置 API 接口 + * + * @author owen + */ +public interface MemberConfigApi { + + /** + * 获得积分配置 + * + * @return 积分配置 + */ + MemberConfigRespDTO getConfig(); +} diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/config/dto/MemberConfigRespDTO.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/config/dto/MemberConfigRespDTO.java new file mode 100644 index 000000000..59aab53f9 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/config/dto/MemberConfigRespDTO.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.member.api.config.dto; + +import lombok.Data; + +/** + * 用户信息 Response DTO + * + * @author 芋道源码 + */ +@Data +public class MemberConfigRespDTO { + + /** + * 积分抵扣开关 + */ + private Boolean pointTradeDeductEnable; + /** + * 积分抵扣,单位:分 + *

+ * 1 积分抵扣多少分 + */ + private Integer pointTradeDeductUnitPrice; + /** + * 积分抵扣最大值 + */ + private Integer pointTradeDeductMaxPrice; + /** + * 1 元赠送多少分 + */ + private Integer pointTradeGivePoint; + +} diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/level/MemberLevelApi.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/level/MemberLevelApi.java new file mode 100644 index 000000000..587683797 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/level/MemberLevelApi.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.member.api.level; + +import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO; +import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum; + +/** + * 会员等级 API 接口 + * + * @author owen + */ +public interface MemberLevelApi { + + /** + * 获得会员等级 + * + * @param id 会员等级编号 + * @return 会员等级 + */ + MemberLevelRespDTO getMemberLevel(Long id); + + /** + * 增加会员经验 + * + * @param userId 会员ID + * @param experience 经验 + * @param bizType 业务类型 {@link MemberExperienceBizTypeEnum} + * @param bizId 业务编号 + */ + void addExperience(Long userId, Integer experience, Integer bizType, String bizId); + + /** + * 扣减会员经验 + * + * @param userId 会员ID + * @param experience 经验 + * @param bizType 业务类型 {@link MemberExperienceBizTypeEnum} + * @param bizId 业务编号 + */ + void reduceExperience(Long userId, Integer experience, Integer bizType, String bizId); + +} diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/level/dto/MemberLevelRespDTO.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/level/dto/MemberLevelRespDTO.java new file mode 100644 index 000000000..a72d65f23 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/level/dto/MemberLevelRespDTO.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.member.api.level.dto; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import lombok.Data; + +/** + * 会员等级 Resp DTO + * + * @author 芋道源码 + */ +@Data +public class MemberLevelRespDTO { + + /** + * 编号 + */ + private Long id; + /** + * 等级名称 + */ + private String name; + /** + * 等级 + */ + private Integer level; + /** + * 升级经验 + */ + private Integer experience; + /** + * 享受折扣 + */ + private Integer discountPercent; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + +} diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/package-info.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/package-info.java new file mode 100644 index 000000000..56cd9857f --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/package-info.java @@ -0,0 +1,4 @@ +/** + * member API 包,定义暴露给其它模块的 API + */ +package cn.iocoder.yudao.module.member.api; diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApi.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApi.java new file mode 100644 index 000000000..3eb749fb6 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApi.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.member.api.point; + +import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum; + +import javax.validation.constraints.Min; + +/** + * 用户积分的 API 接口 + * + * @author owen + */ +public interface MemberPointApi { + + /** + * 增加用户积分 + * + * @param userId 用户编号 + * @param point 积分 + * @param bizType 业务类型 {@link MemberPointBizTypeEnum} + * @param bizId 业务编号 + */ + void addPoint(Long userId, @Min(value = 1L, message = "积分必须是正数") Integer point, + Integer bizType, String bizId); + + /** + * 减少用户积分 + * + * @param userId 用户编号 + * @param point 积分 + * @param bizType 业务类型 {@link MemberPointBizTypeEnum} + * @param bizId 业务编号 + */ + void reducePoint(Long userId, @Min(value = 1L, message = "积分必须是正数") Integer point, + Integer bizType, String bizId); + +} diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApi.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApi.java new file mode 100644 index 000000000..3d2130e18 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApi.java @@ -0,0 +1,59 @@ +package cn.iocoder.yudao.module.member.api.user; + +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * 会员用户的 API 接口 + * + * @author 芋道源码 + */ +public interface MemberUserApi { + + /** + * 获得会员用户信息 + * + * @param id 用户编号 + * @return 用户信息 + */ + MemberUserRespDTO getUser(Long id); + + /** + * 获得会员用户信息们 + * + * @param ids 用户编号的数组 + * @return 用户信息们 + */ + List getUserList(Collection ids); + + /** + * 获得会员用户 Map + * + * @param ids 用户编号的数组 + * @return 会员用户 Map + */ + default Map getUserMap(Collection ids) { + return convertMap(getUserList(ids), MemberUserRespDTO::getId); + } + + /** + * 基于用户昵称,模糊匹配用户列表 + * + * @param nickname 用户昵称,模糊匹配 + * @return 用户信息的列表 + */ + List getUserListByNickname(String nickname); + + /** + * 基于手机号,精准匹配用户 + * + * @param mobile 手机号 + * @return 用户信息 + */ + MemberUserRespDTO getUserByMobile(String mobile); +} diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/MemberUserRespDTO.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/MemberUserRespDTO.java new file mode 100644 index 000000000..50548f935 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/MemberUserRespDTO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.member.api.user.dto; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 用户信息 Response DTO + * + * @author 芋道源码 + */ +@Data +public class MemberUserRespDTO { + + /** + * 用户ID + */ + private Long id; + /** + * 用户昵称 + */ + private String nickname; + /** + * 帐号状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 用户头像 + */ + private String avatar; + /** + * 手机 + */ + private String mobile; + /** + * 创建时间(注册时间) + */ + private LocalDateTime createTime; + + // ========== 其它信息 ========== + + /** + * 会员级别编号 + */ + private Long levelId; + + /** + * 积分 + */ + private Integer point; + +} diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/DictTypeConstants.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/DictTypeConstants.java new file mode 100644 index 000000000..c87cbb901 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/DictTypeConstants.java @@ -0,0 +1,15 @@ +package cn.iocoder.yudao.module.member.enums; + +/** + * Member 字典类型的枚举类 + * + * @author owen + */ +public interface DictTypeConstants { + + /** + * 会员经验记录 - 业务类型 + */ + String MEMBER_EXPERIENCE_BIZ_TYPE = "member_experience_biz_type"; + +} diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java new file mode 100644 index 000000000..c7dc8b749 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.member.enums; + +import cn.iocoder.yudao.framework.common.exception.ErrorCode; + +/** + * Member 错误码枚举类 + *

+ * member 系统,使用 1-004-000-000 段 + */ +public interface ErrorCodeConstants { + + // ========== 用户相关 1-004-001-000 ============ + ErrorCode USER_NOT_EXISTS = new ErrorCode(1_004_001_000, "用户不存在"); + ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_004_001_001, "手机号未注册用户"); + ErrorCode USER_MOBILE_USED = new ErrorCode(1_004_001_002, "修改手机失败,该手机号({})已经被使用"); + ErrorCode USER_POINT_NOT_ENOUGH = new ErrorCode(1_004_001_003, "用户积分余额不足"); + + // ========== AUTH 模块 1-004-003-000 ========== + ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1_004_003_000, "登录失败,账号密码不正确"); + ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1_004_003_001, "登录失败,账号被禁用"); + ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1_004_003_005, "未绑定账号,需要进行绑定"); + ErrorCode AUTH_MOBILE_USED = new ErrorCode(1_004_003_007, "手机号已经被使用"); + + // ========== 用户收件地址 1-004-004-000 ========== + ErrorCode ADDRESS_NOT_EXISTS = new ErrorCode(1_004_004_000, "用户收件地址不存在"); + + //========== 用户标签 1-004-006-000 ========== + ErrorCode TAG_NOT_EXISTS = new ErrorCode(1_004_006_000, "用户标签不存在"); + ErrorCode TAG_NAME_EXISTS = new ErrorCode(1_004_006_001, "用户标签已经存在"); + ErrorCode TAG_HAS_USER = new ErrorCode(1_004_006_002, "用户标签下存在用户,无法删除"); + + //========== 积分配置 1-004-007-000 ========== + + //========== 积分记录 1-004-008-000 ========== + ErrorCode POINT_RECORD_BIZ_NOT_SUPPORT = new ErrorCode(1_004_008_000, "用户积分记录业务类型不支持"); + + //========== 签到配置 1-004-009-000 ========== + ErrorCode SIGN_IN_CONFIG_NOT_EXISTS = new ErrorCode(1_004_009_000, "签到天数规则不存在"); + ErrorCode SIGN_IN_CONFIG_EXISTS = new ErrorCode(1_004_009_001, "签到天数规则已存在"); + + //========== 签到配置 1-004-010-000 ========== + ErrorCode SIGN_IN_RECORD_TODAY_EXISTS = new ErrorCode(1_004_010_000, "今日已签到,请勿重复签到"); + + //========== 用户等级 1-004-011-000 ========== + ErrorCode LEVEL_NOT_EXISTS = new ErrorCode(1_004_011_000, "用户等级不存在"); + ErrorCode LEVEL_NAME_EXISTS = new ErrorCode(1_004_011_001, "用户等级名称[{}]已被使用"); + ErrorCode LEVEL_VALUE_EXISTS = new ErrorCode(1_004_011_002, "用户等级值[{}]已被[{}]使用"); + ErrorCode LEVEL_EXPERIENCE_MIN = new ErrorCode(1_004_011_003, "升级经验必须大于上一个等级[{}]设置的升级经验[{}]"); + ErrorCode LEVEL_EXPERIENCE_MAX = new ErrorCode(1_004_011_004, "升级经验必须小于下一个等级[{}]设置的升级经验[{}]"); + ErrorCode LEVEL_HAS_USER = new ErrorCode(1_004_011_005, "用户等级下存在用户,无法删除"); + + ErrorCode EXPERIENCE_BIZ_NOT_SUPPORT = new ErrorCode(1_004_011_201, "用户经验业务类型不支持"); + + //========== 用户分组 1-004-012-000 ========== + ErrorCode GROUP_NOT_EXISTS = new ErrorCode(1_004_012_000, "用户分组不存在"); + ErrorCode GROUP_HAS_USER = new ErrorCode(1_004_012_001, "用户分组下存在用户,无法删除"); + +} diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/MemberExperienceBizTypeEnum.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/MemberExperienceBizTypeEnum.java new file mode 100644 index 000000000..3038dba31 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/MemberExperienceBizTypeEnum.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.member.enums; + +import cn.hutool.core.util.EnumUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Objects; + +/** + * 会员经验 - 业务类型 + * + * @author owen + */ +@Getter +@AllArgsConstructor +public enum MemberExperienceBizTypeEnum { + + /** + * 管理员调整、邀请新用户、下单、退单、签到、抽奖 + */ + ADMIN(0, "管理员调整", "管理员调整获得 {} 经验", true), + INVITE_REGISTER(1, "邀新奖励", "邀请好友获得 {} 经验", true), + SIGN_IN(4, "签到奖励", "签到获得 {} 经验", true), + LOTTERY(5, "抽奖奖励", "抽奖获得 {} 经验", true), + ORDER_GIVE(11, "下单奖励", "下单获得 {} 经验", true), + ORDER_GIVE_CANCEL(12, "下单奖励(整单取消)", "取消订单获得 {} 经验", false), // ORDER_GIVE 的取消 + ORDER_GIVE_CANCEL_ITEM(13, "下单奖励(单个退款)", "退款订单获得 {} 经验", false), // ORDER_GIVE 的取消 + ; + + /** + * 业务类型 + */ + private final int type; + /** + * 标题 + */ + private final String title; + /** + * 描述 + */ + private final String description; + /** + * 是否为扣减积分 + */ + private final boolean add; + + public static MemberExperienceBizTypeEnum getByType(Integer type) { + return EnumUtil.getBy(MemberExperienceBizTypeEnum.class, + e -> Objects.equals(type, e.getType())); + } +} diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/point/MemberPointBizTypeEnum.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/point/MemberPointBizTypeEnum.java new file mode 100644 index 000000000..ef491f42a --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/point/MemberPointBizTypeEnum.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.member.enums.point; + +import cn.hutool.core.util.EnumUtil; +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Objects; + +/** + * 会员积分的业务类型枚举 + * + * @author 芋道源码 + */ +@AllArgsConstructor +@Getter +public enum MemberPointBizTypeEnum implements IntArrayValuable { + + SIGN(1, "签到", "签到获得 {} 积分", true), + ADMIN(2, "管理员修改", "管理员修改 {} 积分", true), + + ORDER_USE(11, "订单积分抵扣", "下单使用 {} 积分", false), // 下单时,扣减积分 + ORDER_USE_CANCEL(12, "订单积分抵扣(整单取消)", "订单取消,退还 {} 积分", true), // ORDER_USE 的取消 + ORDER_USE_CANCEL_ITEM(13, "订单积分抵扣(单个退款)", "订单退款,退还 {} 积分", true), // ORDER_USE 的取消 + + ORDER_GIVE(21, "订单积分奖励", "下单获得 {} 积分", true), // 支付订单时,赠送积分 + ORDER_GIVE_CANCEL(22, "订单积分奖励(整单取消)", "订单取消,退还 {} 积分", false), // ORDER_GIVE 的取消 + ORDER_GIVE_CANCEL_ITEM(23, "订单积分奖励(单个退款)", "订单退款,扣除赠送的 {} 积分", false) // ORDER_GIVE 的取消 + ; + + /** + * 类型 + */ + private final Integer type; + /** + * 名字 + */ + private final String name; + /** + * 描述 + */ + private final String description; + /** + * 是否为扣减积分 + */ + private final boolean add; + + @Override + public int[] array() { + return new int[0]; + } + + public static MemberPointBizTypeEnum getByType(Integer type) { + return EnumUtil.getBy(MemberPointBizTypeEnum.class, + e -> Objects.equals(type, e.getType())); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/pom.xml b/yudao-module-member/yudao-module-member-biz/pom.xml new file mode 100644 index 000000000..2d06e9c34 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/pom.xml @@ -0,0 +1,95 @@ + + + + cn.iocoder.cloud + yudao-module-member + ${revision} + + 4.0.0 + yudao-module-member-biz + jar + + ${project.artifactId} + + member 模块,我们放会员业务。 + 例如说:会员中心等等 + + + + + cn.iocoder.cloud + yudao-module-member-api + ${revision} + + + cn.iocoder.cloud + yudao-module-system-api + ${revision} + + + cn.iocoder.cloud + yudao-module-infra-api + ${revision} + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-operatelog + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-tenant + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-validation + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-mybatis + + + + cn.iocoder.cloud + yudao-spring-boot-starter-redis + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-mq + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-test + test + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-excel + + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-ip + + + + + diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApiImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApiImpl.java new file mode 100644 index 000000000..b8088a455 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApiImpl.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.member.api.address; + +import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; +import cn.iocoder.yudao.module.member.convert.address.AddressConvert; +import cn.iocoder.yudao.module.member.service.address.AddressService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; + +/** + * 用户收件地址 API 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class AddressApiImpl implements AddressApi { + + @Resource + private AddressService addressService; + + @Override + public AddressRespDTO getAddress(Long id, Long userId) { + return AddressConvert.INSTANCE.convert02(addressService.getAddress(userId, id)); + } + + @Override + public AddressRespDTO getDefaultAddress(Long userId) { + return AddressConvert.INSTANCE.convert02(addressService.getDefaultUserAddress(userId)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/config/MemberConfigApiImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/config/MemberConfigApiImpl.java new file mode 100644 index 000000000..510f4fff7 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/config/MemberConfigApiImpl.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.member.api.config; + +import cn.iocoder.yudao.module.member.api.config.dto.MemberConfigRespDTO; +import cn.iocoder.yudao.module.member.convert.config.MemberConfigConvert; +import cn.iocoder.yudao.module.member.service.config.MemberConfigService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; + +/** + * 用户配置 API 实现类 + * + * @author owen + */ +@Service +@Validated +public class MemberConfigApiImpl implements MemberConfigApi { + + @Resource + private MemberConfigService memberConfigService; + + @Override + public MemberConfigRespDTO getConfig() { + return MemberConfigConvert.INSTANCE.convert01(memberConfigService.getConfig()); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/level/MemberLevelApiImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/level/MemberLevelApiImpl.java new file mode 100644 index 000000000..79fed98eb --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/level/MemberLevelApiImpl.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.member.api.level; + +import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO; +import cn.iocoder.yudao.module.member.convert.level.MemberLevelConvert; +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; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.EXPERIENCE_BIZ_NOT_SUPPORT; + +/** + * 会员等级 API 实现类 + * + * @author owen + */ +@Service +@Validated +public class MemberLevelApiImpl implements MemberLevelApi { + + @Resource + private MemberLevelService memberLevelService; + + @Override + public MemberLevelRespDTO getMemberLevel(Long id) { + return MemberLevelConvert.INSTANCE.convert02(memberLevelService.getLevel(id)); + } + + @Override + public void addExperience(Long userId, Integer experience, Integer bizType, String bizId) { + MemberExperienceBizTypeEnum bizTypeEnum = MemberExperienceBizTypeEnum.getByType(bizType); + if (bizTypeEnum == null) { + throw exception(EXPERIENCE_BIZ_NOT_SUPPORT); + } + memberLevelService.addExperience(userId, experience, bizTypeEnum, bizId); + } + + @Override + public void reduceExperience(Long userId, Integer experience, Integer bizType, String bizId) { + addExperience(userId, -experience, bizType, bizId); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/package-info.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/package-info.java new file mode 100644 index 000000000..5f97979b8 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.member.api; diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApiImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApiImpl.java new file mode 100644 index 000000000..6e21e8546 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApiImpl.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.member.api.point; + +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum; +import cn.iocoder.yudao.module.member.service.point.MemberPointRecordService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.POINT_RECORD_BIZ_NOT_SUPPORT; + +/** + * 用户积分的 API 实现类 + * + * @author owen + */ +@Service +@Validated +public class MemberPointApiImpl implements MemberPointApi { + + @Resource + private MemberPointRecordService memberPointRecordService; + + @Override + public void addPoint(Long userId, Integer point, Integer bizType, String bizId) { + Assert.isTrue(point > 0); + MemberPointBizTypeEnum bizTypeEnum = MemberPointBizTypeEnum.getByType(bizType); + if (bizTypeEnum == null) { + throw exception(POINT_RECORD_BIZ_NOT_SUPPORT); + } + memberPointRecordService.createPointRecord(userId, point, bizTypeEnum, bizId); + } + + @Override + public void reducePoint(Long userId, Integer point, Integer bizType, String bizId) { + Assert.isTrue(point > 0); + MemberPointBizTypeEnum bizTypeEnum = MemberPointBizTypeEnum.getByType(bizType); + if (bizTypeEnum == null) { + throw exception(POINT_RECORD_BIZ_NOT_SUPPORT); + } + memberPointRecordService.createPointRecord(userId, -point, bizTypeEnum, bizId); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApiImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApiImpl.java new file mode 100644 index 000000000..8da857c6d --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApiImpl.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.member.api.user; + +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +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.service.user.MemberUserService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.Collection; +import java.util.List; + +/** + * 会员用户的 API 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class MemberUserApiImpl implements MemberUserApi { + + @Resource + private MemberUserService userService; + + @Override + public MemberUserRespDTO getUser(Long id) { + MemberUserDO user = userService.getUser(id); + return MemberUserConvert.INSTANCE.convert2(user); + } + + @Override + public List getUserList(Collection ids) { + return MemberUserConvert.INSTANCE.convertList2(userService.getUserList(ids)); + } + + @Override + public List getUserListByNickname(String nickname) { + return MemberUserConvert.INSTANCE.convertList2(userService.getUserListByNickname(nickname)); + } + + @Override + public MemberUserRespDTO getUserByMobile(String mobile) { + return MemberUserConvert.INSTANCE.convert2(userService.getUserByMobile(mobile)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/address/AddressController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/address/AddressController.java new file mode 100644 index 000000000..0363634c5 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/address/AddressController.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.member.controller.admin.address; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.member.controller.admin.address.vo.AddressRespVO; +import cn.iocoder.yudao.module.member.convert.address.AddressConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.address.MemberAddressDO; +import cn.iocoder.yudao.module.member.service.address.AddressService; +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.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 用户收件地址") +@RestController +@RequestMapping("/member/address") +@Validated +public class AddressController { + + @Resource + private AddressService addressService; + + @GetMapping("/list") + @Operation(summary = "获得用户收件地址列表") + @Parameter(name = "userId", description = "用户编号", required = true) + @PreAuthorize("@ss.hasPermission('member:user:query')") + public CommonResult> getAddressList(@RequestParam("userId") Long userId) { + List list = addressService.getAddressList(userId); + return success(AddressConvert.INSTANCE.convertList2(list)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/address/package-info.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/address/package-info.java new file mode 100644 index 000000000..652bbb6f1 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/address/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.member.controller.admin.address; diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/address/vo/AddressBaseVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/address/vo/AddressBaseVO.java new file mode 100644 index 000000000..5fa2d1ed9 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/address/vo/AddressBaseVO.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.member.controller.admin.address.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import java.util.*; +import javax.validation.constraints.*; + +/** + * 用户收件地址 Base VO,提供给添加、修改、详细的子 VO 使用 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 + */ +@Data +public class AddressBaseVO { + + @Schema(description = "收件人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") + @NotNull(message = "收件人名称不能为空") + private String name; + + @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "手机号不能为空") + private String mobile; + + @Schema(description = "地区编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "15716") + @NotNull(message = "地区编码不能为空") + private Long areaId; + + @Schema(description = "收件详细地址", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "收件详细地址不能为空") + private String detailAddress; + + @Schema(description = "是否默认", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "是否默认不能为空") + private Boolean defaultStatus; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/address/vo/AddressRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/address/vo/AddressRespVO.java new file mode 100644 index 000000000..26a4988af --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/address/vo/AddressRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.member.controller.admin.address.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 用户收件地址 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class AddressRespVO extends AddressBaseVO { + + @Schema(description = "收件地址编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "7380") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/config/MemberConfigController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/config/MemberConfigController.java new file mode 100644 index 000000000..730358f9b --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/config/MemberConfigController.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.member.controller.admin.config; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.member.controller.admin.config.vo.MemberConfigRespVO; +import cn.iocoder.yudao.module.member.controller.admin.config.vo.MemberConfigSaveReqVO; +import cn.iocoder.yudao.module.member.convert.config.MemberConfigConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.config.MemberConfigDO; +import cn.iocoder.yudao.module.member.service.config.MemberConfigService; +import io.swagger.v3.oas.annotations.Operation; +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 static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 会员设置") +@RestController +@RequestMapping("/member/config") +@Validated +public class MemberConfigController { + + @Resource + private MemberConfigService memberConfigService; + + @PutMapping("/save") + @Operation(summary = "保存会员配置") + @PreAuthorize("@ss.hasPermission('member:config:save')") + public CommonResult saveConfig(@Valid @RequestBody MemberConfigSaveReqVO saveReqVO) { + memberConfigService.saveConfig(saveReqVO); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得会员配置") + @PreAuthorize("@ss.hasPermission('member:config:query')") + public CommonResult getConfig() { + MemberConfigDO config = memberConfigService.getConfig(); + return success(MemberConfigConvert.INSTANCE.convert(config)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/config/vo/MemberConfigBaseVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/config/vo/MemberConfigBaseVO.java new file mode 100644 index 000000000..a9a6b3195 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/config/vo/MemberConfigBaseVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.member.controller.admin.config.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 MemberConfigBaseVO { + + @Schema(description = "积分抵扣开关", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "积分抵扣开发不能为空") + private Boolean pointTradeDeductEnable; + + @Schema(description = "积分抵扣,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "13506") + @NotNull(message = "积分抵扣不能为空") + private Integer pointTradeDeductUnitPrice; + + @Schema(description = "积分抵扣最大值", requiredMode = Schema.RequiredMode.REQUIRED, example = "32428") + @NotNull(message = "积分抵扣最大值不能为空") + private Integer pointTradeDeductMaxPrice; + + @Schema(description = "1 元赠送多少分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + @NotNull(message = "1 元赠送积分不能为空") + private Integer pointTradeGivePoint; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/config/vo/MemberConfigRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/config/vo/MemberConfigRespVO.java new file mode 100644 index 000000000..04f14f3d1 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/config/vo/MemberConfigRespVO.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.member.controller.admin.config.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - 会员配置 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MemberConfigRespVO extends MemberConfigBaseVO { + + @Schema(description = "自增主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/config/vo/MemberConfigSaveReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/config/vo/MemberConfigSaveReqVO.java new file mode 100644 index 000000000..8348f1f3c --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/config/vo/MemberConfigSaveReqVO.java @@ -0,0 +1,13 @@ +package cn.iocoder.yudao.module.member.controller.admin.config.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 MemberConfigSaveReqVO extends MemberConfigBaseVO { +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/MemberGroupController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/MemberGroupController.java new file mode 100644 index 000000000..566e516a1 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/MemberGroupController.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.member.controller.admin.group; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.controller.admin.group.vo.*; +import cn.iocoder.yudao.module.member.convert.group.MemberGroupConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO; +import cn.iocoder.yudao.module.member.service.group.MemberGroupService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + + +@Tag(name = "管理后台 - 用户分组") +@RestController +@RequestMapping("/member/group") +@Validated +public class MemberGroupController { + + @Resource + private MemberGroupService groupService; + + @PostMapping("/create") + @Operation(summary = "创建用户分组") + @PreAuthorize("@ss.hasPermission('member:group:create')") + public CommonResult createGroup(@Valid @RequestBody MemberGroupCreateReqVO createReqVO) { + return success(groupService.createGroup(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新用户分组") + @PreAuthorize("@ss.hasPermission('member:group:update')") + public CommonResult 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 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 getGroup(@RequestParam("id") Long id) { + MemberGroupDO group = groupService.getGroup(id); + return success(MemberGroupConvert.INSTANCE.convert(group)); + } + + @GetMapping("/list-all-simple") + @Operation(summary = "获取会员分组精简信息列表", description = "只包含被开启的会员分组,主要用于前端的下拉选项") + public CommonResult> getSimpleGroupList() { + // 获用户列表,只要开启状态的 + List list = groupService.getEnableGroupList(); + return success(MemberGroupConvert.INSTANCE.convertSimpleList(list)); + } + + @GetMapping("/page") + @Operation(summary = "获得用户分组分页") + @PreAuthorize("@ss.hasPermission('member:group:query')") + public CommonResult> getGroupPage(@Valid MemberGroupPageReqVO pageVO) { + PageResult pageResult = groupService.getGroupPage(pageVO); + return success(MemberGroupConvert.INSTANCE.convertPage(pageResult)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupBaseVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupBaseVO.java new file mode 100644 index 000000000..0519bd968 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupBaseVO.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.member.controller.admin.group.vo; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** + * 用户分组 Base VO,提供给添加、修改、详细的子 VO 使用 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 + */ +@Data +public class MemberGroupBaseVO { + + @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "购物达人") + @NotNull(message = "名称不能为空") + private String name; + + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "你猜") + private String remark; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + @InEnum(CommonStatusEnum.class) + private Integer status; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupCreateReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupCreateReqVO.java new file mode 100644 index 000000000..ef3f83343 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupCreateReqVO.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.member.controller.admin.group.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - 用户分组创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MemberGroupCreateReqVO extends MemberGroupBaseVO { + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupPageReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupPageReqVO.java new file mode 100644 index 000000000..ae67d5f6c --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupPageReqVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.member.controller.admin.group.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 用户分组分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MemberGroupPageReqVO extends PageParam { + + @Schema(description = "名称", example = "购物达人") + private String name; + + @Schema(description = "状态", example = "1") + private Integer status; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupRespVO.java new file mode 100644 index 000000000..97365382a --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupRespVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.member.controller.admin.group.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 用户分组 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MemberGroupRespVO extends MemberGroupBaseVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20357") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupSimpleRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupSimpleRespVO.java new file mode 100644 index 000000000..ee7d905d0 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupSimpleRespVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.member.controller.admin.group.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.ToString; + +@Schema(description = "管理后台 - 用户分组 Response VO") +@Data +@ToString(callSuper = true) +public class MemberGroupSimpleRespVO { + + @Schema(description = "编号", example = "6103") + private Long id; + + @Schema(description = "等级名称", example = "芋艿") + private String name; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupUpdateReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupUpdateReqVO.java new file mode 100644 index 000000000..75910883b --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/group/vo/MemberGroupUpdateReqVO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.member.controller.admin.group.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 用户分组更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MemberGroupUpdateReqVO extends MemberGroupBaseVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20357") + @NotNull(message = "编号不能为空") + private Long id; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/MemberExperienceRecordController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/MemberExperienceRecordController.java new file mode 100644 index 000000000..cdbd76046 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/MemberExperienceRecordController.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.member.controller.admin.level; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceRecordPageReqVO; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceRecordRespVO; +import cn.iocoder.yudao.module.member.convert.level.MemberExperienceRecordConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberExperienceRecordDO; +import cn.iocoder.yudao.module.member.service.level.MemberExperienceRecordService; +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.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.validation.Valid; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 会员经验记录") +@RestController +@RequestMapping("/member/experience-record") +@Validated +public class MemberExperienceRecordController { + + @Resource + private MemberExperienceRecordService experienceLogService; + + @GetMapping("/get") + @Operation(summary = "获得会员经验记录") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('member:experience-record:query')") + public CommonResult getExperienceRecord(@RequestParam("id") Long id) { + MemberExperienceRecordDO experienceLog = experienceLogService.getExperienceRecord(id); + return success(MemberExperienceRecordConvert.INSTANCE.convert(experienceLog)); + } + + @GetMapping("/page") + @Operation(summary = "获得会员经验记录分页") + @PreAuthorize("@ss.hasPermission('member:experience-record:query')") + public CommonResult> getExperienceRecordPage( + @Valid MemberExperienceRecordPageReqVO pageVO) { + PageResult pageResult = experienceLogService.getExperienceRecordPage(pageVO); + return success(MemberExperienceRecordConvert.INSTANCE.convertPage(pageResult)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/MemberLevelController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/MemberLevelController.java new file mode 100644 index 000000000..195800e98 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/MemberLevelController.java @@ -0,0 +1,80 @@ +package cn.iocoder.yudao.module.member.controller.admin.level; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.level.*; +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.service.level.MemberLevelService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 会员等级") +@RestController +@RequestMapping("/member/level") +@Validated +public class MemberLevelController { + + @Resource + private MemberLevelService levelService; + + @PostMapping("/create") + @Operation(summary = "创建会员等级") + @PreAuthorize("@ss.hasPermission('member:level:create')") + public CommonResult createLevel(@Valid @RequestBody MemberLevelCreateReqVO createReqVO) { + return success(levelService.createLevel(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新会员等级") + @PreAuthorize("@ss.hasPermission('member:level:update')") + public CommonResult updateLevel(@Valid @RequestBody MemberLevelUpdateReqVO updateReqVO) { + levelService.updateLevel(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除会员等级") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('member:level:delete')") + public CommonResult deleteLevel(@RequestParam("id") Long id) { + levelService.deleteLevel(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得会员等级") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('member:level:query')") + public CommonResult getLevel(@RequestParam("id") Long id) { + MemberLevelDO level = levelService.getLevel(id); + return success(MemberLevelConvert.INSTANCE.convert(level)); + } + + @GetMapping("/list-all-simple") + @Operation(summary = "获取会员等级精简信息列表", description = "只包含被开启的会员等级,主要用于前端的下拉选项") + public CommonResult> getSimpleLevelList() { + // 获用户列表,只要开启状态的 + List list = levelService.getEnableLevelList(); + // 排序后,返回给前端 + return success(MemberLevelConvert.INSTANCE.convertSimpleList(list)); + } + + @GetMapping("/list") + @Operation(summary = "获得会员等级列表") + @PreAuthorize("@ss.hasPermission('member:level:query')") + public CommonResult> getLevelList(@Valid MemberLevelListReqVO listReqVO) { + List result = levelService.getLevelList(listReqVO); + return success(MemberLevelConvert.INSTANCE.convertList(result)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/MemberLevelRecordController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/MemberLevelRecordController.java new file mode 100644 index 000000000..b54a54da0 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/MemberLevelRecordController.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.member.controller.admin.level; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.record.MemberLevelRecordPageReqVO; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.record.MemberLevelRecordRespVO; +import cn.iocoder.yudao.module.member.convert.level.MemberLevelRecordConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelRecordDO; +import cn.iocoder.yudao.module.member.service.level.MemberLevelRecordService; +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.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.validation.Valid; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 会员等级记录") +@RestController +@RequestMapping("/member/level-record") +@Validated +public class MemberLevelRecordController { + + @Resource + private MemberLevelRecordService levelLogService; + + @GetMapping("/get") + @Operation(summary = "获得会员等级记录") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('member:level-record:query')") + public CommonResult getLevelRecord(@RequestParam("id") Long id) { + MemberLevelRecordDO levelLog = levelLogService.getLevelRecord(id); + return success(MemberLevelRecordConvert.INSTANCE.convert(levelLog)); + } + + @GetMapping("/page") + @Operation(summary = "获得会员等级记录分页") + @PreAuthorize("@ss.hasPermission('member:level-record:query')") + public CommonResult> getLevelRecordPage( + @Valid MemberLevelRecordPageReqVO pageVO) { + PageResult pageResult = levelLogService.getLevelRecordPage(pageVO); + return success(MemberLevelRecordConvert.INSTANCE.convertPage(pageResult)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/experience/MemberExperienceRecordBaseVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/experience/MemberExperienceRecordBaseVO.java new file mode 100644 index 000000000..7c71f8270 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/experience/MemberExperienceRecordBaseVO.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.member.controller.admin.level.vo.experience; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** + * 会员经验记录 Base VO,提供给添加、修改、详细的子 VO 使用 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 + */ +@Data +public class MemberExperienceRecordBaseVO { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3638") + @NotNull(message = "用户编号不能为空") + private Long userId; + + @Schema(description = "业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12164") + @NotNull(message = "业务编号不能为空") + private String bizId; + + @Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "业务类型不能为空") + private Integer bizType; + + @Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "增加经验") + @NotNull(message = "标题不能为空") + private String title; + + @Schema(description = "经验", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + @NotNull(message = "经验不能为空") + private Integer experience; + + @Schema(description = "变更后的经验", requiredMode = Schema.RequiredMode.REQUIRED, example = "200") + @NotNull(message = "变更后的经验不能为空") + private Integer totalExperience; + + @Schema(description = "描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "下单增加 100 经验") + @NotNull(message = "描述不能为空") + private String description; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/experience/MemberExperienceRecordPageReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/experience/MemberExperienceRecordPageReqVO.java new file mode 100644 index 000000000..d18201d7c --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/experience/MemberExperienceRecordPageReqVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.member.controller.admin.level.vo.experience; + +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 MemberExperienceRecordPageReqVO extends PageParam { + + @Schema(description = "用户编号", example = "3638") + private Long userId; + + @Schema(description = "业务编号", example = "12164") + private String bizId; + + @Schema(description = "业务类型", example = "1") + private Integer bizType; + + @Schema(description = "标题", example = "增加经验") + private String title; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/experience/MemberExperienceRecordRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/experience/MemberExperienceRecordRespVO.java new file mode 100644 index 000000000..5e652fcf0 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/experience/MemberExperienceRecordRespVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.member.controller.admin.level.vo.experience; + +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 MemberExperienceRecordRespVO extends MemberExperienceRecordBaseVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19610") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelBaseVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelBaseVO.java new file mode 100644 index 000000000..9580647f8 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelBaseVO.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.module.member.controller.admin.level.vo.level; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.hibernate.validator.constraints.Range; +import org.hibernate.validator.constraints.URL; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Positive; + +/** + * 会员等级 Base VO,提供给添加、修改、详细的子 VO 使用 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 + */ +@Data +public class MemberLevelBaseVO { + + @Schema(description = "等级名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @NotBlank(message = "等级名称不能为空") + private String name; + + @Schema(description = "升级经验", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + @NotNull(message = "升级经验不能为空") + @Positive(message = "升级经验必须大于 0") + private Integer experience; + + @Schema(description = "等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "等级不能为空") + @Positive(message = "等级必须大于 0") + private Integer level; + + @Schema(description = "享受折扣", requiredMode = Schema.RequiredMode.REQUIRED, example = "98") + @NotNull(message = "享受折扣不能为空") + @Range(min = 0, max = 100, message = "享受折扣的范围为 0-100") + private Integer discountPercent; + + @Schema(description = "等级图标", example = "https://www.iocoder.cn/yudao.jpg") + @URL(message = "等级图标必须是 URL 格式") + private String icon; + + @Schema(description = "等级背景图", example = "https://www.iocoder.cn/yudao.jpg") + @URL(message = "等级背景图必须是 URL 格式") + private String backgroundUrl; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + @InEnum(CommonStatusEnum.class) + private Integer status; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelCreateReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelCreateReqVO.java new file mode 100644 index 000000000..f51a7d967 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelCreateReqVO.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.member.controller.admin.level.vo.level; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - 会员等级创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MemberLevelCreateReqVO extends MemberLevelBaseVO { + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelListReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelListReqVO.java new file mode 100644 index 000000000..348e78e8e --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelListReqVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.member.controller.admin.level.vo.level; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.ToString; + +@Schema(description = "管理后台 - 会员等级列表筛选 Request VO") +@Data +@ToString(callSuper = true) +public class MemberLevelListReqVO { + + @Schema(description = "等级名称", example = "芋艿") + private String name; + + @Schema(description = "状态", example = "1") + private Integer status; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelRespVO.java new file mode 100644 index 000000000..df91a814f --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelRespVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.member.controller.admin.level.vo.level; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 会员等级 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MemberLevelRespVO extends MemberLevelBaseVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6103") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelSimpleRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelSimpleRespVO.java new file mode 100644 index 000000000..96c515c8b --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelSimpleRespVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.member.controller.admin.level.vo.level; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.ToString; + +@Schema(description = "管理后台 - 会员等级 Response VO") +@Data +@ToString(callSuper = true) +public class MemberLevelSimpleRespVO { + + @Schema(description = "编号", example = "6103") + private Long id; + + @Schema(description = "等级名称", example = "芋艿") + private String name; + + @Schema(description = "等级图标", example = "https://www.iocoder.cn/yudao.jpg") + private String icon; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelUpdateReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelUpdateReqVO.java new file mode 100644 index 000000000..83ad768de --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/level/MemberLevelUpdateReqVO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.member.controller.admin.level.vo.level; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 会员等级更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MemberLevelUpdateReqVO extends MemberLevelBaseVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6103") + @NotNull(message = "编号不能为空") + private Long id; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/record/MemberLevelRecordBaseVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/record/MemberLevelRecordBaseVO.java new file mode 100644 index 000000000..99df53648 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/record/MemberLevelRecordBaseVO.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.member.controller.admin.level.vo.record; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** + * 会员等级记录 Base VO,提供给添加、修改、详细的子 VO 使用 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 + */ +@Data +public class MemberLevelRecordBaseVO { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25923") + @NotNull(message = "用户编号不能为空") + private Long userId; + + @Schema(description = "等级编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25985") + @NotNull(message = "等级编号不能为空") + private Long levelId; + + @Schema(description = "会员等级", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "会员等级不能为空") + private Integer level; + + @Schema(description = "享受折扣", requiredMode = Schema.RequiredMode.REQUIRED, example = "13319") + @NotNull(message = "享受折扣不能为空") + private Integer discountPercent; + + @Schema(description = "升级经验", requiredMode = Schema.RequiredMode.REQUIRED, example = "13319") + @NotNull(message = "升级经验不能为空") + private Integer experience; + + @Schema(description = "会员此时的经验", requiredMode = Schema.RequiredMode.REQUIRED, example = "13319") + @NotNull(message = "会员此时的经验不能为空") + private Integer userExperience; + + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "推广需要") + @NotNull(message = "备注不能为空") + private String remark; + + @Schema(description = "描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "升级为金牌会员") + @NotNull(message = "描述不能为空") + private String description; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/record/MemberLevelRecordPageReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/record/MemberLevelRecordPageReqVO.java new file mode 100644 index 000000000..2590cfba6 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/record/MemberLevelRecordPageReqVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.member.controller.admin.level.vo.record; + +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 MemberLevelRecordPageReqVO extends PageParam { + + @Schema(description = "用户编号", example = "25923") + private Long userId; + + @Schema(description = "等级编号", example = "25985") + private Long levelId; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/record/MemberLevelRecordRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/record/MemberLevelRecordRespVO.java new file mode 100644 index 000000000..caf98ea4e --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/level/vo/record/MemberLevelRecordRespVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.member.controller.admin.level.vo.record; + +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 MemberLevelRecordRespVO extends MemberLevelRecordBaseVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8741") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/point/MemberPointRecordController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/point/MemberPointRecordController.java new file mode 100644 index 000000000..4d6ec352b --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/point/MemberPointRecordController.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.member.controller.admin.point; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod.MemberPointRecordPageReqVO; +import cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod.MemberPointRecordRespVO; +import cn.iocoder.yudao.module.member.convert.point.MemberPointRecordConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO; +import cn.iocoder.yudao.module.member.service.point.MemberPointRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.util.CollectionUtils; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +@Tag(name = "管理后台 - 签到记录") +@RestController +@RequestMapping("/member/point/record") +@Validated +public class MemberPointRecordController { + + @Resource + private MemberPointRecordService pointRecordService; + + @Resource + private MemberUserApi memberUserApi; + + @GetMapping("/page") + @Operation(summary = "获得用户积分记录分页") + @PreAuthorize("@ss.hasPermission('point:record:query')") + public CommonResult> getPointRecordPage(@Valid MemberPointRecordPageReqVO pageVO) { + // 执行分页查询 + PageResult pageResult = pointRecordService.getPointRecordPage(pageVO); + if (CollectionUtils.isEmpty(pageResult.getList())) { + return success(PageResult.empty(pageResult.getTotal())); + } + + // 拼接结果返回 + List users = memberUserApi.getUserList( + convertSet(pageResult.getList(), MemberPointRecordDO::getUserId)); + return success(MemberPointRecordConvert.INSTANCE.convertPage(pageResult, users)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/point/vo/recrod/MemberPointRecordPageReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/point/vo/recrod/MemberPointRecordPageReqVO.java new file mode 100644 index 000000000..63cc80006 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/point/vo/recrod/MemberPointRecordPageReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod; + +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; + +@Schema(description = "管理后台 - 用户积分记录分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MemberPointRecordPageReqVO extends PageParam { + + @Schema(description = "用户昵称", example = "张三") + private String nickname; + + @Schema(description = "用户编号", example = "123") + private Long userId; + + @Schema(description = "业务类型", example = "1") + private Integer bizType; + + @Schema(description = "积分标题", example = "呵呵") + private String title; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/point/vo/recrod/MemberPointRecordRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/point/vo/recrod/MemberPointRecordRespVO.java new file mode 100644 index 000000000..6714aa87f --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/point/vo/recrod/MemberPointRecordRespVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 用户积分记录 Response VO") +@Data +public class MemberPointRecordRespVO { + + @Schema(description = "自增主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "31457") + private Long id; + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long userId; + + @Schema(description = "昵称", example = "张三") + private String nickname; + + @Schema(description = "业务编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "22706") + private String bizId; + + @Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer bizType; + + @Schema(description = "积分标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "你猜") + private String title; + + @Schema(description = "积分描述", example = "你猜") + private String description; + + @Schema(description = "积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer point; + + @Schema(description = "变动后的积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "200") + private Integer totalPoint; + + @Schema(description = "发生时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/MemberSignInConfigController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/MemberSignInConfigController.java new file mode 100644 index 000000000..65bfe44c2 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/MemberSignInConfigController.java @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.module.member.controller.admin.signin; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.member.controller.admin.signin.vo.config.MemberSignInConfigCreateReqVO; +import cn.iocoder.yudao.module.member.controller.admin.signin.vo.config.MemberSignInConfigRespVO; +import cn.iocoder.yudao.module.member.controller.admin.signin.vo.config.MemberSignInConfigUpdateReqVO; +import cn.iocoder.yudao.module.member.convert.signin.MemberSignInConfigConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInConfigDO; +import cn.iocoder.yudao.module.member.service.signin.MemberSignInConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +// TODO 芋艿:url +@Tag(name = "管理后台 - 签到规则") +@RestController +@RequestMapping("/member/sign-in/config") +@Validated +public class MemberSignInConfigController { + + @Resource + private MemberSignInConfigService signInConfigService; + + @PostMapping("/create") + @Operation(summary = "创建签到规则") + @PreAuthorize("@ss.hasPermission('point:sign-in-config:create')") + public CommonResult createSignInConfig(@Valid @RequestBody MemberSignInConfigCreateReqVO createReqVO) { + return success(signInConfigService.createSignInConfig(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新签到规则") + @PreAuthorize("@ss.hasPermission('point:sign-in-config:update')") + public CommonResult updateSignInConfig(@Valid @RequestBody MemberSignInConfigUpdateReqVO updateReqVO) { + signInConfigService.updateSignInConfig(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除签到规则") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('point:sign-in-config:delete')") + public CommonResult deleteSignInConfig(@RequestParam("id") Long id) { + signInConfigService.deleteSignInConfig(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得签到规则") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('point:sign-in-config:query')") + public CommonResult getSignInConfig(@RequestParam("id") Long id) { + MemberSignInConfigDO signInConfig = signInConfigService.getSignInConfig(id); + return success(MemberSignInConfigConvert.INSTANCE.convert(signInConfig)); + } + + @GetMapping("/list") + @Operation(summary = "获得签到规则列表") + @PreAuthorize("@ss.hasPermission('point:sign-in-config:query')") + public CommonResult> getSignInConfigList() { + List list = signInConfigService.getSignInConfigList(); + return success(MemberSignInConfigConvert.INSTANCE.convertList(list)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/MemberSignInRecordController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/MemberSignInRecordController.java new file mode 100644 index 000000000..83b8f9b3f --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/MemberSignInRecordController.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.member.controller.admin.signin; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.member.controller.admin.signin.vo.record.MemberSignInRecordPageReqVO; +import cn.iocoder.yudao.module.member.controller.admin.signin.vo.record.MemberSignInRecordRespVO; +import cn.iocoder.yudao.module.member.convert.signin.MemberSignInRecordConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInRecordDO; +import cn.iocoder.yudao.module.member.service.signin.MemberSignInRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.util.CollectionUtils; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +@Tag(name = "管理后台 - 签到记录") +@RestController +@RequestMapping("/member/sign-in/record") +@Validated +public class MemberSignInRecordController { + + @Resource + private MemberSignInRecordService signInRecordService; + + @Resource + private MemberUserApi memberUserApi; + + @GetMapping("/page") + @Operation(summary = "获得签到记录分页") + @PreAuthorize("@ss.hasPermission('point:sign-in-record:query')") + public CommonResult> getSignInRecordPage(@Valid MemberSignInRecordPageReqVO pageVO) { + // 执行分页查询 + PageResult pageResult = signInRecordService.getSignInRecordPage(pageVO); + if (CollectionUtils.isEmpty(pageResult.getList())) { + return success(PageResult.empty(pageResult.getTotal())); + } + + // 拼接结果返回 + List users = memberUserApi.getUserList( + convertSet(pageResult.getList(), MemberSignInRecordDO::getUserId)); + return success(MemberSignInRecordConvert.INSTANCE.convertPage(pageResult, users)); + } +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/vo/config/MemberSignInConfigBaseVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/vo/config/MemberSignInConfigBaseVO.java new file mode 100644 index 000000000..2ddeeb9be --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/vo/config/MemberSignInConfigBaseVO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.member.controller.admin.signin.vo.config; + +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.AssertTrue; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.PositiveOrZero; + +/** + * 签到规则 Base VO,提供给添加、修改、详细的子 VO 使用 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 + */ +@Data +public class MemberSignInConfigBaseVO { + + @Schema(description = "签到第 x 天", requiredMode = Schema.RequiredMode.REQUIRED, example = "7") + @NotNull(message = "签到天数不能为空") + private Integer day; + + @Schema(description = "奖励积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @NotNull(message = "奖励积分不能为空") + @PositiveOrZero(message = "奖励积分不能小于 0") + private Integer point; + + @Schema(description = "奖励经验", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @NotNull(message = "奖励经验不能为空") + @PositiveOrZero(message = "奖励经验不能小于 0") + private Integer experience; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @AssertTrue(message = "签到奖励积分和经验不能同时为空") + @JsonIgnore + public boolean isConfigAward() { + return ObjUtil.notEqual(point, 0) || ObjUtil.notEqual(experience, 0); + } +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/vo/config/MemberSignInConfigCreateReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/vo/config/MemberSignInConfigCreateReqVO.java new file mode 100644 index 000000000..7ca03fa93 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/vo/config/MemberSignInConfigCreateReqVO.java @@ -0,0 +1,12 @@ +package cn.iocoder.yudao.module.member.controller.admin.signin.vo.config; + +import lombok.*; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "管理后台 - 签到规则创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MemberSignInConfigCreateReqVO extends MemberSignInConfigBaseVO { + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/vo/config/MemberSignInConfigRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/vo/config/MemberSignInConfigRespVO.java new file mode 100644 index 000000000..8d423b25c --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/vo/config/MemberSignInConfigRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.member.controller.admin.signin.vo.config; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 签到规则 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MemberSignInConfigRespVO extends MemberSignInConfigBaseVO { + + @Schema(description = "自增主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "20937") + private Integer id; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/vo/config/MemberSignInConfigUpdateReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/vo/config/MemberSignInConfigUpdateReqVO.java new file mode 100644 index 000000000..89b6de15c --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/vo/config/MemberSignInConfigUpdateReqVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.member.controller.admin.signin.vo.config; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import javax.validation.constraints.*; + +@Schema(description = "管理后台 - 签到规则更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MemberSignInConfigUpdateReqVO extends MemberSignInConfigBaseVO { + + @Schema(description = "规则自增主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "13653") + @NotNull(message = "规则自增主键不能为空") + private Long id; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/vo/record/MemberSignInRecordPageReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/vo/record/MemberSignInRecordPageReqVO.java new file mode 100644 index 000000000..b46712b6e --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/vo/record/MemberSignInRecordPageReqVO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.member.controller.admin.signin.vo.record; + +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 MemberSignInRecordPageReqVO extends PageParam { + + @Schema(description = "签到用户", example = "土豆") + private String nickname; + + @Schema(description = "第几天签到", example = "10") + private Integer day; + + @Schema(description = "用户编号", example = "123") + private Long userId; + + @Schema(description = "签到时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/vo/record/MemberSignInRecordRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/vo/record/MemberSignInRecordRespVO.java new file mode 100644 index 000000000..b5755ba53 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/signin/vo/record/MemberSignInRecordRespVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.member.controller.admin.signin.vo.record; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 签到记录 Response VO") +@Data +public class MemberSignInRecordRespVO { + + @Schema(description = "签到自增 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "11903") + private Long id; + + @Schema(description = "签到用户", requiredMode = Schema.RequiredMode.REQUIRED, example = "6507") + private Long userId; + + @Schema(description = "昵称", example = "张三") + private String nickname; + + @Schema(description = "第几天签到", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer day; + + @Schema(description = "签到的积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer point; + + @Schema(description = "签到时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/tag/MemberTagController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/tag/MemberTagController.java new file mode 100644 index 000000000..34f3c20cc --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/tag/MemberTagController.java @@ -0,0 +1,94 @@ +package cn.iocoder.yudao.module.member.controller.admin.tag; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.controller.admin.tag.vo.MemberTagCreateReqVO; +import cn.iocoder.yudao.module.member.controller.admin.tag.vo.MemberTagPageReqVO; +import cn.iocoder.yudao.module.member.controller.admin.tag.vo.MemberTagRespVO; +import cn.iocoder.yudao.module.member.controller.admin.tag.vo.MemberTagUpdateReqVO; +import cn.iocoder.yudao.module.member.convert.tag.MemberTagConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO; +import cn.iocoder.yudao.module.member.service.tag.MemberTagService; +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/tag") +@Validated +public class MemberTagController { + + @Resource + private MemberTagService tagService; + + @PostMapping("/create") + @Operation(summary = "创建会员标签") + @PreAuthorize("@ss.hasPermission('member:tag:create')") + public CommonResult createTag(@Valid @RequestBody MemberTagCreateReqVO createReqVO) { + return success(tagService.createTag(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新会员标签") + @PreAuthorize("@ss.hasPermission('member:tag:update')") + public CommonResult updateTag(@Valid @RequestBody MemberTagUpdateReqVO updateReqVO) { + tagService.updateTag(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除会员标签") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('member:tag:delete')") + public CommonResult deleteTag(@RequestParam("id") Long id) { + tagService.deleteTag(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得会员标签") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('member:tag:query')") + public CommonResult getMemberTag(@RequestParam("id") Long id) { + MemberTagDO tag = tagService.getTag(id); + return success(MemberTagConvert.INSTANCE.convert(tag)); + } + + @GetMapping("/list-all-simple") + @Operation(summary = "获取会员标签精简信息列表", description = "只包含被开启的会员标签,主要用于前端的下拉选项") + public CommonResult> getSimpleTagList() { + // 获用户列表,只要开启状态的 + List list = tagService.getTagList(); + // 排序后,返回给前端 + return success(MemberTagConvert.INSTANCE.convertList(list)); + } + + @GetMapping("/list") + @Operation(summary = "获得会员标签列表") + @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048") + @PreAuthorize("@ss.hasPermission('member:tag:query')") + public CommonResult> getMemberTagList(@RequestParam("ids") Collection ids) { + List list = tagService.getTagList(ids); + return success(MemberTagConvert.INSTANCE.convertList(list)); + } + + @GetMapping("/page") + @Operation(summary = "获得会员标签分页") + @PreAuthorize("@ss.hasPermission('member:tag:query')") + public CommonResult> getTagPage(@Valid MemberTagPageReqVO pageVO) { + PageResult pageResult = tagService.getTagPage(pageVO); + return success(MemberTagConvert.INSTANCE.convertPage(pageResult)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/tag/vo/MemberTagBaseVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/tag/vo/MemberTagBaseVO.java new file mode 100644 index 000000000..bc0efea68 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/tag/vo/MemberTagBaseVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.member.controller.admin.tag.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 MemberTagBaseVO { + + @Schema(description = "标签名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") + @NotNull(message = "标签名称不能为空") + private String name; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/tag/vo/MemberTagCreateReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/tag/vo/MemberTagCreateReqVO.java new file mode 100644 index 000000000..b61f26bb2 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/tag/vo/MemberTagCreateReqVO.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.member.controller.admin.tag.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 MemberTagCreateReqVO extends MemberTagBaseVO { + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/tag/vo/MemberTagPageReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/tag/vo/MemberTagPageReqVO.java new file mode 100644 index 000000000..99f59b068 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/tag/vo/MemberTagPageReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.member.controller.admin.tag.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 MemberTagPageReqVO extends PageParam { + + @Schema(description = "标签名称", example = "李四") + private String name; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/tag/vo/MemberTagRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/tag/vo/MemberTagRespVO.java new file mode 100644 index 000000000..2c21f53e3 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/tag/vo/MemberTagRespVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.member.controller.admin.tag.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 MemberTagRespVO extends MemberTagBaseVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "907") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/tag/vo/MemberTagUpdateReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/tag/vo/MemberTagUpdateReqVO.java new file mode 100644 index 000000000..2fe0e614d --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/tag/vo/MemberTagUpdateReqVO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.member.controller.admin.tag.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 MemberTagUpdateReqVO extends MemberTagBaseVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "907") + @NotNull(message = "编号不能为空") + private Long id; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/MemberUserController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/MemberUserController.java new file mode 100644 index 000000000..b382c1caf --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/MemberUserController.java @@ -0,0 +1,121 @@ +package cn.iocoder.yudao.module.member.controller.admin.user; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.controller.admin.user.vo.*; +import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO; +import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO; +import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; +import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum; +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.point.MemberPointRecordService; +import cn.iocoder.yudao.module.member.service.tag.MemberTagService; +import cn.iocoder.yudao.module.member.service.user.MemberUserService; +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 java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - 会员用户") +@RestController +@RequestMapping("/member/user") +@Validated +public class MemberUserController { + + @Resource + private MemberUserService memberUserService; + @Resource + private MemberTagService memberTagService; + @Resource + private MemberLevelService memberLevelService; + @Resource + private MemberGroupService memberGroupService; + @Resource + private MemberPointRecordService memberPointRecordService; + + @PutMapping("/update") + @Operation(summary = "更新会员用户") + @PreAuthorize("@ss.hasPermission('member:user:update')") + public CommonResult updateUser(@Valid @RequestBody MemberUserUpdateReqVO updateReqVO) { + memberUserService.updateUser(updateReqVO); + return success(true); + } + + @PutMapping("/update-level") + @Operation(summary = "更新会员用户等级") + @PreAuthorize("@ss.hasPermission('member:user:update-level')") + public CommonResult updateUserLevel(@Valid @RequestBody MemberUserUpdateLevelReqVO updateReqVO) { + memberLevelService.updateUserLevel(updateReqVO); + return success(true); + } + + @PutMapping("/update-point") + @Operation(summary = "更新会员用户积分") + @PreAuthorize("@ss.hasPermission('member:user:update-point')") + public CommonResult updateUserPoint(@Valid @RequestBody MemberUserUpdatePointReqVO updateReqVO) { + memberPointRecordService.createPointRecord(updateReqVO.getId(), updateReqVO.getPoint(), + MemberPointBizTypeEnum.ADMIN, String.valueOf(getLoginUserId())); + return success(true); + } + + @PutMapping("/update-balance") + @Operation(summary = "更新会员用户余额") + @PreAuthorize("@ss.hasPermission('member:user:update-balance')") + public CommonResult updateUserBalance(@Valid @RequestBody Long id) { + // todo @jason:增加一个【修改余额】 + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得会员用户") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('member:user:query')") + public CommonResult getUser(@RequestParam("id") Long id) { + MemberUserDO user = memberUserService.getUser(id); + return success(MemberUserConvert.INSTANCE.convert03(user)); + } + + @GetMapping("/page") + @Operation(summary = "获得会员用户分页") + @PreAuthorize("@ss.hasPermission('member:user:query')") + public CommonResult> getUserPage(@Valid MemberUserPageReqVO pageVO) { + PageResult pageResult = memberUserService.getUserPage(pageVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty()); + } + + // 处理用户标签返显 + Set tagIds = pageResult.getList().stream() + .map(MemberUserDO::getTagIds) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + List tags = memberTagService.getTagList(tagIds); + // 处理用户级别返显 + List levels = memberLevelService.getLevelList( + convertSet(pageResult.getList(), MemberUserDO::getLevelId)); + // 处理用户分组返显 + List groups = memberGroupService.getGroupList( + convertSet(pageResult.getList(), MemberUserDO::getGroupId)); + return success(MemberUserConvert.INSTANCE.convertPage(pageResult, tags, levels, groups)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserBaseVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserBaseVO.java new file mode 100644 index 000000000..e20963e5e --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserBaseVO.java @@ -0,0 +1,65 @@ +package cn.iocoder.yudao.module.member.controller.admin.user.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.hibernate.validator.constraints.URL; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY; + +/** + * 会员用户 Base VO,提供给添加、修改、详细的子 VO 使用 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 + */ +@Data +public class MemberUserBaseVO { + + @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300") + @NotNull(message = "手机号不能为空") + private String mobile; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "状态不能为空") + private Byte status; + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") + @NotNull(message = "用户昵称不能为空") + private String nickname; + + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/x.png") + @URL(message = "头像必须是 URL 格式") + private String avatar; + + @Schema(description = "用户昵称", example = "李四") + private String name; + + @Schema(description = "用户性别", example = "1") + private Byte sex; + + @Schema(description = "所在地编号", example = "4371") + private Long areaId; + + @Schema(description = "所在地全程", example = "上海上海市普陀区") + private String areaName; + + @Schema(description = "出生日期", example = "2023-03-12") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY) + private LocalDateTime birthday; + + @Schema(description = "会员备注", example = "我是小备注") + private String mark; + + @Schema(description = "会员标签", example = "[1, 2]") + private List tagIds; + + @Schema(description = "会员等级编号", example = "1") + private Long levelId; + + @Schema(description = "用户分组编号", example = "1") + private Long groupId; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserPageReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserPageReqVO.java new file mode 100644 index 000000000..abb94285e --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserPageReqVO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.member.controller.admin.user.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 java.util.List; + +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 MemberUserPageReqVO extends PageParam { + + @Schema(description = "手机号", example = "15601691300") + private String mobile; + + @Schema(description = "用户昵称", example = "李四") + private String nickname; + + @Schema(description = "最后登录时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] loginDate; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "会员标签编号列表", example = "[1, 2]") + private List tagIds; + + @Schema(description = "会员等级编号", example = "1") + private Long levelId; + + @Schema(description = "用户分组编号", example = "1") + private Long groupId; + + // TODO 芋艿:注册用户类型; + + // TODO 芋艿:登录用户类型; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserRespVO.java new file mode 100644 index 000000000..1cd228335 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserRespVO.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.member.controller.admin.user.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - 会员用户 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MemberUserRespVO extends MemberUserBaseVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23788") + private Long id; + + @Schema(description = "注册 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1") + private String registerIp; + + @Schema(description = "最后登录IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1") + private String loginIp; + + @Schema(description = "最后登录时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime loginDate; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + // ========== 其它信息 ========== + + @Schema(description = "积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer point; + + @Schema(description = "总积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000") + private Integer totalPoint; + + @Schema(description = "会员标签", example = "[红色, 快乐]") + private List tagNames; + + @Schema(description = "会员等级", example = "黄金会员") + private String levelName; + + @Schema(description = "用户分组", example = "购物达人") + private String groupName; + + @Schema(description = "用户经验值", requiredMode = Schema.RequiredMode.REQUIRED, example = "200") + private Integer experience; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserUpdateLevelReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserUpdateLevelReqVO.java new file mode 100644 index 000000000..dba48f670 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserUpdateLevelReqVO.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.member.controller.admin.user.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.ToString; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 用户修改等级 Request VO") +@Data +@ToString(callSuper = true) +public class MemberUserUpdateLevelReqVO { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23788") + @NotNull(message = "用户编号不能为空") + private Long id; + + /** + * 取消用户等级时,值为空 + */ + @Schema(description = "用户等级编号", example = "1") + private Long levelId; + + @Schema(description = "修改原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "推广需要") + @NotBlank(message = "修改原因不能为空") + private String reason; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserUpdatePointReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserUpdatePointReqVO.java new file mode 100644 index 000000000..a072c0726 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserUpdatePointReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.member.controller.admin.user.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.ToString; + +import javax.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 用户修改积分 Request VO") +@Data +@ToString(callSuper = true) +public class MemberUserUpdatePointReqVO { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23788") + @NotNull(message = "用户编号不能为空") + private Long id; + + @Schema(description = "变动积分,正数为增加,负数为减少", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + @NotNull(message = "变动积分不能为空") + private Integer point; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserUpdateReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserUpdateReqVO.java new file mode 100644 index 000000000..c6a92758d --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserUpdateReqVO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.member.controller.admin.user.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 MemberUserUpdateReqVO extends MemberUserBaseVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23788") + @NotNull(message = "编号不能为空") + private Long id; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.http b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.http new file mode 100644 index 000000000..6bae7c7eb --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.http @@ -0,0 +1,54 @@ +### 请求 /create 接口 => 成功 +POST {{appApi}}//member/address/create +Content-Type: application/json +tenant-id: {{appTenentId}} +Authorization: Bearer {{appToken}} + +{ + "name": "yunai", + "mobile": "15601691300", + "areaId": "610632", + "postCode": "200000", + "detailAddress": "芋道源码 233 号 666 室", + "defaulted": true +} + +### 请求 /update 接口 => 成功 +PUT {{appApi}}//member/address/update +Content-Type: application/json +tenant-id: {{appTenentId}} +Authorization: Bearer {{appToken}} + +{ + "id": "1", + "name": "yunai888", + "mobile": "15601691300", + "areaId": "610632", + "postCode": "200000", + "detailAddress": "芋道源码 233 号 666 室", + "defaulted": false +} + +### 请求 /delete 接口 => 成功 +DELETE {{appApi}}//member/address/delete?id=2 +Content-Type: application/json +tenant-id: {{appTenentId}} +Authorization: Bearer {{appToken}} + +### 请求 /get 接口 => 成功 +GET {{appApi}}//member/address/get?id=1 +Content-Type: application/json +tenant-id: {{appTenentId}} +Authorization: Bearer {{appToken}} + +### 请求 /get-default 接口 => 成功 +GET {{appApi}}//member/address/get-default +Content-Type: application/json +tenant-id: {{appTenentId}} +Authorization: Bearer {{appToken}} + +### 请求 /list 接口 => 成功 +GET {{appApi}}//member/address/list +Content-Type: application/json +tenant-id: {{appTenentId}} +Authorization: Bearer {{appToken}} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.java new file mode 100644 index 000000000..7ba55c3bd --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.java @@ -0,0 +1,82 @@ +package cn.iocoder.yudao.module.member.controller.app.address; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressCreateReqVO; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressRespVO; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressUpdateReqVO; +import cn.iocoder.yudao.module.member.convert.address.AddressConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.address.MemberAddressDO; +import cn.iocoder.yudao.module.member.service.address.AddressService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "用户 APP - 用户收件地址") +@RestController +@RequestMapping("/member/address") +@Validated +public class AppAddressController { + + @Resource + private AddressService addressService; + + @PostMapping("/create") + @Operation(summary = "创建用户收件地址") + @PreAuthenticated + public CommonResult createAddress(@Valid @RequestBody AppAddressCreateReqVO createReqVO) { + return success(addressService.createAddress(getLoginUserId(), createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新用户收件地址") + @PreAuthenticated + public CommonResult updateAddress(@Valid @RequestBody AppAddressUpdateReqVO updateReqVO) { + addressService.updateAddress(getLoginUserId(), updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除用户收件地址") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthenticated + public CommonResult deleteAddress(@RequestParam("id") Long id) { + addressService.deleteAddress(getLoginUserId(), id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得用户收件地址") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthenticated + public CommonResult getAddress(@RequestParam("id") Long id) { + MemberAddressDO address = addressService.getAddress(getLoginUserId(), id); + return success(AddressConvert.INSTANCE.convert(address)); + } + + @GetMapping("/get-default") + @Operation(summary = "获得默认的用户收件地址") + @PreAuthenticated + public CommonResult getDefaultUserAddress() { + MemberAddressDO address = addressService.getDefaultUserAddress(getLoginUserId()); + return success(AddressConvert.INSTANCE.convert(address)); + } + + @GetMapping("/list") + @Operation(summary = "获得用户收件地址列表") + @PreAuthenticated + public CommonResult> getAddressList() { + List list = addressService.getAddressList(getLoginUserId()); + return success(AddressConvert.INSTANCE.convertList(list)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressBaseVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressBaseVO.java new file mode 100644 index 000000000..076ce36ff --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressBaseVO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.member.controller.app.address.vo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +// TODO 芋艿:example 缺失 +/** +* 用户收件地址 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class AppAddressBaseVO { + + @Schema(description = "收件人名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "收件人名称不能为空") + private String name; + + @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "手机号不能为空") + private String mobile; + + @Schema(description = "地区编号", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "地区编号不能为空") + private Long areaId; + + @Schema(description = "收件详细地址", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "收件详细地址不能为空") + private String detailAddress; + + @Schema(description = "是否默认地址", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "是否默认地址不能为空") + private Boolean defaultStatus; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressCreateReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressCreateReqVO.java new file mode 100644 index 000000000..c92687f27 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressCreateReqVO.java @@ -0,0 +1,11 @@ +package cn.iocoder.yudao.module.member.controller.app.address.vo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +@Schema(description = "用户 APP - 用户收件地址创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class AppAddressCreateReqVO extends AppAddressBaseVO { + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressRespVO.java new file mode 100644 index 000000000..d3e2f9ffd --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressRespVO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.member.controller.app.address.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "用户 APP - 用户收件地址 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class AppAddressRespVO extends AppAddressBaseVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "地区名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "上海上海市普陀区") + private String areaName; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressUpdateReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressUpdateReqVO.java new file mode 100644 index 000000000..19b58d807 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressUpdateReqVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.member.controller.app.address.vo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import javax.validation.constraints.*; + +@Schema(description = "用户 APP - 用户收件地址更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class AppAddressUpdateReqVO extends AppAddressBaseVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "编号不能为空") + private Long id; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http new file mode 100644 index 000000000..998c70c21 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http @@ -0,0 +1,66 @@ +### 请求 /login 接口 => 成功 +POST {{appApi}}/member/auth/login +Content-Type: application/json +tenant-id: {{appTenentId}} + +{ + "mobile": "15601691300", + "password": "admin123" +} + +### 请求 /send-sms-code 接口 => 成功 +POST {{appApi}}/member/auth/send-sms-code +Content-Type: application/json +tenant-id: {{appTenentId}} + +{ + "mobile": "15601691399", + "scene": 1 +} + +### 请求 /sms-login 接口 => 成功 +POST {{appApi}}/member/auth/sms-login +Content-Type: application/json +tenant-id: {{appTenentId}} + +{ + "mobile": "15601691301", + "code": 9999 +} + +### 请求 /social-login 接口 => 成功 +POST {{appApi}}/member/auth/social-login +Content-Type: application/json +tenant-id: {{appTenentId}} + +{ + "type": 34, + "code": "0e1oc9000CTjFQ1oim200bhtb61oc90g", + "state": "default" +} + +### 请求 /weixin-mini-app-login 接口 => 成功 +POST {{appApi}}/member/auth/weixin-mini-app-login +Content-Type: application/json +tenant-id: {{appTenentId}} + +{ + "phoneCode": "618e6412e0c728f5b8fc7164497463d0158a923c9e7fd86af8bba393b9decbc5", + "loginCode": "001frTkl21JUf94VGxol2hSlff1frTkR" +} + +### 请求 /logout 接口 => 成功 +POST {{appApi}}/member/auth/logout +Content-Type: application/json +Authorization: Bearer c1b76bdaf2c146c581caa4d7fd81ee66 +tenant-id: {{appTenentId}} + +### 请求 /auth/refresh-token 接口 => 成功 +POST {{appApi}}/member/auth/refresh-token?refreshToken=bc43d929094849a28b3a69f6e6940d70 +Content-Type: application/json +tenant-id: {{appTenentId}} + +### 请求 /auth/create-weixin-jsapi-signature 接口 => 成功 +POST {{appApi}}/member/auth/create-weixin-jsapi-signature?url=http://www.iocoder.cn +Authorization: Bearer {{appToken}} +tenant-id: {{appTenentId}} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java new file mode 100644 index 000000000..ed3963e90 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java @@ -0,0 +1,128 @@ +package cn.iocoder.yudao.module.member.controller.app.auth; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.framework.security.config.SecurityProperties; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; +import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; +import cn.iocoder.yudao.module.member.convert.auth.AuthConvert; +import cn.iocoder.yudao.module.member.service.auth.MemberAuthService; +import cn.iocoder.yudao.module.system.api.social.SocialClientApi; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.annotation.security.PermitAll; +import javax.servlet.http.HttpServletRequest; +import javax.validation.Valid; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "用户 APP - 认证") +@RestController +@RequestMapping("/member/auth") +@Validated +@Slf4j +public class AppAuthController { + + @Resource + private MemberAuthService authService; + + @Resource + private SocialClientApi socialClientApi; + + @Resource + private SecurityProperties securityProperties; + + @PostMapping("/login") + @Operation(summary = "使用手机 + 密码登录") + public CommonResult login(@RequestBody @Valid AppAuthLoginReqVO reqVO) { + return success(authService.login(reqVO)); + } + + @PostMapping("/logout") + @PermitAll + @Operation(summary = "登出系统") + public CommonResult logout(HttpServletRequest request) { + String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader()); + if (StrUtil.isNotBlank(token)) { + authService.logout(token); + } + return success(true); + } + + @PostMapping("/refresh-token") + @Operation(summary = "刷新令牌") + @Parameter(name = "refreshToken", description = "刷新令牌", required = true) + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 + public CommonResult refreshToken(@RequestParam("refreshToken") String refreshToken) { + return success(authService.refreshToken(refreshToken)); + } + + // ========== 短信登录相关 ========== + + @PostMapping("/sms-login") + @Operation(summary = "使用手机 + 验证码登录") + public CommonResult smsLogin(@RequestBody @Valid AppAuthSmsLoginReqVO reqVO, + @RequestHeader Integer terminal) { + return success(authService.smsLogin(reqVO, terminal)); + } + + @PostMapping("/send-sms-code") + @Operation(summary = "发送手机验证码") + public CommonResult sendSmsCode(@RequestBody @Valid AppAuthSmsSendReqVO reqVO) { + authService.sendSmsCode(getLoginUserId(), reqVO); + return success(true); + } + + @PostMapping("/validate-sms-code") + @Operation(summary = "校验手机验证码") + public CommonResult validateSmsCode(@RequestBody @Valid AppAuthSmsValidateReqVO reqVO) { + authService.validateSmsCode(getLoginUserId(), reqVO); + return success(true); + } + + // ========== 社交登录相关 ========== + + @GetMapping("/social-auth-redirect") + @Operation(summary = "社交授权的跳转") + @Parameters({ + @Parameter(name = "type", description = "社交类型", required = true), + @Parameter(name = "redirectUri", description = "回调路径") + }) + public CommonResult socialAuthRedirect(@RequestParam("type") Integer type, + @RequestParam("redirectUri") String redirectUri) { + return CommonResult.success(authService.getSocialAuthorizeUrl(type, redirectUri)); + } + + @PostMapping("/social-login") + @Operation(summary = "社交快捷登录,使用 code 授权码", description = "适合未登录的用户,但是社交账号已绑定用户") + public CommonResult socialLogin(@RequestBody @Valid AppAuthSocialLoginReqVO reqVO) { + return success(authService.socialLogin(reqVO)); + } + + @PostMapping("/weixin-mini-app-login") + @Operation(summary = "微信小程序的一键登录") + public CommonResult weixinMiniAppLogin(@RequestBody @Valid AppAuthWeixinMiniAppLoginReqVO reqVO) { + return success(authService.weixinMiniAppLogin(reqVO)); + } + + @PostMapping("/create-weixin-jsapi-signature") + @Operation(summary = "创建微信 JS SDK 初始化所需的签名", + description = "参考 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html 文档") + public CommonResult createWeixinMpJsapiSignature(@RequestParam("url") String url) { + SocialWxJsapiSignatureRespDTO signature = socialClientApi.createWxMpJsapiSignature( + UserTypeEnum.MEMBER.getValue(), url); + return success(AuthConvert.INSTANCE.convert(signature)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthCheckCodeReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthCheckCodeReqVO.java new file mode 100644 index 000000000..eee7062cb --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthCheckCodeReqVO.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.member.controller.app.auth.vo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +// TODO 芋艿:code review 相关逻辑 +@Schema(description = "用户 APP - 校验验证码 Request VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AppAuthCheckCodeReqVO { + + @Schema(description = "手机号", example = "15601691234") + @NotBlank(message = "手机号不能为空") + @Mobile + private String mobile; + + @Schema(description = "手机验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotBlank(message = "手机验证码不能为空") + @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位") + @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") + private String code; + + @Schema(description = "发送场景,对应 SmsSceneEnum 枚举", example = "1") + @NotNull(message = "发送场景不能为空") + @InEnum(SmsSceneEnum.class) + private Integer scene; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginReqVO.java new file mode 100644 index 000000000..e64209de8 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginReqVO.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.member.controller.app.auth.vo; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.AssertTrue; +import javax.validation.constraints.NotEmpty; + +@Schema(description = "用户 APP - 手机 + 密码登录 Request VO,如果登录并绑定社交用户,需要传递 social 开头的参数") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AppAuthLoginReqVO { + + @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300") + @NotEmpty(message = "手机号不能为空") + @Mobile + private String mobile; + + @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "buzhidao") + @NotEmpty(message = "密码不能为空") + @Length(min = 4, max = 16, message = "密码长度为 4-16 位") + private String password; + + // ========== 绑定社交登录时,需要传递如下参数 ========== + + @Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @InEnum(SocialTypeEnum.class) + private Integer socialType; + + @Schema(description = "授权码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String socialCode; + + @Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62") + private String socialState; + + @AssertTrue(message = "授权码不能为空") + public boolean isSocialCodeValid() { + return socialType == null || StrUtil.isNotEmpty(socialCode); + } + + @AssertTrue(message = "授权 state 不能为空") + public boolean isSocialState() { + return socialType == null || StrUtil.isNotEmpty(socialState); + } + +} \ No newline at end of file diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginRespVO.java new file mode 100644 index 000000000..072ec9e4b --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginRespVO.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.member.controller.app.auth.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Schema(description = "用户 APP - 登录 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AppAuthLoginRespVO { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long userId; + + @Schema(description = "访问令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "happy") + private String accessToken; + + @Schema(description = "刷新令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "nice") + private String refreshToken; + + @Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime expiresTime; + + /** + * 仅社交登录、社交绑定时会返回 + * + * 为什么需要返回?微信公众号、微信小程序支付需要传递 openid 给支付接口 + */ + @Schema(description = "社交用户 openid", example = "qq768") + private String openid; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSmsLoginReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSmsLoginReqVO.java new file mode 100644 index 000000000..8225269a3 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSmsLoginReqVO.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.member.controller.app.auth.vo; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.AssertTrue; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Pattern; + +@Schema(description = "用户 APP - 手机 + 验证码登录 Request VO,如果登录并绑定社交用户,需要传递 social 开头的参数") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AppAuthSmsLoginReqVO { + + @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300") + @NotEmpty(message = "手机号不能为空") + @Mobile + private String mobile; + + @Schema(description = "手机验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "手机验证码不能为空") + @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位") + @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") + private String code; + + // ========== 绑定社交登录时,需要传递如下参数 ========== + + @Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @InEnum(SocialTypeEnum.class) + private Integer socialType; + + @Schema(description = "授权码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String socialCode; + + @Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62") + private String socialState; + + @AssertTrue(message = "授权码不能为空") + public boolean isSocialCodeValid() { + return socialType == null || StrUtil.isNotEmpty(socialCode); + } + + @AssertTrue(message = "授权 state 不能为空") + public boolean isSocialState() { + return socialType == null || StrUtil.isNotEmpty(socialState); + } + +} \ No newline at end of file diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSmsSendReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSmsSendReqVO.java new file mode 100644 index 000000000..5f4b030f3 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSmsSendReqVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.member.controller.app.auth.vo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotNull; + +@Schema(description = "用户 APP - 发送手机验证码 Request VO") +@Data +@Accessors(chain = true) +public class AppAuthSmsSendReqVO { + + @Schema(description = "手机号", example = "15601691234") + @Mobile + private String mobile; + + @Schema(description = "发送场景,对应 SmsSceneEnum 枚举", example = "1") + @NotNull(message = "发送场景不能为空") + @InEnum(SmsSceneEnum.class) + private Integer scene; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSmsValidateReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSmsValidateReqVO.java new file mode 100644 index 000000000..1a57be74b --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSmsValidateReqVO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.member.controller.app.auth.vo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +@Schema(description = "用户 APP - 校验手机验证码 Request VO") +@Data +@Accessors(chain = true) +public class AppAuthSmsValidateReqVO { + + @Schema(description = "手机号", example = "15601691234") + @Mobile + private String mobile; + + @Schema(description = "发送场景,对应 SmsSceneEnum 枚举", example = "1") + @NotNull(message = "发送场景不能为空") + @InEnum(SmsSceneEnum.class) + private Integer scene; + + @Schema(description = "手机验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "手机验证码不能为空") + @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位") + @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") + private String code; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialLoginReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialLoginReqVO.java new file mode 100644 index 000000000..d3bac4799 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialLoginReqVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.member.controller.app.auth.vo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +@Schema(description = "用户 APP - 社交快捷登录 Request VO,使用 code 授权码") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AppAuthSocialLoginReqVO { + + @Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @InEnum(SocialTypeEnum.class) + @NotNull(message = "社交平台的类型不能为空") + private Integer type; + + @Schema(description = "授权码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "授权码不能为空") + private String code; + + @Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62") + @NotEmpty(message = "state 不能为空") + private String state; + +} \ No newline at end of file diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthWeixinMiniAppLoginReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthWeixinMiniAppLoginReqVO.java new file mode 100644 index 000000000..11dcdf957 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthWeixinMiniAppLoginReqVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.member.controller.app.auth.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotEmpty; + +@Schema(description = "用户 APP - 微信小程序手机登录 Request VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AppAuthWeixinMiniAppLoginReqVO { + + @Schema(description = "手机 code,小程序通过 wx.getPhoneNumber 方法获得", requiredMode = Schema.RequiredMode.REQUIRED, example = "hello") + @NotEmpty(message = "手机 code 不能为空") + private String phoneCode; + + @Schema(description = "登录 code,小程序通过 wx.login 方法获得", requiredMode = Schema.RequiredMode.REQUIRED, example = "word") + @NotEmpty(message = "登录 code 不能为空") + private String loginCode; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AuthWeixinJsapiSignatureRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AuthWeixinJsapiSignatureRespVO.java new file mode 100644 index 000000000..37e63652b --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AuthWeixinJsapiSignatureRespVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.member.controller.app.auth.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Schema(description = "用户 APP - 微信公众号 JSAPI 签名 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AuthWeixinJsapiSignatureRespVO { + + @Schema(description = "微信公众号的 appId", requiredMode = Schema.RequiredMode.REQUIRED, example = "hello") + private String appId; + + @Schema(description = "匿名串", requiredMode = Schema.RequiredMode.REQUIRED, example = "world") + private String nonceStr; + + @Schema(description = "时间戳", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long timestamp; + + @Schema(description = "URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn") + private String url; + + @Schema(description = "签名", requiredMode = Schema.RequiredMode.REQUIRED, example = "阿巴阿巴") + private String signature; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/level/AppMemberExperienceRecordController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/level/AppMemberExperienceRecordController.java new file mode 100644 index 000000000..5c33e5c1b --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/level/AppMemberExperienceRecordController.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.member.controller.app.level; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; +import cn.iocoder.yudao.module.member.controller.app.level.vo.experience.AppMemberExperienceRecordRespVO; +import cn.iocoder.yudao.module.member.convert.level.MemberExperienceRecordConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberExperienceRecordDO; +import cn.iocoder.yudao.module.member.service.level.MemberExperienceRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.validation.Valid; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "用户 App - 会员经验记录") +@RestController +@RequestMapping("/member/experience-record") +@Validated +public class AppMemberExperienceRecordController { + + @Resource + private MemberExperienceRecordService experienceLogService; + + @GetMapping("/page") + @Operation(summary = "获得会员经验记录分页") + @PreAuthenticated + public CommonResult> getExperienceRecordPage( + @Valid PageParam pageParam) { + PageResult pageResult = experienceLogService.getExperienceRecordPage( + getLoginUserId(), pageParam); + return success(MemberExperienceRecordConvert.INSTANCE.convertPage02(pageResult)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/level/AppMemberLevelController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/level/AppMemberLevelController.java new file mode 100644 index 000000000..d4a4483af --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/level/AppMemberLevelController.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.member.controller.app.level; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.member.controller.app.level.vo.level.AppMemberLevelRespVO; +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.service.level.MemberLevelService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "用户 App - 会员等级") +@RestController +@RequestMapping("/member/level") +@Validated +public class AppMemberLevelController { + + @Resource + private MemberLevelService levelService; + + @GetMapping("/list") + @Operation(summary = "获得会员等级列表") + public CommonResult> getLevelList() { + List result = levelService.getEnableLevelList(); + return success(MemberLevelConvert.INSTANCE.convertList02(result)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/level/vo/experience/AppMemberExperienceRecordRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/level/vo/experience/AppMemberExperienceRecordRespVO.java new file mode 100644 index 000000000..e2d7bb0c3 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/level/vo/experience/AppMemberExperienceRecordRespVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.member.controller.app.level.vo.experience; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "用户 App - 会员经验记录 Response VO") +@Data +public class AppMemberExperienceRecordRespVO { + + @Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "增加经验") + private String title; + + @Schema(description = "经验", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer experience; + + @Schema(description = "描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "下单增加 100 经验") + private String description; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/level/vo/level/AppMemberLevelRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/level/vo/level/AppMemberLevelRespVO.java new file mode 100644 index 000000000..fdade172f --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/level/vo/level/AppMemberLevelRespVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.member.controller.app.level.vo.level; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户 App - 会员等级 Response VO") +@Data +public class AppMemberLevelRespVO { + + @Schema(description = "等级名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + private String name; + + @Schema(description = "等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer level; + + @Schema(description = "升级经验", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer experience; + + @Schema(description = "享受折扣", requiredMode = Schema.RequiredMode.REQUIRED, example = "98") + private Integer discountPercent; + + @Schema(description = "等级图标", example = "https://www.iocoder.cn/yudao.jpg") + private String icon; + + @Schema(description = "等级背景图", example = "https://www.iocoder.cn/yudao.jpg") + private String backgroundUrl; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/point/AppMemberPointRecordController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/point/AppMemberPointRecordController.java new file mode 100644 index 000000000..1e63cc7d4 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/point/AppMemberPointRecordController.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.member.controller.app.point; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; +import cn.iocoder.yudao.module.member.controller.app.point.vo.AppMemberPointRecordRespVO; +import cn.iocoder.yudao.module.member.convert.point.MemberPointRecordConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO; +import cn.iocoder.yudao.module.member.service.point.MemberPointRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.validation.Valid; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "用户 App - 签到记录") +@RestController +@RequestMapping("/member/point/record") +@Validated +public class AppMemberPointRecordController { + + @Resource + private MemberPointRecordService pointRecordService; + + @GetMapping("/page") + @Operation(summary = "获得用户积分记录分页") + @PreAuthenticated + public CommonResult> getPointRecordPage(@Valid PageParam pageVO) { + PageResult pageResult = pointRecordService.getPointRecordPage(getLoginUserId(), pageVO); + return success(MemberPointRecordConvert.INSTANCE.convertPage02(pageResult)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/point/vo/AppMemberPointRecordRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/point/vo/AppMemberPointRecordRespVO.java new file mode 100644 index 000000000..ec95b2e02 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/point/vo/AppMemberPointRecordRespVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.member.controller.app.point.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "用户 App - 用户积分记录 Response VO") +@Data +public class AppMemberPointRecordRespVO { + + @Schema(description = "自增主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "31457") + private Long id;; + + @Schema(description = "积分标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "你猜") + private String title; + + @Schema(description = "积分描述", example = "你猜") + private String description; + + @Schema(description = "积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer point; + + @Schema(description = "发生时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/signin/AppMemberSignInConfigController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/signin/AppMemberSignInConfigController.java new file mode 100644 index 000000000..62a52e3d8 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/signin/AppMemberSignInConfigController.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.member.controller.app.signin; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.member.controller.app.signin.vo.config.AppMemberSignInConfigRespVO; +import cn.iocoder.yudao.module.member.convert.signin.MemberSignInConfigConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInConfigDO; +import cn.iocoder.yudao.module.member.service.signin.MemberSignInConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "用户 App - 签到规则") +@RestController +@RequestMapping("/member/sign-in/config") +@Validated +public class AppMemberSignInConfigController { + + @Resource + private MemberSignInConfigService signInConfigService; + + @GetMapping("/list") + @Operation(summary = "获得签到规则列表") + public CommonResult> getSignInConfigList() { + List pageResult = signInConfigService.getSignInConfigList(CommonStatusEnum.ENABLE.getStatus()); + return success(MemberSignInConfigConvert.INSTANCE.convertList02(pageResult)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/signin/AppMemberSignInRecordController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/signin/AppMemberSignInRecordController.java new file mode 100644 index 000000000..2f7afa042 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/signin/AppMemberSignInRecordController.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.module.member.controller.app.signin; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; +import cn.iocoder.yudao.module.member.controller.app.signin.vo.record.AppMemberSignInRecordRespVO; +import cn.iocoder.yudao.module.member.controller.app.signin.vo.record.AppMemberSignInRecordSummaryRespVO; +import cn.iocoder.yudao.module.member.convert.signin.MemberSignInRecordConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInRecordDO; +import cn.iocoder.yudao.module.member.service.signin.MemberSignInRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - 签到记录") +@RestController +@RequestMapping("/member/sign-in/record") +@Validated +public class AppMemberSignInRecordController { + + @Resource + private MemberSignInRecordService signInRecordService; + + @GetMapping("/get-summary") + @Operation(summary = "获得个人签到统计") + @PreAuthenticated + public CommonResult getSignInRecordSummary() { + return success(signInRecordService.getSignInRecordSummary(getLoginUserId())); + } + + @PostMapping("/create") + @Operation(summary = "签到") + @PreAuthenticated + public CommonResult createSignInRecord() { + MemberSignInRecordDO recordDO = signInRecordService.createSignRecord(getLoginUserId()); + return success(MemberSignInRecordConvert.INSTANCE.coverRecordToAppRecordVo(recordDO)); + } + + @GetMapping("/page") + @Operation(summary = "获得签到记录分页") + @PreAuthenticated + public CommonResult> getSignRecordPage(PageParam pageParam) { + PageResult pageResult = signInRecordService.getSignRecordPage(getLoginUserId(), pageParam); + return success(MemberSignInRecordConvert.INSTANCE.convertPage02(pageResult)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/signin/vo/config/AppMemberSignInConfigRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/signin/vo/config/AppMemberSignInConfigRespVO.java new file mode 100644 index 000000000..a18d3a28e --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/signin/vo/config/AppMemberSignInConfigRespVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.member.controller.app.signin.vo.config; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户 App - 签到规则 Response VO") +@Data +public class AppMemberSignInConfigRespVO { + + @Schema(description = "签到第 x 天", requiredMode = Schema.RequiredMode.REQUIRED, example = "7") + private Integer day; + + @Schema(description = "奖励积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer point; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/signin/vo/record/AppMemberSignInRecordRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/signin/vo/record/AppMemberSignInRecordRespVO.java new file mode 100644 index 000000000..2d910d0c6 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/signin/vo/record/AppMemberSignInRecordRespVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.member.controller.app.signin.vo.record; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "用户 App - 签到记录 Response VO") +@Data +public class AppMemberSignInRecordRespVO { + + @Schema(description = "第几天签到", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer day; + + @Schema(description = "签到的分数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer point; + + @Schema(description = "签到的经验", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer experience; + + @Schema(description = "签到时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/signin/vo/record/AppMemberSignInRecordSummaryRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/signin/vo/record/AppMemberSignInRecordSummaryRespVO.java new file mode 100644 index 000000000..30fb66a15 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/signin/vo/record/AppMemberSignInRecordSummaryRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.member.controller.app.signin.vo.record; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户 App - 个人签到统计 Response VO") +@Data +public class AppMemberSignInRecordSummaryRespVO { + + @Schema(description = "总签到天数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer totalDay; + + @Schema(description = "连续签到第 x 天", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") + private Integer continuousDay; + + @Schema(description = "今天是否已签到", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean todaySignIn; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/social/AppSocialUserController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/social/AppSocialUserController.java new file mode 100644 index 000000000..fd5e3fa83 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/social/AppSocialUserController.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.member.controller.app.social; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; +import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserBindReqVO; +import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO; +import cn.iocoder.yudao.module.member.convert.social.SocialUserConvert; +import cn.iocoder.yudao.module.system.api.social.SocialUserApi; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; + +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "用户 App - 社交用户") +@RestController +@RequestMapping("/system/social-user") +@Validated +public class AppSocialUserController { + + @Resource + private SocialUserApi socialUserApi; + + @PostMapping("/bind") + @Operation(summary = "社交绑定,使用 code 授权码") + public CommonResult socialBind(@RequestBody @Valid AppSocialUserBindReqVO reqVO) { + socialUserApi.bindSocialUser(SocialUserConvert.INSTANCE.convert(getLoginUserId(), UserTypeEnum.MEMBER.getValue(), reqVO)); + return CommonResult.success(true); + } + + @DeleteMapping("/unbind") + @Operation(summary = "取消社交绑定") + @PreAuthenticated + public CommonResult socialUnbind(@RequestBody AppSocialUserUnbindReqVO reqVO) { + socialUserApi.unbindSocialUser(SocialUserConvert.INSTANCE.convert(getLoginUserId(), UserTypeEnum.MEMBER.getValue(), reqVO)); + return CommonResult.success(true); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/social/vo/AppSocialUserBindReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/social/vo/AppSocialUserBindReqVO.java new file mode 100644 index 000000000..49141f935 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/social/vo/AppSocialUserBindReqVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.member.controller.app.social.vo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +@Schema(description = "用户 APP - 社交绑定 Request VO,使用 code 授权码") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AppSocialUserBindReqVO { + + @Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @InEnum(SocialTypeEnum.class) + @NotNull(message = "社交平台的类型不能为空") + private Integer type; + + @Schema(description = "授权码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "授权码不能为空") + private String code; + + @Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62") + @NotEmpty(message = "state 不能为空") + private String state; + +} \ No newline at end of file diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/social/vo/AppSocialUserUnbindReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/social/vo/AppSocialUserUnbindReqVO.java new file mode 100644 index 000000000..159cdcd33 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/social/vo/AppSocialUserUnbindReqVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.member.controller.app.social.vo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +@Schema(description = "用户 APP - 取消社交绑定 Request VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AppSocialUserUnbindReqVO { + + @Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @InEnum(SocialTypeEnum.class) + @NotNull(message = "社交平台的类型不能为空") + private Integer type; + + @Schema(description = "社交用户的 openid", requiredMode = Schema.RequiredMode.REQUIRED, example = "IPRmJ0wvBptiPIlGEZiPewGwiEiE") + @NotEmpty(message = "社交用户的 openid 不能为空") + private String openid; + +} \ No newline at end of file diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppMemberUserController.http b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppMemberUserController.http new file mode 100644 index 000000000..745556f75 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppMemberUserController.http @@ -0,0 +1,4 @@ +### 请求 /member/user/profile/get 接口 => 没有权限 +GET {{appApi}}/member/user/get +Authorization: Bearer test245 +tenant-id: {{appTenentId}} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppMemberUserController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppMemberUserController.java new file mode 100644 index 000000000..9322f9146 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppMemberUserController.java @@ -0,0 +1,76 @@ +package cn.iocoder.yudao.module.member.controller.app.user; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; +import cn.iocoder.yudao.module.member.controller.app.user.vo.*; +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.user.MemberUserDO; +import cn.iocoder.yudao.module.member.service.level.MemberLevelService; +import cn.iocoder.yudao.module.member.service.user.MemberUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "用户 APP - 用户个人中心") +@RestController +@RequestMapping("/member/user") +@Validated +@Slf4j +public class AppMemberUserController { + + @Resource + private MemberUserService userService; + @Resource + private MemberLevelService levelService; + + @GetMapping("/get") + @Operation(summary = "获得基本信息") + @PreAuthenticated + public CommonResult getUserInfo() { + MemberUserDO user = userService.getUser(getLoginUserId()); + MemberLevelDO level = levelService.getLevel(user.getLevelId()); + return success(MemberUserConvert.INSTANCE.convert(user, level)); + } + + @PutMapping("/update") + @Operation(summary = "修改基本信息") + @PreAuthenticated + public CommonResult updateUser(@RequestBody @Valid AppMemberUserUpdateReqVO reqVO) { + userService.updateUser(getLoginUserId(), reqVO); + return success(true); + } + + @PutMapping("/update-mobile") + @Operation(summary = "修改用户手机") + @PreAuthenticated + public CommonResult updateUserMobile(@RequestBody @Valid AppMemberUserUpdateMobileReqVO reqVO) { + userService.updateUserMobile(getLoginUserId(), reqVO); + return success(true); + } + + @PutMapping("/update-password") + @Operation(summary = "修改用户密码", description = "用户修改密码时使用") + @PreAuthenticated + public CommonResult updatePassword(@RequestBody @Valid AppMemberUserUpdatePasswordReqVO reqVO) { + userService.updateUserPassword(getLoginUserId(), reqVO); + return success(true); + } + + @PutMapping("/reset-password") + @Operation(summary = "重置密码", description = "用户忘记密码时使用") + public CommonResult resetPassword(@RequestBody @Valid AppMemberUserResetPasswordReqVO reqVO) { + userService.resetUserPassword(reqVO); + return success(true); + } + +} + diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserInfoRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserInfoRespVO.java new file mode 100644 index 000000000..25cceedc2 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserInfoRespVO.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.module.member.controller.app.user.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Schema(description = "用户 APP - 用户个人信息 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class AppMemberUserInfoRespVO { + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + private String nickname; + + @Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.png") + private String avatar; + + @Schema(description = "用户手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300") + private String mobile; + + @Schema(description = "积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer point; + + @Schema(description = "经验值", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Integer experience; + + @Schema(description = "用户等级") + private Level level; + + @Schema(description = "是否成为推广员", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean brokerageEnabled; + + @Schema(description = "用户 App - 会员等级") + @Data + public static class Level { + + @Schema(description = "等级编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "等级名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + private String name; + + @Schema(description = "等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer level; + + @Schema(description = "等级图标", example = "https://www.iocoder.cn/yudao.jpg") + private String icon; + + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserResetPasswordReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserResetPasswordReqVO.java new file mode 100644 index 000000000..22cbf55ee --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserResetPasswordReqVO.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.member.controller.app.user.vo; + +import cn.iocoder.yudao.framework.common.validation.Mobile; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Pattern; + +@Schema(description = "用户 APP - 重置密码 Request VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AppMemberUserResetPasswordReqVO { + + @Schema(description = "新密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "buzhidao") + @NotEmpty(message = "新密码不能为空") + @Length(min = 4, max = 16, message = "密码长度为 4-16 位") + private String password; + + @Schema(description = "手机验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "手机验证码不能为空") + @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位") + @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") + private String code; + + @Schema(description = "手机号",requiredMode = Schema.RequiredMode.REQUIRED,example = "15878962356") + @NotBlank(message = "手机号不能为空") + @Mobile + private String mobile; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateMobileReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateMobileReqVO.java new file mode 100644 index 000000000..6653506fc --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateMobileReqVO.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.member.controller.app.user.vo; + +import cn.iocoder.yudao.framework.common.validation.Mobile; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Pattern; + +@Schema(description = "用户 APP - 修改手机 Request VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AppMemberUserUpdateMobileReqVO { + + @Schema(description = "手机验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "手机验证码不能为空") + @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位") + @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") + private String code; + + @Schema(description = "手机号",requiredMode = Schema.RequiredMode.REQUIRED,example = "15823654487") + @NotBlank(message = "手机号不能为空") + @Length(min = 8, max = 11, message = "手机号码长度为 8-11 位") + @Mobile + private String mobile; + + @Schema(description = "原手机验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "原手机验证码不能为空") + @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位") + @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") + private String oldCode; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdatePasswordReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdatePasswordReqVO.java new file mode 100644 index 000000000..cc78ca832 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdatePasswordReqVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.member.controller.app.user.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Pattern; + +@Schema(description = "用户 APP - 修改密码 Request VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AppMemberUserUpdatePasswordReqVO { + + @Schema(description = "新密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "buzhidao") + @NotEmpty(message = "新密码不能为空") + @Length(min = 4, max = 16, message = "密码长度为 4-16 位") + private String password; + + @Schema(description = "手机验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "手机验证码不能为空") + @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位") + @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") + private String code; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateReqVO.java new file mode 100644 index 000000000..a676f6256 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateReqVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.member.controller.app.user.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.hibernate.validator.constraints.URL; + +@Schema(description = "用户 App - 会员用户更新 Request VO") +@Data +public class AppMemberUserUpdateReqVO { + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") + private String nickname; + + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/x.png") + @URL(message = "头像必须是 URL 格式") + private String avatar; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/package-info.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/package-info.java new file mode 100644 index 000000000..9e2888c69 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/package-info.java @@ -0,0 +1,6 @@ +/** + * 提供 RESTful API 给前端: + * 1. admin 包:提供给管理后台 yudao-ui-admin 前端项目 + * 2. app 包:提供给用户 APP yudao-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分 + */ +package cn.iocoder.yudao.module.member.controller; diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/address/AddressConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/address/AddressConvert.java new file mode 100644 index 000000000..fd5198e14 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/address/AddressConvert.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.member.convert.address; + +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; +import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; +import cn.iocoder.yudao.module.member.controller.admin.address.vo.AddressRespVO; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressCreateReqVO; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressRespVO; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressUpdateReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.address.MemberAddressDO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 用户收件地址 Convert + * + * @author 芋道源码 + */ +@Mapper +public interface AddressConvert { + + AddressConvert INSTANCE = Mappers.getMapper(AddressConvert.class); + + MemberAddressDO convert(AppAddressCreateReqVO bean); + + MemberAddressDO convert(AppAddressUpdateReqVO bean); + + @Mapping(source = "areaId", target = "areaName", qualifiedByName = "convertAreaIdToAreaName") + AppAddressRespVO convert(MemberAddressDO bean); + + List convertList(List list); + + AddressRespDTO convert02(MemberAddressDO bean); + + @Named("convertAreaIdToAreaName") + default String convertAreaIdToAreaName(Integer areaId) { + return AreaUtils.format(areaId); + } + + List convertList2(List list); + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java new file mode 100644 index 000000000..29e8f4fdc --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.member.convert.auth; + +import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; +import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO; +import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserResetPasswordReqVO; +import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenRespDTO; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeValidateReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO; +import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface AuthConvert { + + AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class); + + SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialLoginReqVO reqVO); + SocialUserUnbindReqDTO convert(Long userId, Integer userType, AppSocialUserUnbindReqVO reqVO); + + SmsCodeSendReqDTO convert(AppAuthSmsSendReqVO reqVO); + SmsCodeUseReqDTO convert(AppMemberUserResetPasswordReqVO reqVO, SmsSceneEnum scene, String usedIp); + SmsCodeUseReqDTO convert(AppAuthSmsLoginReqVO reqVO, Integer scene, String usedIp); + + AppAuthLoginRespVO convert(OAuth2AccessTokenRespDTO bean, String openid); + + SmsCodeValidateReqDTO convert(AppAuthSmsValidateReqVO bean); + + SocialWxJsapiSignatureRespDTO convert(SocialWxJsapiSignatureRespDTO bean); + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/config/MemberConfigConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/config/MemberConfigConvert.java new file mode 100644 index 000000000..9847645f9 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/config/MemberConfigConvert.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.member.convert.config; + +import cn.iocoder.yudao.module.member.api.config.dto.MemberConfigRespDTO; +import cn.iocoder.yudao.module.member.controller.admin.config.vo.MemberConfigRespVO; +import cn.iocoder.yudao.module.member.controller.admin.config.vo.MemberConfigSaveReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.config.MemberConfigDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * 会员配置 Convert + * + * @author QingX + */ +@Mapper +public interface MemberConfigConvert { + + MemberConfigConvert INSTANCE = Mappers.getMapper(MemberConfigConvert.class); + + MemberConfigRespVO convert(MemberConfigDO bean); + + MemberConfigDO convert(MemberConfigSaveReqVO bean); + + MemberConfigRespDTO convert01(MemberConfigDO config); +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/group/MemberGroupConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/group/MemberGroupConvert.java new file mode 100644 index 000000000..06f49d60c --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/group/MemberGroupConvert.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.member.convert.group; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupCreateReqVO; +import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupRespVO; +import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupSimpleRespVO; +import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupUpdateReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 用户分组 Convert + * + * @author owen + */ +@Mapper +public interface MemberGroupConvert { + + MemberGroupConvert INSTANCE = Mappers.getMapper(MemberGroupConvert.class); + + MemberGroupDO convert(MemberGroupCreateReqVO bean); + + MemberGroupDO convert(MemberGroupUpdateReqVO bean); + + MemberGroupRespVO convert(MemberGroupDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + + List convertSimpleList(List list); +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/level/MemberExperienceRecordConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/level/MemberExperienceRecordConvert.java new file mode 100644 index 000000000..93f864f08 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/level/MemberExperienceRecordConvert.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.member.convert.level; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceRecordRespVO; +import cn.iocoder.yudao.module.member.controller.app.level.vo.experience.AppMemberExperienceRecordRespVO; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberExperienceRecordDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 会员经验记录 Convert + * + * @author owen + */ +@Mapper +public interface MemberExperienceRecordConvert { + + MemberExperienceRecordConvert INSTANCE = Mappers.getMapper(MemberExperienceRecordConvert.class); + + MemberExperienceRecordRespVO convert(MemberExperienceRecordDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + + MemberExperienceRecordDO convert(Long userId, Integer experience, Integer totalExperience, + String bizId, Integer bizType, + String title, String description); + + PageResult convertPage02(PageResult page); + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/level/MemberLevelConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/level/MemberLevelConvert.java new file mode 100644 index 000000000..f2282815e --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/level/MemberLevelConvert.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.member.convert.level; + +import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.level.MemberLevelCreateReqVO; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.level.MemberLevelRespVO; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.level.MemberLevelSimpleRespVO; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.level.MemberLevelUpdateReqVO; +import cn.iocoder.yudao.module.member.controller.app.level.vo.level.AppMemberLevelRespVO; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 会员等级 Convert + * + * @author owen + */ +@Mapper +public interface MemberLevelConvert { + + MemberLevelConvert INSTANCE = Mappers.getMapper(MemberLevelConvert.class); + + MemberLevelDO convert(MemberLevelCreateReqVO bean); + + MemberLevelDO convert(MemberLevelUpdateReqVO bean); + + MemberLevelRespVO convert(MemberLevelDO bean); + + List convertList(List list); + + List convertSimpleList(List list); + + List convertList02(List list); + + MemberLevelRespDTO convert02(MemberLevelDO bean); + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/level/MemberLevelRecordConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/level/MemberLevelRecordConvert.java new file mode 100644 index 000000000..d01f1b63c --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/level/MemberLevelRecordConvert.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.member.convert.level; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.record.MemberLevelRecordRespVO; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelRecordDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 会员等级记录 Convert + * + * @author owen + */ +@Mapper +public interface MemberLevelRecordConvert { + + MemberLevelRecordConvert INSTANCE = Mappers.getMapper(MemberLevelRecordConvert.class); + + MemberLevelRecordRespVO convert(MemberLevelRecordDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + + default MemberLevelRecordDO copyTo(MemberLevelDO from, MemberLevelRecordDO to) { + if (from != null) { + to.setLevelId(from.getId()); + to.setLevel(from.getLevel()); + to.setDiscountPercent(from.getDiscountPercent()); + to.setExperience(from.getExperience()); + } + return to; + } +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/package-info.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/package-info.java new file mode 100644 index 000000000..6523a6656 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/package-info.java @@ -0,0 +1,6 @@ +/** + * 提供 POJO 类的实体转换 + * + * 目前使用 MapStruct 框架 + */ +package cn.iocoder.yudao.module.member.convert; diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/point/MemberPointRecordConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/point/MemberPointRecordConvert.java new file mode 100644 index 000000000..1d2c146d2 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/point/MemberPointRecordConvert.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.member.convert.point; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod.MemberPointRecordRespVO; +import cn.iocoder.yudao.module.member.controller.app.point.vo.AppMemberPointRecordRespVO; +import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * 用户积分记录 Convert + * + * @author QingX + */ +@Mapper +public interface MemberPointRecordConvert { + + MemberPointRecordConvert INSTANCE = Mappers.getMapper(MemberPointRecordConvert.class); + + default PageResult convertPage(PageResult pageResult, List users) { + PageResult voPageResult = convertPage(pageResult); + // user 拼接 + Map userMap = convertMap(users, MemberUserRespDTO::getId); + voPageResult.getList().forEach(record -> MapUtils.findAndThen(userMap, record.getUserId(), + memberUserRespDTO -> record.setNickname(memberUserRespDTO.getNickname()))); + return voPageResult; + } + PageResult convertPage(PageResult pageResult); + + PageResult convertPage02(PageResult pageResult); + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/signin/MemberSignInConfigConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/signin/MemberSignInConfigConvert.java new file mode 100644 index 000000000..5acd87151 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/signin/MemberSignInConfigConvert.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.member.convert.signin; + +import cn.iocoder.yudao.module.member.controller.admin.signin.vo.config.MemberSignInConfigCreateReqVO; +import cn.iocoder.yudao.module.member.controller.admin.signin.vo.config.MemberSignInConfigRespVO; +import cn.iocoder.yudao.module.member.controller.admin.signin.vo.config.MemberSignInConfigUpdateReqVO; +import cn.iocoder.yudao.module.member.controller.app.signin.vo.config.AppMemberSignInConfigRespVO; +import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInConfigDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 签到规则 Convert + * + * @author QingX + */ +@Mapper +public interface MemberSignInConfigConvert { + + MemberSignInConfigConvert INSTANCE = Mappers.getMapper(MemberSignInConfigConvert.class); + + MemberSignInConfigDO convert(MemberSignInConfigCreateReqVO bean); + + MemberSignInConfigDO convert(MemberSignInConfigUpdateReqVO bean); + + MemberSignInConfigRespVO convert(MemberSignInConfigDO bean); + + List convertList(List list); + + List convertList02(List list); + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/signin/MemberSignInRecordConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/signin/MemberSignInRecordConvert.java new file mode 100644 index 000000000..0526ed64a --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/signin/MemberSignInRecordConvert.java @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.module.member.convert.signin; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.member.controller.admin.signin.vo.record.MemberSignInRecordRespVO; +import cn.iocoder.yudao.module.member.controller.app.signin.vo.record.AppMemberSignInRecordRespVO; +import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInConfigDO; +import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInRecordDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * 签到记录 Convert + * + * @author 芋道源码 + */ +@Mapper +public interface MemberSignInRecordConvert { + + MemberSignInRecordConvert INSTANCE = Mappers.getMapper(MemberSignInRecordConvert.class); + + default PageResult convertPage(PageResult pageResult, List users) { + PageResult voPageResult = convertPage(pageResult); + // user 拼接 + Map userMap = convertMap(users, MemberUserRespDTO::getId); + voPageResult.getList().forEach(record -> MapUtils.findAndThen(userMap, record.getUserId(), + memberUserRespDTO -> record.setNickname(memberUserRespDTO.getNickname()))); + return voPageResult; + } + + PageResult convertPage(PageResult pageResult); + + PageResult convertPage02(PageResult pageResult); + + AppMemberSignInRecordRespVO coverRecordToAppRecordVo(MemberSignInRecordDO memberSignInRecordDO); + + default MemberSignInRecordDO convert(Long userId, MemberSignInRecordDO firstRecord, List signInConfigs) { + // 1. 计算今天是第几天签到 + long day = ChronoUnit.DAYS.between(firstRecord.getCreateTime(), LocalDateTime.now()); + // 2. 初始化签到信息 + // TODO @puhui999:signInRecord=》record + MemberSignInRecordDO signInRecord = new MemberSignInRecordDO().setUserId(userId) + .setDay(Integer.parseInt(Long.toString(day))) // 设置签到天数 TODO @puhui999:day 应该跟着第几天签到走;不是累加哈;另外 long 转 int,应该 (int) day 就可以了。。。 + .setPoint(0) // 设置签到积分默认为 + .setExperience(0); // 设置签到经验默认为 0 + // 3. 获取签到对应的积分数 + MemberSignInConfigDO lastConfig = signInConfigs.get(signInConfigs.size() - 1); // 最大签到天数 + if (day > lastConfig.getDay()) { // 超出范围按第一天的经验计算 + // TODO @puhui999:不能直接取 0,万一它 day 不匹配哈。就是第一天没奖励。。。 + signInRecord.setPoint(signInConfigs.get(0).getPoint()); + signInRecord.setExperience(signInConfigs.get(0).getExperience()); + return signInRecord; + } + // TODO @puhui999:signInConfig 可以改成 config; + MemberSignInConfigDO signInConfig = CollUtil.findOne(signInConfigs, config -> ObjUtil.equal(config.getDay(), day)); + if (signInConfig == null) { + return signInRecord; + } + signInRecord.setPoint(signInConfig.getPoint()); + signInRecord.setExperience(signInConfig.getExperience()); + return signInRecord; + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/social/SocialUserConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/social/SocialUserConvert.java new file mode 100644 index 000000000..3c9288ba8 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/social/SocialUserConvert.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.member.convert.social; + +import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserBindReqVO; +import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface SocialUserConvert { + + SocialUserConvert INSTANCE = Mappers.getMapper(SocialUserConvert.class); + + SocialUserBindReqDTO convert(Long userId, Integer userType, AppSocialUserBindReqVO reqVO); + + SocialUserUnbindReqDTO convert(Long userId, Integer userType, AppSocialUserUnbindReqVO reqVO); + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/tag/MemberTagConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/tag/MemberTagConvert.java new file mode 100644 index 000000000..9d3a41f1a --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/tag/MemberTagConvert.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.member.convert.tag; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.controller.admin.tag.vo.MemberTagCreateReqVO; +import cn.iocoder.yudao.module.member.controller.admin.tag.vo.MemberTagRespVO; +import cn.iocoder.yudao.module.member.controller.admin.tag.vo.MemberTagUpdateReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 会员标签 Convert + * + * @author 芋道源码 + */ +@Mapper +public interface MemberTagConvert { + + MemberTagConvert INSTANCE = Mappers.getMapper(MemberTagConvert.class); + + MemberTagDO convert(MemberTagCreateReqVO bean); + + MemberTagDO convert(MemberTagUpdateReqVO bean); + + MemberTagRespVO convert(MemberTagDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/user/MemberUserConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/user/MemberUserConvert.java new file mode 100644 index 000000000..aae9a7601 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/user/MemberUserConvert.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.member.convert.user; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserRespVO; +import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO; +import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserInfoRespVO; +import cn.iocoder.yudao.module.member.convert.address.AddressConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO; +import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO; +import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +@Mapper(uses = {AddressConvert.class}) +public interface MemberUserConvert { + + MemberUserConvert INSTANCE = Mappers.getMapper(MemberUserConvert.class); + + AppMemberUserInfoRespVO convert(MemberUserDO bean); + + @Mapping(source = "level", target = "level") + @Mapping(source = "bean.experience", target = "experience") + AppMemberUserInfoRespVO convert(MemberUserDO bean, MemberLevelDO level); + + MemberUserRespDTO convert2(MemberUserDO bean); + + List convertList2(List list); + + MemberUserDO convert(MemberUserUpdateReqVO bean); + + PageResult convertPage(PageResult page); + + @Mapping(source = "areaId", target = "areaName", qualifiedByName = "convertAreaIdToAreaName") + MemberUserRespVO convert03(MemberUserDO bean); + + default PageResult convertPage(PageResult pageResult, + List tags, + List levels, + List groups) { + PageResult result = convertPage(pageResult); + // 处理关联数据 + Map tagMap = convertMap(tags, MemberTagDO::getId, MemberTagDO::getName); + Map levelMap = convertMap(levels, MemberLevelDO::getId, MemberLevelDO::getName); + Map groupMap = convertMap(groups, MemberGroupDO::getId, MemberGroupDO::getName); + // 填充关联数据 + result.getList().forEach(user -> { + user.setTagNames(convertList(user.getTagIds(), tagMap::get)); + user.setLevelName(levelMap.get(user.getLevelId())); + user.setGroupName(groupMap.get(user.getGroupId())); + }); + return result; + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md new file mode 100644 index 000000000..8153487b7 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md @@ -0,0 +1 @@ + diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/MemberAddressDO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/MemberAddressDO.java new file mode 100644 index 000000000..f2e43b563 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/MemberAddressDO.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.member.dal.dataobject.address; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 用户收件地址 DO + * + * @author 芋道源码 + */ +@TableName("member_address") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MemberAddressDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 用户编号 + */ + private Long userId; + /** + * 收件人名称 + */ + private String name; + /** + * 手机号 + */ + private String mobile; + /** + * 地区编号 + */ + private Long areaId; + /** + * 收件详细地址 + */ + private String detailAddress; + /** + * 是否默认 + * + * true - 默认收件地址 + */ + private Boolean defaultStatus; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/config/MemberConfigDO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/config/MemberConfigDO.java new file mode 100644 index 000000000..6efb4a1c0 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/config/MemberConfigDO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.member.dal.dataobject.config; + +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 QingX + */ +@TableName(value = "member_config", autoResultMap = true) +@KeySequence("member_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MemberConfigDO extends BaseDO { + + /** + * 自增主键 + */ + @TableId + private Long id; + /** + * 积分抵扣开关 + */ + private Boolean pointTradeDeductEnable; + /** + * 积分抵扣,单位:分 + * + * 1 积分抵扣多少分 + */ + private Integer pointTradeDeductUnitPrice; + /** + * 积分抵扣最大值 + */ + private Integer pointTradeDeductMaxPrice; + /** + * 1 元赠送多少分 + */ + private Integer pointTradeGivePoint; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/group/MemberGroupDO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/group/MemberGroupDO.java new file mode 100644 index 000000000..c9a82ab5d --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/group/MemberGroupDO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.member.dal.dataobject.group; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 用户分组 DO + * + * @author owen + */ +@TableName("member_group") +@KeySequence("member_group_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MemberGroupDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 名称 + */ + private String name; + /** + * 备注 + */ + private String remark; + /** + * 状态 + *

+ * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/level/MemberExperienceRecordDO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/level/MemberExperienceRecordDO.java new file mode 100644 index 000000000..d7c06d4ba --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/level/MemberExperienceRecordDO.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.member.dal.dataobject.level; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; +import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum; +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_experience_record") +@KeySequence("member_experience_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MemberExperienceRecordDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 用户编号 + * + * 关联 {@link MemberUserDO#getId()} 字段 + */ + private Long userId; + /** + * 业务类型 + *

+ * 枚举 {@link MemberExperienceBizTypeEnum} + */ + private Integer bizType; + /** + * 业务编号 + */ + private String bizId; + /** + * 标题 + */ + private String title; + /** + * 描述 + */ + private String description; + /** + * 经验 + */ + private Integer experience; + /** + * 变更后的经验 + */ + private Integer totalExperience; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/level/MemberLevelDO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/level/MemberLevelDO.java new file mode 100644 index 000000000..05035ffe5 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/level/MemberLevelDO.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.member.dal.dataobject.level; + +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_level") +@KeySequence("member_level_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MemberLevelDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 等级名称 + */ + private String name; + /** + * 等级 + */ + private Integer level; + /** + * 升级经验 + */ + private Integer experience; + /** + * 享受折扣 + */ + private Integer discountPercent; + + /** + * 等级图标 + */ + private String icon; + /** + * 等级背景图 + */ + private String backgroundUrl; + /** + * 状态 + *

+ * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/level/MemberLevelRecordDO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/level/MemberLevelRecordDO.java new file mode 100644 index 000000000..8b5451d45 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/level/MemberLevelRecordDO.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.member.dal.dataobject.level; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; +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_level_record") +@KeySequence("member_level_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MemberLevelRecordDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 用户编号 + * + * 关联 {@link MemberUserDO#getId()} 字段 + */ + private Long userId; + /** + * 等级编号 + * + * 关联 {@link MemberLevelDO#getId()} 字段 + */ + private Long levelId; + /** + * 会员等级 + * + * 冗余 {@link MemberLevelDO#getLevel()} 字段 + */ + private Integer level; + /** + * 享受折扣 + */ + private Integer discountPercent; + /** + * 升级经验 + */ + private Integer experience; + /** + * 会员此时的经验 + */ + private Integer userExperience; + /** + * 备注 + */ + private String remark; + /** + * 描述 + */ + private String description; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/point/MemberPointRecordDO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/point/MemberPointRecordDO.java new file mode 100644 index 000000000..f884f08d8 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/point/MemberPointRecordDO.java @@ -0,0 +1,69 @@ +package cn.iocoder.yudao.module.member.dal.dataobject.point; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 用户积分记录 DO + * + * @author QingX + */ +@TableName("member_point_record") +@KeySequence("member_point_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MemberPointRecordDO extends BaseDO { + + /** + * 自增主键 + */ + @TableId + private Long id; + /** + * 用户编号 + * + * 对应 MemberUserDO 的 id 属性 + */ + private Long userId; + + /** + * 业务编码 + */ + private String bizId; + /** + * 业务类型 + * + * 枚举 {@link MemberPointBizTypeEnum} + */ + private Integer bizType; + + /** + * 积分标题 + */ + private String title; + /** + * 积分描述 + */ + private String description; + + /** + * 变动积分 + * + * 1、正数表示获得积分 + * 2、负数表示消耗积分 + */ + private Integer point; + /** + * 变动后的积分 + */ + private Integer totalPoint; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/signin/MemberSignInConfigDO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/signin/MemberSignInConfigDO.java new file mode 100644 index 000000000..76d55c9bf --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/signin/MemberSignInConfigDO.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.member.dal.dataobject.signin; + +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 QingX + */ +@TableName("member_sign_in_config") +@KeySequence("member_sign_in_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MemberSignInConfigDO extends BaseDO { + + /** + * 规则自增主键 + */ + @TableId + private Long id; + /** + * 签到第 x 天 + */ + private Integer day; + /** + * 奖励积分 + */ + private Integer point; + /** + * 奖励经验 + */ + private Integer experience; + + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/signin/MemberSignInRecordDO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/signin/MemberSignInRecordDO.java new file mode 100644 index 000000000..b07b5efbc --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/signin/MemberSignInRecordDO.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.member.dal.dataobject.signin; + +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 芋道源码 + */ +@TableName("member_sign_in_record") +@KeySequence("member_sign_in_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MemberSignInRecordDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 签到用户 + */ + private Long userId; + /** + * 第几天签到 + */ + private Integer day; + /** + * 签到的积分 + */ + private Integer point; + /** + * 签到的经验 + */ + private Integer experience; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/tag/MemberTagDO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/tag/MemberTagDO.java new file mode 100644 index 000000000..b984064e0 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/tag/MemberTagDO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.member.dal.dataobject.tag; + +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 芋道源码 + */ +@TableName("member_tag") +@KeySequence("member_tag_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MemberTagDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 标签名称 + */ + private String name; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/MemberUserDO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/MemberUserDO.java new file mode 100644 index 000000000..97ddc191d --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/MemberUserDO.java @@ -0,0 +1,145 @@ +package cn.iocoder.yudao.module.member.dal.dataobject.user; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.enums.TerminalEnum; +import cn.iocoder.yudao.framework.ip.core.Area; +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; +import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO; +import cn.iocoder.yudao.module.system.enums.common.SexEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 会员用户 DO + * + * uk_mobile 索引:基于 {@link #mobile} 字段 + * + * @author 芋道源码 + */ +@TableName(value = "member_user", autoResultMap = true) +@KeySequence("member_user_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MemberUserDO extends TenantBaseDO { + + // ========== 账号信息 ========== + + /** + * 用户ID + */ + @TableId + private Long id; + /** + * 手机 + */ + private String mobile; + /** + * 加密后的密码 + * + * 因为目前使用 {@link BCryptPasswordEncoder} 加密器,所以无需自己处理 salt 盐 + */ + private String password; + /** + * 帐号状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 注册 IP + */ + private String registerIp; + /** + * 注册终端 + * 枚举 {@link TerminalEnum} + */ + private Integer registerTerminal; + /** + * 最后登录IP + */ + private String loginIp; + /** + * 最后登录时间 + */ + private LocalDateTime loginDate; + + // ========== 基础信息 ========== + + /** + * 用户昵称 + */ + private String nickname; + /** + * 用户头像 + */ + private String avatar; + + /** + * 真实名字 + */ + private String name; + /** + * 性别 + * + * 枚举 {@link SexEnum} + */ + private Integer sex; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 所在地 + * + * 关联 {@link Area#getId()} 字段 + */ + private Integer areaId; + /** + * 用户备注 + */ + private String mark; + + // ========== 其它信息 ========== + + /** + * 积分 + */ + private Integer point; + // TODO 疯狂:增加一个 totalPoint;个人信息接口要返回 + + /** + * 会员标签列表,以逗号分隔 + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List tagIds; + + /** + * 会员级别编号 + * + * 关联 {@link MemberLevelDO#getId()} 字段 + */ + private Long levelId; + /** + * 会员经验 + */ + private Integer experience; + /** + * 用户分组编号 + * + * 关联 {@link MemberGroupDO#getId()} 字段 + */ + private Long groupId; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/address/MemberAddressMapper.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/address/MemberAddressMapper.java new file mode 100644 index 000000000..3df68c51a --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/address/MemberAddressMapper.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.member.dal.mysql.address; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.member.dal.dataobject.address.MemberAddressDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface MemberAddressMapper extends BaseMapperX { + + default MemberAddressDO selectByIdAndUserId(Long id, Long userId) { + return selectOne(MemberAddressDO::getId, id, MemberAddressDO::getUserId, userId); + } + + default List selectListByUserIdAndDefaulted(Long userId, Boolean defaulted) { + return selectList(new LambdaQueryWrapperX().eq(MemberAddressDO::getUserId, userId) + .eqIfPresent(MemberAddressDO::getDefaultStatus, defaulted)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/config/MemberConfigMapper.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/config/MemberConfigMapper.java new file mode 100644 index 000000000..e03938378 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/config/MemberConfigMapper.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.member.dal.mysql.config; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.member.dal.dataobject.config.MemberConfigDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 积分设置 Mapper + * + * @author QingX + */ +@Mapper +public interface MemberConfigMapper extends BaseMapperX { +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/group/MemberGroupMapper.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/group/MemberGroupMapper.java new file mode 100644 index 000000000..da4f7b7a8 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/group/MemberGroupMapper.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.member.dal.mysql.group; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupPageReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 用户分组 Mapper + * + * @author owen + */ +@Mapper +public interface MemberGroupMapper extends BaseMapperX { + + default PageResult selectPage(MemberGroupPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(MemberGroupDO::getName, reqVO.getName()) + .eqIfPresent(MemberGroupDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(MemberGroupDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(MemberGroupDO::getId)); + } + + default List selectListByStatus(Integer status) { + return selectList(MemberGroupDO::getStatus, status); + } +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/level/MemberExperienceRecordMapper.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/level/MemberExperienceRecordMapper.java new file mode 100644 index 000000000..4e5f6f567 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/level/MemberExperienceRecordMapper.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.member.dal.mysql.level; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceRecordPageReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberExperienceRecordDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * 会员经验记录 Mapper + * + * @author owen + */ +@Mapper +public interface MemberExperienceRecordMapper extends BaseMapperX { + + default PageResult selectPage(MemberExperienceRecordPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(MemberExperienceRecordDO::getUserId, reqVO.getUserId()) + .eqIfPresent(MemberExperienceRecordDO::getBizId, reqVO.getBizId()) + .eqIfPresent(MemberExperienceRecordDO::getBizType, reqVO.getBizType()) + .eqIfPresent(MemberExperienceRecordDO::getTitle, reqVO.getTitle()) + .betweenIfPresent(MemberExperienceRecordDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(MemberExperienceRecordDO::getId)); + } + + default PageResult selectPage(Long userId, PageParam pageParam) { + return selectPage(pageParam, new LambdaQueryWrapper() + .eq(MemberExperienceRecordDO::getUserId, userId) + .orderByDesc(MemberExperienceRecordDO::getId)); + } +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/level/MemberLevelMapper.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/level/MemberLevelMapper.java new file mode 100644 index 000000000..d2dcb6cb4 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/level/MemberLevelMapper.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.member.dal.mysql.level; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.level.MemberLevelListReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 会员等级 Mapper + * + * @author owen + */ +@Mapper +public interface MemberLevelMapper extends BaseMapperX { + + default List selectList(MemberLevelListReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .likeIfPresent(MemberLevelDO::getName, reqVO.getName()) + .eqIfPresent(MemberLevelDO::getStatus, reqVO.getStatus()) + .orderByAsc(MemberLevelDO::getLevel)); + } + + + default List selectListByStatus(Integer status) { + return selectList(new LambdaQueryWrapperX() + .eq(MemberLevelDO::getStatus, status) + .orderByAsc(MemberLevelDO::getLevel)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/level/MemberLevelRecordMapper.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/level/MemberLevelRecordMapper.java new file mode 100644 index 000000000..6808b957a --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/level/MemberLevelRecordMapper.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.member.dal.mysql.level; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.record.MemberLevelRecordPageReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelRecordDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 会员等级记录 Mapper + * + * @author owen + */ +@Mapper +public interface MemberLevelRecordMapper extends BaseMapperX { + + default PageResult selectPage(MemberLevelRecordPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(MemberLevelRecordDO::getUserId, reqVO.getUserId()) + .eqIfPresent(MemberLevelRecordDO::getLevelId, reqVO.getLevelId()) + .betweenIfPresent(MemberLevelRecordDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(MemberLevelRecordDO::getId)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/point/MemberPointRecordMapper.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/point/MemberPointRecordMapper.java new file mode 100644 index 000000000..5c3370929 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/point/MemberPointRecordMapper.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.member.dal.mysql.point; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +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.point.vo.recrod.MemberPointRecordPageReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Set; + +/** + * 用户积分记录 Mapper + * + * @author QingX + */ +@Mapper +public interface MemberPointRecordMapper extends BaseMapperX { + + default PageResult selectPage(MemberPointRecordPageReqVO reqVO, Set userIds) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .inIfPresent(MemberPointRecordDO::getUserId, userIds) + .eqIfPresent(MemberPointRecordDO::getUserId, reqVO.getUserId()) + .eqIfPresent(MemberPointRecordDO::getBizType, reqVO.getBizType()) + .likeIfPresent(MemberPointRecordDO::getTitle, reqVO.getTitle()) + .orderByDesc(MemberPointRecordDO::getId)); + } + + default PageResult selectPage(Long userId, PageParam pageVO) { + return selectPage(pageVO, new LambdaQueryWrapperX() + .eq(MemberPointRecordDO::getUserId, userId) + .orderByDesc(MemberPointRecordDO::getId)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/signin/MemberSignInConfigMapper.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/signin/MemberSignInConfigMapper.java new file mode 100644 index 000000000..211ead33d --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/signin/MemberSignInConfigMapper.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.member.dal.mysql.signin; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInConfigDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 签到规则 Mapper + * + * @author QingX + */ +@Mapper +public interface MemberSignInConfigMapper extends BaseMapperX { + + default MemberSignInConfigDO selectByDay(Integer day) { + return selectOne(MemberSignInConfigDO::getDay, day); + } + + default List selectListByStatus(Integer status) { + return selectList(MemberSignInConfigDO::getStatus, status); + } +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/signin/MemberSignInRecordMapper.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/signin/MemberSignInRecordMapper.java new file mode 100644 index 000000000..01fe75c14 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/signin/MemberSignInRecordMapper.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.member.dal.mysql.signin; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +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.signin.vo.record.MemberSignInRecordPageReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInRecordDO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Set; + +/** + * 签到记录 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface MemberSignInRecordMapper extends BaseMapperX { + + default PageResult selectPage(MemberSignInRecordPageReqVO reqVO, Set userIds) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .inIfPresent(MemberSignInRecordDO::getUserId, userIds) + .eqIfPresent(MemberSignInRecordDO::getUserId, reqVO.getUserId()) + .eqIfPresent(MemberSignInRecordDO::getDay, reqVO.getDay()) + .betweenIfPresent(MemberSignInRecordDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(MemberSignInRecordDO::getId)); + } + + default PageResult selectPage(Long userId, PageParam pageParam) { + return selectPage(pageParam, new LambdaQueryWrapperX() + .eq(MemberSignInRecordDO::getUserId, userId) + .orderByDesc(MemberSignInRecordDO::getId)); + } + + // TODO @puhui999:这 2 个方法,是不是一个 first;一个 last 就可以了。。 + /** + * 获取用户最近的签到记录信息,根据签到时间倒序 + * + * @param userId 用户编号 + * @return 签到记录列表 + */ + default MemberSignInRecordDO selectLastRecordByUserIdDesc(Long userId) { + return selectOne(new QueryWrapper() + .eq("user_id", userId) + .orderByDesc("create_time") + .last("limit 1")); + } + + /** + * 获取用户最早的签到记录信息,根据签到时间倒序 + * + * @param userId 用户编号 + * @return 签到记录列表 + */ + default MemberSignInRecordDO selectLastRecordByUserIdAsc(Long userId) { + return selectOne(new QueryWrapper() + .eq("user_id", userId) + .orderByAsc("create_time") + .last("limit 1")); + } + + default Long selectCountByUserId(Long userId) { + // TODO @puhui999:可以使用 selectCount 里面允许传递字段的方法 + return selectCount(new LambdaQueryWrapperX() + .eq(MemberSignInRecordDO::getUserId, userId)); + } + + /** + * 获取用户的签到记录列表信息,根据签到时间倒序 + * + * @param userId 用户编号 + * @return 签到记录信息 + */ + // TODO @puhui999:这个排序,可以交给 service 哈; + default List selectListByUserId(Long userId) { + return selectList(new LambdaQueryWrapperX() + .eq(MemberSignInRecordDO::getUserId, userId) + .orderByDesc(MemberSignInRecordDO::getCreateTime)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/tag/MemberTagMapper.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/tag/MemberTagMapper.java new file mode 100644 index 000000000..f4723e282 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/tag/MemberTagMapper.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.member.dal.mysql.tag; + +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.tag.vo.MemberTagPageReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 会员标签 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface MemberTagMapper extends BaseMapperX { + + default PageResult selectPage(MemberTagPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(MemberTagDO::getName, reqVO.getName()) + .betweenIfPresent(MemberTagDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(MemberTagDO::getId)); + } + + default MemberTagDO selelctByName(String name) { + return selectOne(MemberTagDO::getName, name); + } +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java new file mode 100644 index 000000000..3f871020c --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java @@ -0,0 +1,96 @@ +package cn.iocoder.yudao.module.member.dal.mysql.user; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +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.user.vo.MemberUserPageReqVO; +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 java.util.List; +import java.util.stream.Collectors; + +/** + * 会员 User Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface MemberUserMapper extends BaseMapperX { + + default MemberUserDO selectByMobile(String mobile) { + return selectOne(MemberUserDO::getMobile, mobile); + } + + default List selectListByNicknameLike(String nickname) { + return selectList(new LambdaQueryWrapperX() + .likeIfPresent(MemberUserDO::getNickname, nickname)); + } + + default PageResult selectPage(MemberUserPageReqVO reqVO) { + // 处理 tagIds 过滤条件 + String tagIdSql = ""; + if (CollUtil.isNotEmpty(reqVO.getTagIds())) { + tagIdSql = reqVO.getTagIds().stream() + .map(tagId -> "FIND_IN_SET(" + tagId + ", tag_ids)") + .collect(Collectors.joining(" OR ")); + } + // 分页查询 + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(MemberUserDO::getMobile, reqVO.getMobile()) + .betweenIfPresent(MemberUserDO::getLoginDate, reqVO.getLoginDate()) + .likeIfPresent(MemberUserDO::getNickname, reqVO.getNickname()) + .betweenIfPresent(MemberUserDO::getCreateTime, reqVO.getCreateTime()) + .eqIfPresent(MemberUserDO::getLevelId, reqVO.getLevelId()) + .eqIfPresent(MemberUserDO::getGroupId, reqVO.getGroupId()) + .apply(StrUtil.isNotEmpty(tagIdSql), tagIdSql) + .orderByDesc(MemberUserDO::getId)); + } + + default Long selectCountByGroupId(Long groupId) { + return selectCount(MemberUserDO::getGroupId, groupId); + } + + default Long selectCountByLevelId(Long levelId) { + return selectCount(MemberUserDO::getLevelId, levelId); + } + + default Long selectCountByTagId(Long tagId) { + return selectCount(new LambdaQueryWrapperX() + .apply("FIND_IN_SET({0}, tag_ids)", tagId)); + } + + /** + * 更新用户积分(增加) + * + * @param id 用户编号 + * @param incrCount 增加积分(正数) + */ + default void updatePointIncr(Long id, Integer incrCount) { + Assert.isTrue(incrCount > 0); + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" point = point + " + incrCount) + .eq(MemberUserDO::getId, id); + update(null, lambdaUpdateWrapper); + } + + /** + * 更新用户积分(减少) + * + * @param id 用户编号 + * @param incrCount 增加积分(负数) + * @return 更新行数 + */ + default int updatePointDecr(Long id, Integer incrCount) { + Assert.isTrue(incrCount < 0); + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" point = point + " + incrCount) // 负数,所以使用 + 号 + .eq(MemberUserDO::getId, id); + return update(null, lambdaUpdateWrapper); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/package-info.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/package-info.java new file mode 100644 index 000000000..a45c2a161 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/package-info.java @@ -0,0 +1,9 @@ +/** + * DAL = Data Access Layer 数据访问层 + * 1. data object:数据对象 + * 2. redis:Redis 的 CRUD 操作 + * 3. mysql:MySQL 的 CRUD 操作 + * + * 其中,MySQL 的表以 member_ 作为前缀 + */ +package cn.iocoder.yudao.module.member.dal; diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/redis/package-info.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/redis/package-info.java new file mode 100644 index 000000000..8dfa9fb20 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/redis/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位,后续有类后,可以删除,避免 package 无法提交到 Git 上 + */ +package cn.iocoder.yudao.module.member.dal.redis; diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/framework/package-info.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/framework/package-info.java new file mode 100644 index 000000000..7e9ca95de --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/framework/package-info.java @@ -0,0 +1,6 @@ +/** + * 属于 member 模块的 framework 封装 + * + * @author 芋道源码 + */ +package cn.iocoder.yudao.module.member.framework; diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/framework/web/config/MemberWebConfiguration.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/framework/web/config/MemberWebConfiguration.java new file mode 100644 index 000000000..82c70034e --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/framework/web/config/MemberWebConfiguration.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.member.framework.web.config; + +import cn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration; +import org.springdoc.core.GroupedOpenApi; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * member 模块的 web 组件的 Configuration + * + * @author 芋道源码 + */ +@Configuration(proxyBeanMethods = false) +public class MemberWebConfiguration { + + /** + * member 模块的 API 分组 + */ + @Bean + public GroupedOpenApi memberGroupedOpenApi() { + return YudaoSwaggerAutoConfiguration.buildGroupedOpenApi("member"); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/framework/web/package-info.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/framework/web/package-info.java new file mode 100644 index 000000000..3a964cfc2 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/framework/web/package-info.java @@ -0,0 +1,4 @@ +/** + * member 模块的 web 配置 + */ +package cn.iocoder.yudao.module.member.framework.web; diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/mq/message/user/UserCreateMessage.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/mq/message/user/UserCreateMessage.java new file mode 100644 index 000000000..509527b09 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/mq/message/user/UserCreateMessage.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.member.mq.message.user; + +import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessage; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotNull; + +/** + * 会员用户创建消息 + * + * @author owen + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class UserCreateMessage extends AbstractStreamMessage { + + /** + * 用户编号 + */ + @NotNull(message = "用户编号不能为空") + private Long userId; + + @Override + public String getStreamKey() { + return "member.create.send"; + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/mq/producer/user/UserCreateProducer.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/mq/producer/user/UserCreateProducer.java new file mode 100644 index 000000000..6337b9f00 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/mq/producer/user/UserCreateProducer.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.member.mq.producer.user; + +import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; +import cn.iocoder.yudao.module.member.mq.message.user.UserCreateMessage; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * 会员用户创建 Producer + * + * @author owen + */ +@Slf4j +@Component +public class UserCreateProducer { + + @Resource + private RedisMQTemplate redisMQTemplate; + + /** + * 发送 {@link UserCreateMessage} 消息 + * + * @param userId 用户编号 + */ + public void sendUserCreateMessage(Long userId) { + redisMQTemplate.send(new UserCreateMessage().setUserId(userId)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/package-info.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/package-info.java new file mode 100644 index 000000000..405aa4cbf --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/package-info.java @@ -0,0 +1,8 @@ +/** + * member 模块,我们放会员业务。 + * 例如说:会员中心等等 + * + * 1. Controller URL:以 /member/ 开头,避免和其它 Module 冲突 + * 2. DataObject 表名:以 member_ 开头,方便在数据库中区分 + */ +package cn.iocoder.yudao.module.member; diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressService.java new file mode 100644 index 000000000..099c49c42 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressService.java @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.member.service.address; + +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressCreateReqVO; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressUpdateReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.address.MemberAddressDO; + +import javax.validation.Valid; +import java.util.List; + +/** + * 用户收件地址 Service 接口 + * + * @author 芋道源码 + */ +public interface AddressService { + + /** + * 创建用户收件地址 + * + * + * @param userId 用户编号 + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createAddress(Long userId, @Valid AppAddressCreateReqVO createReqVO); + + /** + * 更新用户收件地址 + * + * @param userId 用户编号 + * @param updateReqVO 更新信息 + */ + void updateAddress(Long userId, @Valid AppAddressUpdateReqVO updateReqVO); + + /** + * 删除用户收件地址 + * + * @param userId 用户编号 + * @param id 编号 + */ + void deleteAddress(Long userId, Long id); + + /** + * 获得用户收件地址 + * + * @param id 编号 + * @return 用户收件地址 + */ + MemberAddressDO getAddress(Long userId, Long id); + + /** + * 获得用户收件地址列表 + * + * @param userId 用户编号 + * @return 用户收件地址列表 + */ + List getAddressList(Long userId); + + /** + * 获得用户默认的收件地址 + * + * @param userId 用户编号 + * @return 用户收件地址 + */ + MemberAddressDO getDefaultUserAddress(Long userId); + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImpl.java new file mode 100644 index 000000000..901f1b340 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImpl.java @@ -0,0 +1,97 @@ +package cn.iocoder.yudao.module.member.service.address; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressCreateReqVO; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressUpdateReqVO; +import cn.iocoder.yudao.module.member.convert.address.AddressConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.address.MemberAddressDO; +import cn.iocoder.yudao.module.member.dal.mysql.address.MemberAddressMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.ADDRESS_NOT_EXISTS; + +/** + * 用户收件地址 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class AddressServiceImpl implements AddressService { + + @Resource + private MemberAddressMapper memberAddressMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createAddress(Long userId, AppAddressCreateReqVO createReqVO) { + // 如果添加的是默认收件地址,则将原默认地址修改为非默认 + if (Boolean.TRUE.equals(createReqVO.getDefaultStatus())) { + List addresses = memberAddressMapper.selectListByUserIdAndDefaulted(userId, true); + addresses.forEach(address -> memberAddressMapper.updateById(new MemberAddressDO().setId(address.getId()).setDefaultStatus(false))); + } + + // 插入 + MemberAddressDO address = AddressConvert.INSTANCE.convert(createReqVO); + address.setUserId(userId); + memberAddressMapper.insert(address); + // 返回 + return address.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateAddress(Long userId, AppAddressUpdateReqVO updateReqVO) { + // 校验存在,校验是否能够操作 + validAddressExists(userId, updateReqVO.getId()); + + // 如果修改的是默认收件地址,则将原默认地址修改为非默认 + if (Boolean.TRUE.equals(updateReqVO.getDefaultStatus())) { + List addresses = memberAddressMapper.selectListByUserIdAndDefaulted(userId, true); + addresses.stream().filter(u -> !u.getId().equals(updateReqVO.getId())) // 排除自己 + .forEach(address -> memberAddressMapper.updateById(new MemberAddressDO().setId(address.getId()).setDefaultStatus(false))); + } + + // 更新 + MemberAddressDO updateObj = AddressConvert.INSTANCE.convert(updateReqVO); + memberAddressMapper.updateById(updateObj); + } + + @Override + public void deleteAddress(Long userId, Long id) { + // 校验存在,校验是否能够操作 + validAddressExists(userId, id); + // 删除 + memberAddressMapper.deleteById(id); + } + + private void validAddressExists(Long userId, Long id) { + MemberAddressDO addressDO = getAddress(userId, id); + if (addressDO == null) { + throw exception(ADDRESS_NOT_EXISTS); + } + } + + @Override + public MemberAddressDO getAddress(Long userId, Long id) { + return memberAddressMapper.selectByIdAndUserId(id, userId); + } + + @Override + public List getAddressList(Long userId) { + return memberAddressMapper.selectListByUserIdAndDefaulted(userId, null); + } + + @Override + public MemberAddressDO getDefaultUserAddress(Long userId) { + List addresses = memberAddressMapper.selectListByUserIdAndDefaulted(userId, true); + return CollUtil.getFirst(addresses); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthService.java new file mode 100644 index 000000000..9ab878817 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthService.java @@ -0,0 +1,90 @@ +package cn.iocoder.yudao.module.member.service.auth; + +import cn.iocoder.yudao.framework.common.enums.TerminalEnum; +import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; + +import javax.validation.Valid; + +/** + * 会员的认证 Service 接口 + * + * 提供用户的账号密码登录、token 的校验等认证相关的功能 + * + * @author 芋道源码 + */ +public interface MemberAuthService { + + /** + * 手机 + 密码登录 + * + * @param reqVO 登录信息 + * @return 登录结果 + */ + AppAuthLoginRespVO login(@Valid AppAuthLoginReqVO reqVO); + + /** + * 基于 token 退出登录 + * + * @param token token + */ + void logout(String token); + + /** + * 手机 + 验证码登陆 + * + * @param reqVO 登陆信息 + * @param terminal 终端 {@link TerminalEnum} + * @return 登录结果 + */ + AppAuthLoginRespVO smsLogin(@Valid AppAuthSmsLoginReqVO reqVO, Integer terminal); + + /** + * 社交登录,使用 code 授权码 + * + * @param reqVO 登录信息 + * @return 登录结果 + */ + AppAuthLoginRespVO socialLogin(@Valid AppAuthSocialLoginReqVO reqVO); + + /** + * 微信小程序的一键登录 + * + * @param reqVO 登录信息 + * @return 登录结果 + */ + AppAuthLoginRespVO weixinMiniAppLogin(AppAuthWeixinMiniAppLoginReqVO reqVO); + + /** + * 获得社交认证 URL + * + * @param type 社交平台类型 + * @param redirectUri 跳转地址 + * @return 认证 URL + */ + String getSocialAuthorizeUrl(Integer type, String redirectUri); + + /** + * 给用户发送短信验证码 + * + * @param userId 用户编号 + * @param reqVO 发送信息 + */ + void sendSmsCode(Long userId, AppAuthSmsSendReqVO reqVO); + + /** + * 校验短信验证码是否正确 + * + * @param userId 用户编号 + * @param reqVO 校验信息 + */ + void validateSmsCode(Long userId, AppAuthSmsValidateReqVO reqVO); + + /** + * 刷新访问令牌 + * + * @param refreshToken 刷新令牌 + * @return 登录结果 + */ + AppAuthLoginRespVO refreshToken(String refreshToken); + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java new file mode 100644 index 000000000..b6e88fd41 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java @@ -0,0 +1,264 @@ +package cn.iocoder.yudao.module.member.service.auth; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.enums.TerminalEnum; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; +import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; +import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; +import cn.iocoder.yudao.module.member.convert.auth.AuthConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; +import cn.iocoder.yudao.module.member.service.user.MemberUserService; +import cn.iocoder.yudao.module.system.api.logger.LoginLogApi; +import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; +import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi; +import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenCreateReqDTO; +import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenRespDTO; +import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; +import cn.iocoder.yudao.module.system.api.social.SocialClientApi; +import cn.iocoder.yudao.module.system.api.social.SocialUserApi; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO; +import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; +import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; +import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2ClientConstants; +import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.Objects; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; +import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*; + +/** + * 会员的认证 Service 接口 + * + * @author 芋道源码 + */ +@Service +@Slf4j +public class MemberAuthServiceImpl implements MemberAuthService { + + @Resource + private MemberUserService userService; + @Resource + private SmsCodeApi smsCodeApi; + @Resource + private LoginLogApi loginLogApi; + @Resource + private SocialUserApi socialUserApi; + @Resource + private SocialClientApi socialClientApi; + @Resource + private OAuth2TokenApi oauth2TokenApi; + + @Override + public AppAuthLoginRespVO login(AppAuthLoginReqVO reqVO) { + // 使用手机 + 密码,进行登录。 + MemberUserDO user = login0(reqVO.getMobile(), reqVO.getPassword()); + + // 如果 socialType 非空,说明需要绑定社交用户 + String openid = null; + if (reqVO.getSocialType() != null) { + openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(), + reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())); + } + + // 创建 Token 令牌,记录登录日志 + return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE, openid); + } + + @Override + @Transactional + public AppAuthLoginRespVO smsLogin(AppAuthSmsLoginReqVO reqVO, Integer terminal) { + // 校验验证码 + String userIp = getClientIP(); + smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_LOGIN.getScene(), userIp)); + + // 获得获得注册用户 + MemberUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp, terminal); + Assert.notNull(user, "获取用户失败,结果为空"); + + // 如果 socialType 非空,说明需要绑定社交用户 + String openid = null; + if (reqVO.getSocialType() != null) { + openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(), + reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())); + } + + // 创建 Token 令牌,记录登录日志 + return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_SMS, openid); + } + + @Override + public AppAuthLoginRespVO socialLogin(AppAuthSocialLoginReqVO reqVO) { + // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 + SocialUserRespDTO socialUser = socialUserApi.getSocialUser(UserTypeEnum.MEMBER.getValue(), reqVO.getType(), + reqVO.getCode(), reqVO.getState()); + if (socialUser == null) { + throw exception(AUTH_THIRD_LOGIN_NOT_BIND); + } + + // 自动登录 + MemberUserDO user = userService.getUser(socialUser.getUserId()); + if (user == null) { + throw exception(USER_NOT_EXISTS); + } + + // 创建 Token 令牌,记录登录日志 + return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL, socialUser.getOpenid()); + } + + @Override + public AppAuthLoginRespVO weixinMiniAppLogin(AppAuthWeixinMiniAppLoginReqVO reqVO) { + // 获得对应的手机号信息 + SocialWxPhoneNumberInfoRespDTO phoneNumberInfo = socialClientApi.getWxMaPhoneNumberInfo( + UserTypeEnum.MEMBER.getValue(), reqVO.getPhoneCode()); + Assert.notNull(phoneNumberInfo, "获得手机信息失败,结果为空"); + + // 获得获得注册用户 + MemberUserDO user = userService.createUserIfAbsent(phoneNumberInfo.getPurePhoneNumber(), + getClientIP(), TerminalEnum.WECHAT_MINI_PROGRAM.getTerminal()); + Assert.notNull(user, "获取用户失败,结果为空"); + + // 绑定社交用户 + String openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(), + SocialTypeEnum.WECHAT_MINI_APP.getType(), reqVO.getLoginCode(), "")); + + // 创建 Token 令牌,记录登录日志 + return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL, openid); + } + + private AppAuthLoginRespVO createTokenAfterLoginSuccess(MemberUserDO user, String mobile, + LoginLogTypeEnum logType, String openid) { + // 插入登陆日志 + createLoginLog(user.getId(), mobile, logType, LoginResultEnum.SUCCESS); + // 创建 Token 令牌 + OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.createAccessToken(new OAuth2AccessTokenCreateReqDTO() + .setUserId(user.getId()).setUserType(getUserType().getValue()) + .setClientId(OAuth2ClientConstants.CLIENT_ID_DEFAULT)); + // 构建返回结果 + return AuthConvert.INSTANCE.convert(accessTokenRespDTO, openid); + } + + @Override + public String getSocialAuthorizeUrl(Integer type, String redirectUri) { + return socialClientApi.getAuthorizeUrl(type, UserTypeEnum.MEMBER.getValue(), redirectUri); + } + + private MemberUserDO login0(String mobile, String password) { + final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_MOBILE; + // 校验账号是否存在 + MemberUserDO user = userService.getUserByMobile(mobile); + if (user == null) { + createLoginLog(null, mobile, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); + throw exception(AUTH_LOGIN_BAD_CREDENTIALS); + } + if (!userService.isPasswordMatch(password, user.getPassword())) { + createLoginLog(user.getId(), mobile, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); + throw exception(AUTH_LOGIN_BAD_CREDENTIALS); + } + // 校验是否禁用 + if (ObjectUtil.notEqual(user.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { + createLoginLog(user.getId(), mobile, logTypeEnum, LoginResultEnum.USER_DISABLED); + throw exception(AUTH_LOGIN_USER_DISABLED); + } + return user; + } + + private void createLoginLog(Long userId, String mobile, LoginLogTypeEnum logType, LoginResultEnum loginResult) { + // 插入登录日志 + LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); + reqDTO.setLogType(logType.getType()); + reqDTO.setTraceId(TracerUtils.getTraceId()); + reqDTO.setUserId(userId); + reqDTO.setUserType(getUserType().getValue()); + reqDTO.setUsername(mobile); + reqDTO.setUserAgent(ServletUtils.getUserAgent()); + reqDTO.setUserIp(getClientIP()); + reqDTO.setResult(loginResult.getResult()); + loginLogApi.createLoginLog(reqDTO); + // 更新最后登录时间 + if (userId != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { + userService.updateUserLogin(userId, getClientIP()); + } + } + + @Override + public void logout(String token) { + // 删除访问令牌 + OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.removeAccessToken(token); + if (accessTokenRespDTO == null) { + return; + } + // 删除成功,则记录登出日志 + createLogoutLog(accessTokenRespDTO.getUserId()); + } + + @Override + public void sendSmsCode(Long userId, AppAuthSmsSendReqVO reqVO) { + // 情况 1:如果是修改手机场景,需要校验新手机号是否已经注册,说明不能使用该手机了 + if (Objects.equals(reqVO.getScene(), SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene())) { + MemberUserDO user = userService.getUserByMobile(reqVO.getMobile()); + if (user != null && !Objects.equals(user.getId(), userId)) { + throw exception(AUTH_MOBILE_USED); + } + } + // 情况 2:如果是重置密码场景,需要校验手机号是存在的 + if (Objects.equals(reqVO.getScene(), SmsSceneEnum.MEMBER_RESET_PASSWORD.getScene())) { + MemberUserDO user= userService.getUserByMobile(reqVO.getMobile()); + if (user == null) { + throw exception(USER_MOBILE_NOT_EXISTS); + } + } + + // 执行发送 + smsCodeApi.sendSmsCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP())); + } + + @Override + public void validateSmsCode(Long userId, AppAuthSmsValidateReqVO reqVO) { + smsCodeApi.validateSmsCode(AuthConvert.INSTANCE.convert(reqVO)); + } + + @Override + public AppAuthLoginRespVO refreshToken(String refreshToken) { + OAuth2AccessTokenRespDTO accessTokenDO = oauth2TokenApi.refreshAccessToken(refreshToken, + OAuth2ClientConstants.CLIENT_ID_DEFAULT); + return AuthConvert.INSTANCE.convert(accessTokenDO, null); + } + + private void createLogoutLog(Long userId) { + LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); + reqDTO.setLogType(LoginLogTypeEnum.LOGOUT_SELF.getType()); + reqDTO.setTraceId(TracerUtils.getTraceId()); + reqDTO.setUserId(userId); + reqDTO.setUserType(getUserType().getValue()); + reqDTO.setUsername(getMobile(userId)); + reqDTO.setUserAgent(ServletUtils.getUserAgent()); + reqDTO.setUserIp(getClientIP()); + reqDTO.setResult(LoginResultEnum.SUCCESS.getResult()); + loginLogApi.createLoginLog(reqDTO); + } + + private String getMobile(Long userId) { + if (userId == null) { + return null; + } + MemberUserDO user = userService.getUser(userId); + return user != null ? user.getMobile() : null; + } + + private UserTypeEnum getUserType() { + return UserTypeEnum.MEMBER; + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/config/MemberConfigService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/config/MemberConfigService.java new file mode 100644 index 000000000..fc4545425 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/config/MemberConfigService.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.member.service.config; + +import cn.iocoder.yudao.module.member.controller.admin.config.vo.MemberConfigSaveReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.config.MemberConfigDO; + +import javax.validation.Valid; + +/** + * 会员配置 Service 接口 + * + * @author QingX + */ +public interface MemberConfigService { + + /** + * 保存会员配置 + * + * @param saveReqVO 更新信息 + */ + void saveConfig(@Valid MemberConfigSaveReqVO saveReqVO); + + /** + * 获得会员配置 + * + * @return 积分配置 + */ + MemberConfigDO getConfig(); + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/config/MemberConfigServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/config/MemberConfigServiceImpl.java new file mode 100644 index 000000000..be56f8a8a --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/config/MemberConfigServiceImpl.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.member.service.config; + +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.member.controller.admin.config.vo.MemberConfigSaveReqVO; +import cn.iocoder.yudao.module.member.convert.config.MemberConfigConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.config.MemberConfigDO; +import cn.iocoder.yudao.module.member.dal.mysql.config.MemberConfigMapper; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 会员配置 Service 实现类 + * + * @author QingX + */ +@Service +@Validated +public class MemberConfigServiceImpl implements MemberConfigService { + + @Resource + private MemberConfigMapper memberConfigMapper; + + @Override + public void saveConfig(MemberConfigSaveReqVO saveReqVO) { + // 存在,则进行更新 + MemberConfigDO dbConfig = getConfig(); + if (dbConfig != null) { + memberConfigMapper.updateById(MemberConfigConvert.INSTANCE.convert(saveReqVO).setId(dbConfig.getId())); + return; + } + // 不存在,则进行插入 + memberConfigMapper.insert(MemberConfigConvert.INSTANCE.convert(saveReqVO)); + } + + @Override + public MemberConfigDO getConfig() { + List list = memberConfigMapper.selectList(); + return CollectionUtils.getFirst(list); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/group/MemberGroupService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/group/MemberGroupService.java new file mode 100644 index 000000000..54c7882e0 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/group/MemberGroupService.java @@ -0,0 +1,86 @@ +package cn.iocoder.yudao.module.member.service.group; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupCreateReqVO; +import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupPageReqVO; +import cn.iocoder.yudao.module.member.controller.admin.group.vo.MemberGroupUpdateReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.group.MemberGroupDO; + +import javax.validation.Valid; +import java.util.Collection; +import java.util.List; + +/** + * 用户分组 Service 接口 + * + * @author owen + */ +public interface MemberGroupService { + + /** + * 创建用户分组 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createGroup(@Valid MemberGroupCreateReqVO createReqVO); + + /** + * 更新用户分组 + * + * @param updateReqVO 更新信息 + */ + void updateGroup(@Valid MemberGroupUpdateReqVO updateReqVO); + + /** + * 删除用户分组 + * + * @param id 编号 + */ + void deleteGroup(Long id); + + /** + * 获得用户分组 + * + * @param id 编号 + * @return 用户分组 + */ + MemberGroupDO getGroup(Long id); + + /** + * 获得用户分组列表 + * + * @param ids 编号 + * @return 用户分组列表 + */ + List getGroupList(Collection ids); + + /** + * 获得用户分组分页 + * + * @param pageReqVO 分页查询 + * @return 用户分组分页 + */ + PageResult getGroupPage(MemberGroupPageReqVO pageReqVO); + + + /** + * 获得指定状态的用户分组列表 + * + * @param status 状态 + * @return 用户分组列表 + */ + List getGroupListByStatus(Integer status); + + + /** + * 获得开启状态的用户分组列表 + * + * @return 用户分组列表 + */ + default List getEnableGroupList() { + return getGroupListByStatus(CommonStatusEnum.ENABLE.getStatus()); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/group/MemberGroupServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/group/MemberGroupServiceImpl.java new file mode 100644 index 000000000..cdf1e4fee --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/group/MemberGroupServiceImpl.java @@ -0,0 +1,103 @@ +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.service.user.MemberUserService; +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 memberGroupMapper; + + @Resource + private MemberUserService memberUserService; + + @Override + public Long createGroup(MemberGroupCreateReqVO createReqVO) { + // 插入 + MemberGroupDO group = MemberGroupConvert.INSTANCE.convert(createReqVO); + memberGroupMapper.insert(group); + // 返回 + return group.getId(); + } + + @Override + public void updateGroup(MemberGroupUpdateReqVO updateReqVO) { + // 校验存在 + validateGroupExists(updateReqVO.getId()); + // 更新 + MemberGroupDO updateObj = MemberGroupConvert.INSTANCE.convert(updateReqVO); + memberGroupMapper.updateById(updateObj); + } + + @Override + public void deleteGroup(Long id) { + // 校验存在 + validateGroupExists(id); + // 校验分组下是否有用户 + validateGroupHasUser(id); + // 删除 + memberGroupMapper.deleteById(id); + } + + void validateGroupExists(Long id) { + if (memberGroupMapper.selectById(id) == null) { + throw exception(GROUP_NOT_EXISTS); + } + } + + void validateGroupHasUser(Long id) { + Long count = memberUserService.getUserCountByGroupId(id); + if (count > 0) { + throw exception(GROUP_HAS_USER); + } + } + + @Override + public MemberGroupDO getGroup(Long id) { + return memberGroupMapper.selectById(id); + } + + @Override + public List getGroupList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return ListUtil.empty(); + } + return memberGroupMapper.selectBatchIds(ids); + } + + @Override + public PageResult getGroupPage(MemberGroupPageReqVO pageReqVO) { + return memberGroupMapper.selectPage(pageReqVO); + } + + @Override + public List getGroupListByStatus(Integer status) { + return memberGroupMapper.selectListByStatus(status); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberExperienceRecordService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberExperienceRecordService.java new file mode 100644 index 000000000..76470f72a --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberExperienceRecordService.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.module.member.service.level; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceRecordPageReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberExperienceRecordDO; +import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum; + +/** + * 会员经验记录 Service 接口 + * + * @author owen + */ +public interface MemberExperienceRecordService { + + /** + * 获得会员经验记录 + * + * @param id 编号 + * @return 会员经验记录 + */ + MemberExperienceRecordDO getExperienceRecord(Long id); + + /** + * 【管理员】获得会员经验记录分页 + * + * @param pageReqVO 分页查询 + * @return 会员经验记录分页 + */ + PageResult getExperienceRecordPage(MemberExperienceRecordPageReqVO pageReqVO); + + /** + * 【会员】获得会员经验记录分页 + * + * @param userId 用户编号 + * @param pageParam 分页查询 + * @return 会员经验记录分页 + */ + PageResult getExperienceRecordPage(Long userId, PageParam pageParam); + + /** + * 根据业务类型, 创建 经验变动记录 + * + * @param userId 会员编号 + * @param experience 变动经验值 + * @param totalExperience 会员当前的经验 + * @param bizType 业务类型 + * @param bizId 业务ID + */ + void createExperienceRecord(Long userId, Integer experience, Integer totalExperience, + MemberExperienceBizTypeEnum bizType, String bizId); + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberExperienceRecordServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberExperienceRecordServiceImpl.java new file mode 100644 index 000000000..80ecc84d6 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberExperienceRecordServiceImpl.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.member.service.level; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.experience.MemberExperienceRecordPageReqVO; +import cn.iocoder.yudao.module.member.convert.level.MemberExperienceRecordConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberExperienceRecordDO; +import cn.iocoder.yudao.module.member.dal.mysql.level.MemberExperienceRecordMapper; +import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.Collection; +import java.util.List; + +/** + * 会员经验记录 Service 实现类 + * + * @author owen + */ +@Service +@Validated +public class MemberExperienceRecordServiceImpl implements MemberExperienceRecordService { + + @Resource + private MemberExperienceRecordMapper experienceLogMapper; + + @Override + public MemberExperienceRecordDO getExperienceRecord(Long id) { + return experienceLogMapper.selectById(id); + } + + @Override + public PageResult getExperienceRecordPage(MemberExperienceRecordPageReqVO pageReqVO) { + return experienceLogMapper.selectPage(pageReqVO); + } + + @Override + public PageResult getExperienceRecordPage(Long userId, PageParam pageParam) { + return experienceLogMapper.selectPage(userId, pageParam); + } + + @Override + public void createExperienceRecord(Long userId, Integer experience, Integer totalExperience, + MemberExperienceBizTypeEnum bizType, String bizId) { + String description = StrUtil.format(bizType.getDescription(), experience); + MemberExperienceRecordDO record = MemberExperienceRecordConvert.INSTANCE.convert( + userId, experience, totalExperience, + bizId, bizType.getType(), bizType.getTitle(), description); + experienceLogMapper.insert(record); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelRecordService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelRecordService.java new file mode 100644 index 000000000..b5e4f669e --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelRecordService.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.member.service.level; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.record.MemberLevelRecordPageReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelRecordDO; + +/** + * 会员等级记录 Service 接口 + * + * @author owen + */ +public interface MemberLevelRecordService { + + /** + * 获得会员等级记录 + * + * @param id 编号 + * @return 会员等级记录 + */ + MemberLevelRecordDO getLevelRecord(Long id); + + /** + * 获得会员等级记录分页 + * + * @param pageReqVO 分页查询 + * @return 会员等级记录分页 + */ + PageResult getLevelRecordPage(MemberLevelRecordPageReqVO pageReqVO); + + /** + * 创建会员等级记录 + * + * @param levelRecord 会员等级记录 + */ + void createLevelRecord(MemberLevelRecordDO levelRecord); + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelRecordServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelRecordServiceImpl.java new file mode 100644 index 000000000..810961241 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelRecordServiceImpl.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.member.service.level; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.record.MemberLevelRecordPageReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelRecordDO; +import cn.iocoder.yudao.module.member.dal.mysql.level.MemberLevelRecordMapper; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; + +/** + * 会员等级记录 Service 实现类 + * + * @author owen + */ +@Service +@Validated +public class MemberLevelRecordServiceImpl implements MemberLevelRecordService { + + @Resource + private MemberLevelRecordMapper levelLogMapper; + + @Override + public MemberLevelRecordDO getLevelRecord(Long id) { + return levelLogMapper.selectById(id); + } + + @Override + public PageResult getLevelRecordPage(MemberLevelRecordPageReqVO pageReqVO) { + return levelLogMapper.selectPage(pageReqVO); + } + + @Override + public void createLevelRecord(MemberLevelRecordDO levelRecord) { + levelLogMapper.insert(levelRecord); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelService.java new file mode 100644 index 000000000..76d46e5c3 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelService.java @@ -0,0 +1,102 @@ +package cn.iocoder.yudao.module.member.service.level; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.level.MemberLevelCreateReqVO; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.level.MemberLevelListReqVO; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.level.MemberLevelUpdateReqVO; +import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateLevelReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO; +import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum; + +import javax.validation.Valid; +import java.util.Collection; +import java.util.List; + +/** + * 会员等级 Service 接口 + * + * @author owen + */ +public interface MemberLevelService { + + /** + * 创建会员等级 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createLevel(@Valid MemberLevelCreateReqVO createReqVO); + + /** + * 更新会员等级 + * + * @param updateReqVO 更新信息 + */ + void updateLevel(@Valid MemberLevelUpdateReqVO updateReqVO); + + /** + * 删除会员等级 + * + * @param id 编号 + */ + void deleteLevel(Long id); + + /** + * 获得会员等级 + * + * @param id 编号 + * @return 会员等级 + */ + MemberLevelDO getLevel(Long id); + + /** + * 获得会员等级列表 + * + * @param ids 编号 + * @return 会员等级列表 + */ + List getLevelList(Collection ids); + + /** + * 获得会员等级列表 + * + * @param listReqVO 查询参数 + * @return 会员等级列表 + */ + List getLevelList(MemberLevelListReqVO listReqVO); + + /** + * 获得指定状态的会员等级列表 + * + * @param status 状态 + * @return 会员等级列表 + */ + List getLevelListByStatus(Integer status); + + /** + * 获得开启状态的会员等级列表 + * + * @return 会员等级列表 + */ + default List getEnableLevelList() { + return getLevelListByStatus(CommonStatusEnum.ENABLE.getStatus()); + } + + /** + * 修改会员的等级 + * + * @param updateReqVO 修改参数 + */ + void updateUserLevel(MemberUserUpdateLevelReqVO updateReqVO); + + /** + * 增加会员经验 + * + * @param userId 会员ID + * @param experience 经验 + * @param bizType 业务类型 + * @param bizId 业务编号 + */ + void addExperience(Long userId, Integer experience, MemberExperienceBizTypeEnum bizType, String bizId); + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelServiceImpl.java new file mode 100644 index 000000000..c98dd4b97 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelServiceImpl.java @@ -0,0 +1,300 @@ +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.ObjectUtil; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.level.MemberLevelCreateReqVO; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.level.MemberLevelListReqVO; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.level.MemberLevelUpdateReqVO; +import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateLevelReqVO; +import cn.iocoder.yudao.module.member.convert.level.MemberLevelConvert; +import cn.iocoder.yudao.module.member.convert.level.MemberLevelRecordConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelRecordDO; +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.enums.MemberExperienceBizTypeEnum; +import cn.iocoder.yudao.module.member.service.user.MemberUserService; +import com.google.common.annotations.VisibleForTesting; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*; + +/** + * 会员等级 Service 实现类 + * + * @author owen + */ +@Slf4j +@Service +@Validated +public class MemberLevelServiceImpl implements MemberLevelService { + + @Resource + private MemberLevelMapper memberLevelMapper; + + @Resource + private MemberLevelRecordService memberLevelRecordService; + @Resource + private MemberExperienceRecordService memberExperienceRecordService; + @Resource + private MemberUserService memberUserService; + + @Override + public Long createLevel(MemberLevelCreateReqVO createReqVO) { + // 校验配置是否有效 + validateConfigValid(null, createReqVO.getName(), createReqVO.getLevel(), createReqVO.getExperience()); + + // 插入 + MemberLevelDO level = MemberLevelConvert.INSTANCE.convert(createReqVO); + memberLevelMapper.insert(level); + // 返回 + return level.getId(); + } + + @Override + public void updateLevel(MemberLevelUpdateReqVO updateReqVO) { + // 校验存在 + validateLevelExists(updateReqVO.getId()); + // 校验配置是否有效 + validateConfigValid(updateReqVO.getId(), updateReqVO.getName(), updateReqVO.getLevel(), updateReqVO.getExperience()); + + // 更新 + MemberLevelDO updateObj = MemberLevelConvert.INSTANCE.convert(updateReqVO); + memberLevelMapper.updateById(updateObj); + } + + @Override + public void deleteLevel(Long id) { + // 校验存在 + validateLevelExists(id); + // 校验分组下是否有用户 + validateLevelHasUser(id); + // 删除 + memberLevelMapper.deleteById(id); + } + + @VisibleForTesting + MemberLevelDO validateLevelExists(Long id) { + MemberLevelDO levelDO = memberLevelMapper.selectById(id); + if (levelDO == null) { + throw exception(LEVEL_NOT_EXISTS); + } + return levelDO; + } + + @VisibleForTesting + void validateNameUnique(List list, Long id, String name) { + for (MemberLevelDO levelDO : list) { + if (ObjUtil.notEqual(levelDO.getName(), name)) { + continue; + } + if (id == null || !id.equals(levelDO.getId())) { + throw exception(LEVEL_NAME_EXISTS, levelDO.getName()); + } + } + } + + @VisibleForTesting + void validateLevelUnique(List list, Long id, Integer level) { + for (MemberLevelDO levelDO : list) { + if (ObjUtil.notEqual(levelDO.getLevel(), level)) { + continue; + } + + if (id == null || !id.equals(levelDO.getId())) { + throw exception(LEVEL_VALUE_EXISTS, levelDO.getLevel(), levelDO.getName()); + } + } + } + + @VisibleForTesting + void validateExperienceOutRange(List list, Long id, Integer level, Integer experience) { + for (MemberLevelDO levelDO : list) { + if (levelDO.getId().equals(id)) { + continue; + } + + if (levelDO.getLevel() < level) { + // 经验大于前一个等级 + if (experience <= levelDO.getExperience()) { + throw exception(LEVEL_EXPERIENCE_MIN, levelDO.getName(), levelDO.getExperience()); + } + } else if (levelDO.getLevel() > level) { + //小于下一个级别 + if (experience >= levelDO.getExperience()) { + throw exception(LEVEL_EXPERIENCE_MAX, levelDO.getName(), levelDO.getExperience()); + } + } + } + } + + @VisibleForTesting + void validateConfigValid(Long id, String name, Integer level, Integer experience) { + List list = memberLevelMapper.selectList(); + // 校验名称唯一 + validateNameUnique(list, id, name); + // 校验等级唯一 + validateLevelUnique(list, id, level); + // 校验升级所需经验是否有效: 大于前一个等级,小于下一个级别 + validateExperienceOutRange(list, id, level, experience); + } + + @VisibleForTesting + void validateLevelHasUser(Long id) { + Long count = memberUserService.getUserCountByLevelId(id); + if (count > 0) { + throw exception(LEVEL_HAS_USER); + } + } + + @Override + public MemberLevelDO getLevel(Long id) { + return id != null && id > 0 ? memberLevelMapper.selectById(id) : null; + } + + @Override + public List getLevelList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return memberLevelMapper.selectBatchIds(ids); + } + + @Override + public List getLevelList(MemberLevelListReqVO listReqVO) { + return memberLevelMapper.selectList(listReqVO); + } + + @Override + public List getLevelListByStatus(Integer status) { + return memberLevelMapper.selectListByStatus(status); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateUserLevel(MemberUserUpdateLevelReqVO updateReqVO) { + MemberUserDO user = memberUserService.getUser(updateReqVO.getId()); + if (user == null) { + throw exception(USER_NOT_EXISTS); + } + // 等级未发生变化 + if (ObjUtil.equal(user.getLevelId(), updateReqVO.getLevelId())) { + return; + } + + // 1. 记录等级变动 + MemberLevelRecordDO levelRecord = new MemberLevelRecordDO() + .setUserId(user.getId()).setRemark(updateReqVO.getReason()); + MemberLevelDO memberLevel = null; + if (updateReqVO.getLevelId() == null) { + // 取消用户等级时,需要扣减经验 + levelRecord.setExperience(-user.getExperience()); + levelRecord.setUserExperience(0); + levelRecord.setDescription("管理员取消了等级"); + } else { + // 复制等级配置 + memberLevel = validateLevelExists(updateReqVO.getLevelId()); + MemberLevelRecordConvert.INSTANCE.copyTo(memberLevel, levelRecord); + // 变动经验值 = 等级的升级经验 - 会员当前的经验;正数为增加经验,负数为扣减经验 + levelRecord.setExperience(memberLevel.getExperience() - user.getExperience()); + levelRecord.setUserExperience(memberLevel.getExperience()); // 会员当前的经验 = 等级的升级经验 + levelRecord.setDescription("管理员调整为:" + memberLevel.getName()); + } + memberLevelRecordService.createLevelRecord(levelRecord); + + // 2. 记录会员经验变动 + memberExperienceRecordService.createExperienceRecord(user.getId(), + levelRecord.getExperience(), levelRecord.getUserExperience(), + MemberExperienceBizTypeEnum.ADMIN, String.valueOf(MemberExperienceBizTypeEnum.ADMIN.getType())); + + // 3. 更新会员表上的等级编号、经验值 + memberUserService.updateUserLevel(user.getId(), updateReqVO.getLevelId(), + levelRecord.getUserExperience()); + + // 4. 给会员发送等级变动消息 + notifyMemberLevelChange(user.getId(), memberLevel); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void addExperience(Long userId, Integer experience, MemberExperienceBizTypeEnum bizType, String bizId) { + if (experience == 0) { + return; + } + if (!bizType.isAdd() && experience > 0) { + experience = -experience; + } + + // 1. 创建经验记录 + MemberUserDO user = memberUserService.getUser(userId); + Integer userExperience = ObjUtil.defaultIfNull(user.getExperience(), 0); + userExperience = NumberUtil.max(userExperience + experience, 0); // 防止扣出负数 + MemberLevelRecordDO levelRecord = new MemberLevelRecordDO() + .setUserId(user.getId()) + .setExperience(experience) + .setUserExperience(userExperience); + memberExperienceRecordService.createExperienceRecord(userId, experience, userExperience, + bizType, bizId); + + // 2.1 保存等级变更记录 + MemberLevelDO newLevel = calculateNewLevel(user, userExperience); + if (newLevel != null) { + MemberLevelRecordConvert.INSTANCE.copyTo(newLevel, levelRecord); + memberLevelRecordService.createLevelRecord(levelRecord); + + // 2.2 给会员发送等级变动消息 + notifyMemberLevelChange(userId, newLevel); + } + + // 3. 更新会员表上的等级编号、经验值 + memberUserService.updateUserLevel(user.getId(), levelRecord.getLevelId(), userExperience); + } + + /** + * 计算会员等级 + * + * @param user 会员 + * @param userExperience 会员当前的经验值 + * @return 会员新的等级,null表示无变化 + */ + private MemberLevelDO calculateNewLevel(MemberUserDO user, int userExperience) { + List 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; + } + + return matchLevel; + } + + private void notifyMemberLevelChange(Long userId, MemberLevelDO level) { + //todo: 给会员发消息 + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/point/MemberPointRecordService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/point/MemberPointRecordService.java new file mode 100644 index 000000000..74e91880f --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/point/MemberPointRecordService.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.member.service.point; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod.MemberPointRecordPageReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO; +import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum; + +/** + * 用户积分记录 Service 接口 + * + * @author QingX + */ +public interface MemberPointRecordService { + + /** + * 【管理员】获得积分记录分页 + * + * @param pageReqVO 分页查询 + * @return 签到记录分页 + */ + PageResult getPointRecordPage(MemberPointRecordPageReqVO pageReqVO); + + /** + * 【会员】获得积分记录分页 + * + * @param userId 用户编号 + * @param pageVO 分页查询 + * @return 签到记录分页 + */ + PageResult getPointRecordPage(Long userId, PageParam pageVO); + + /** + * 创建用户积分记录 + * + * @param userId 用户ID + * @param point 变动积分 + * @param bizType 业务类型 + * @param bizId 业务编号 + */ + void createPointRecord(Long userId, Integer point, MemberPointBizTypeEnum bizType, String bizId); +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/point/MemberPointRecordServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/point/MemberPointRecordServiceImpl.java new file mode 100644 index 000000000..e665bdf13 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/point/MemberPointRecordServiceImpl.java @@ -0,0 +1,94 @@ +package cn.iocoder.yudao.module.member.service.point; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod.MemberPointRecordPageReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO; +import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; +import cn.iocoder.yudao.module.member.dal.mysql.point.MemberPointRecordMapper; +import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum; +import cn.iocoder.yudao.module.member.service.user.MemberUserService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.USER_POINT_NOT_ENOUGH; + + +/** + * 积分记录 Service 实现类 + * + * @author QingX + */ +@Slf4j +@Service +@Validated +public class MemberPointRecordServiceImpl implements MemberPointRecordService { + + @Resource + private MemberPointRecordMapper memberPointRecordMapper; + + @Resource + private MemberUserService memberUserService; + + @Override + public PageResult getPointRecordPage(MemberPointRecordPageReqVO pageReqVO) { + // 根据用户昵称查询出用户 ids + Set userIds = null; + if (StringUtils.isNotBlank(pageReqVO.getNickname())) { + List users = memberUserService.getUserListByNickname(pageReqVO.getNickname()); + // 如果查询用户结果为空直接返回无需继续查询 + if (CollectionUtils.isEmpty(users)) { + return PageResult.empty(); + } + userIds = convertSet(users, MemberUserDO::getId); + } + // 执行查询 + return memberPointRecordMapper.selectPage(pageReqVO, userIds); + } + + @Override + public PageResult getPointRecordPage(Long userId, PageParam pageVO) { + return memberPointRecordMapper.selectPage(userId, pageVO); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void createPointRecord(Long userId, Integer point, MemberPointBizTypeEnum bizType, String bizId) { + if (point == 0) { + return; + } + // 1. 校验用户积分余额 + MemberUserDO user = memberUserService.getUser(userId); + Integer userPoint = ObjectUtil.defaultIfNull(user.getPoint(), 0); + int totalPoint = userPoint + point; // 用户变动后的积分 + if (totalPoint < 0) { + throw exception(USER_POINT_NOT_ENOUGH); + } + + // 2. 更新用户积分 + boolean success = memberUserService.updateUserPoint(userId, point); + if (!success) { + throw exception(USER_POINT_NOT_ENOUGH); + } + + // 3. 增加积分记录 + MemberPointRecordDO record = new MemberPointRecordDO() + .setUserId(userId).setBizId(bizId).setBizType(bizType.getType()) + .setTitle(bizType.getName()).setDescription(StrUtil.format(bizType.getDescription(), point)) + .setPoint(point).setTotalPoint(totalPoint); + memberPointRecordMapper.insert(record); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInConfigService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInConfigService.java new file mode 100644 index 000000000..b4a9c041c --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInConfigService.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.member.service.signin; + +import cn.iocoder.yudao.module.member.controller.admin.signin.vo.config.MemberSignInConfigCreateReqVO; +import cn.iocoder.yudao.module.member.controller.admin.signin.vo.config.MemberSignInConfigUpdateReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInConfigDO; + +import javax.validation.Valid; +import java.util.List; + +/** + * 签到规则 Service 接口 + * + * @author QingX + */ +public interface MemberSignInConfigService { + + /** + * 创建签到规则 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createSignInConfig(@Valid MemberSignInConfigCreateReqVO createReqVO); + + /** + * 更新签到规则 + * + * @param updateReqVO 更新信息 + */ + void updateSignInConfig(@Valid MemberSignInConfigUpdateReqVO updateReqVO); + + /** + * 删除签到规则 + * + * @param id 编号 + */ + void deleteSignInConfig(Long id); + + /** + * 获得签到规则 + * + * @param id 编号 + * @return 签到规则 + */ + MemberSignInConfigDO getSignInConfig(Long id); + + /** + * 获得签到规则列表 + * + * @return 签到规则分页 + */ + List getSignInConfigList(); + + /** + * 获得签到规则列表 + * + * @param status 状态 + * @return 签到规则分页 + */ + List getSignInConfigList(Integer status); + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInConfigServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInConfigServiceImpl.java new file mode 100644 index 000000000..4e2b04c63 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInConfigServiceImpl.java @@ -0,0 +1,106 @@ +package cn.iocoder.yudao.module.member.service.signin; + +import cn.iocoder.yudao.module.member.controller.admin.signin.vo.config.MemberSignInConfigCreateReqVO; +import cn.iocoder.yudao.module.member.controller.admin.signin.vo.config.MemberSignInConfigUpdateReqVO; +import cn.iocoder.yudao.module.member.convert.signin.MemberSignInConfigConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInConfigDO; +import cn.iocoder.yudao.module.member.dal.mysql.signin.MemberSignInConfigMapper; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.Comparator; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.SIGN_IN_CONFIG_EXISTS; +import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.SIGN_IN_CONFIG_NOT_EXISTS; + +/** + * 签到规则 Service 实现类 + * + * @author QingX + */ +@Service +@Validated +public class MemberSignInConfigServiceImpl implements MemberSignInConfigService { + + @Resource + private MemberSignInConfigMapper memberSignInConfigMapper; + + @Override + public Long createSignInConfig(MemberSignInConfigCreateReqVO createReqVO) { + // 判断是否重复插入签到天数 + validateSignInConfigDayDuplicate(createReqVO.getDay(), null); + + // 插入 + MemberSignInConfigDO signInConfig = MemberSignInConfigConvert.INSTANCE.convert(createReqVO); + memberSignInConfigMapper.insert(signInConfig); + // 返回 + return signInConfig.getId(); + } + + @Override + public void updateSignInConfig(MemberSignInConfigUpdateReqVO updateReqVO) { + // 校验存在 + validateSignInConfigExists(updateReqVO.getId()); + // 判断是否重复插入签到天数 + validateSignInConfigDayDuplicate(updateReqVO.getDay(), updateReqVO.getId()); + + // 判断更新 + MemberSignInConfigDO updateObj = MemberSignInConfigConvert.INSTANCE.convert(updateReqVO); + memberSignInConfigMapper.updateById(updateObj); + } + + @Override + public void deleteSignInConfig(Long id) { + // 校验存在 + validateSignInConfigExists(id); + // 删除 + memberSignInConfigMapper.deleteById(id); + } + + private void validateSignInConfigExists(Long id) { + if (memberSignInConfigMapper.selectById(id) == null) { + throw exception(SIGN_IN_CONFIG_NOT_EXISTS); + } + } + + /** + * 校验 day 是否重复 + * + * @param day 天 + * @param id 编号,只有更新的时候会传递 + */ + private void validateSignInConfigDayDuplicate(Integer day, Long id) { + MemberSignInConfigDO config = memberSignInConfigMapper.selectByDay(day); + // 1. 新增时,config 非空,则说明重复 + if (id == null && config != null) { + throw exception(SIGN_IN_CONFIG_EXISTS); + } + // 2. 更新时,如果 config 非空,且 id 不相等,则说明重复 + if (id != null && config != null && !config.getId().equals(id)) { + throw exception(SIGN_IN_CONFIG_EXISTS); + } + } + + @Override + public MemberSignInConfigDO getSignInConfig(Long id) { + return memberSignInConfigMapper.selectById(id); + } + + @Override + public List getSignInConfigList() { + List list = memberSignInConfigMapper.selectList(); + list.sort(Comparator.comparing(MemberSignInConfigDO::getDay)); + return list; + } + + @Override + public List getSignInConfigList(Integer status) { + List list = memberSignInConfigMapper.selectListByStatus(status); + list.sort(Comparator.comparing(MemberSignInConfigDO::getDay)); + return list; + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInRecordService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInRecordService.java new file mode 100644 index 000000000..b22ceed1a --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInRecordService.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.member.service.signin; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.controller.admin.signin.vo.record.MemberSignInRecordPageReqVO; +import cn.iocoder.yudao.module.member.controller.app.signin.vo.record.AppMemberSignInRecordSummaryRespVO; +import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInRecordDO; + +/** + * 签到记录 Service 接口 + * + * @author 芋道源码 + */ +public interface MemberSignInRecordService { + + /** + * 【管理员】获得签到记录分页 + * + * @param pageReqVO 分页查询 + * @return 签到记录分页 + */ + PageResult getSignInRecordPage(MemberSignInRecordPageReqVO pageReqVO); + + /** + * 【会员】获得签到记录分页 + * + * @param userId 用户编号 + * @param pageParam 分页查询 + * @return 签到记录分页 + */ + PageResult getSignRecordPage(Long userId, PageParam pageParam); + + /** + * 创建签到记录 + * + * @param userId 用户编号 + * @return 签到记录 + */ + MemberSignInRecordDO createSignRecord(Long userId); + + /** + * 根据用户编号,获得个人签到统计信息 + * + * @param userId 用户编号 + * @return 个人签到统计信息 + */ + AppMemberSignInRecordSummaryRespVO getSignInRecordSummary(Long userId); + + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInRecordServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInRecordServiceImpl.java new file mode 100644 index 000000000..be9b420e9 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInRecordServiceImpl.java @@ -0,0 +1,182 @@ +package cn.iocoder.yudao.module.member.service.signin; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.date.DateUtils; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.member.controller.admin.signin.vo.record.MemberSignInRecordPageReqVO; +import cn.iocoder.yudao.module.member.controller.app.signin.vo.record.AppMemberSignInRecordSummaryRespVO; +import cn.iocoder.yudao.module.member.convert.signin.MemberSignInRecordConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInConfigDO; +import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInRecordDO; +import cn.iocoder.yudao.module.member.dal.mysql.signin.MemberSignInRecordMapper; +import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum; +import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum; +import cn.iocoder.yudao.module.member.service.level.MemberLevelService; +import cn.iocoder.yudao.module.member.service.point.MemberPointRecordService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.time.LocalDate; +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.SIGN_IN_RECORD_TODAY_EXISTS; + +/** + * 签到记录 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class MemberSignInRecordServiceImpl implements MemberSignInRecordService { + + @Resource + private MemberSignInRecordMapper signInRecordMapper; + @Resource + private MemberSignInConfigService signInConfigService; + @Resource + private MemberPointRecordService pointRecordService; + @Resource + private MemberLevelService memberLevelService; + + @Resource + private MemberUserApi memberUserApi; + + @Override + public AppMemberSignInRecordSummaryRespVO getSignInRecordSummary(Long userId) { + // 1. 初始化默认返回信息 + // TODO @puhui999:这里 vo 改成 summary 会更好理解; + AppMemberSignInRecordSummaryRespVO vo = new AppMemberSignInRecordSummaryRespVO(); + vo.setTotalDay(0); + vo.setContinuousDay(0); + vo.setTodaySignIn(false); + + // 2. 获取用户签到的记录数 + Long signCount = signInRecordMapper.selectCountByUserId(userId); + if (ObjUtil.equal(signCount, 0L)) { + return vo; + } + vo.setTotalDay(signCount.intValue()); // 设置总签到天数 + + // 3. 校验当天是否有签到 + // TODO @puhui999:是不是 signInRecord 可以精简成 record 哈;另外,Desc 貌似可以去掉哈;最后一条了; + MemberSignInRecordDO signInRecord = signInRecordMapper.selectLastRecordByUserIdDesc(userId); + if (signInRecord == null) { + return vo; + } + vo.setTodaySignIn(DateUtils.isToday(signInRecord.getCreateTime())); + + // 4. 校验今天是否签到,没有签到则直接返回 + if (!vo.getTodaySignIn()) { + return vo; + } + // 4.1. 判断连续签到天数 + // TODO @puhui999:连续签到,可以基于 signInRecord 的 day 和当前时间判断呀? + List signInRecords = signInRecordMapper.selectListByUserId(userId); + vo.setContinuousDay(calculateConsecutiveDays(signInRecords)); + return vo; + } + + /** + * 计算连续签到天数 + * + * @param signInRecords 签到记录列表 + * @return int 连续签到天数 + */ + public int calculateConsecutiveDays(List signInRecords) { + int consecutiveDays = 1; // 初始连续天数为1 + LocalDate previousDate = null; + + for (MemberSignInRecordDO record : signInRecords) { + LocalDate currentDate = record.getCreateTime().toLocalDate(); + + if (previousDate != null) { + // 检查相邻两个日期是否连续 + if (currentDate.minusDays(1).isEqual(previousDate)) { + consecutiveDays++; + } else { + // 如果日期不连续,停止遍历 + break; + } + } + + previousDate = currentDate; + } + + return consecutiveDays; + } + + @Override + public PageResult getSignInRecordPage(MemberSignInRecordPageReqVO pageReqVO) { + // 根据用户昵称查询出用户ids + Set userIds = null; + if (StringUtils.isNotBlank(pageReqVO.getNickname())) { + List users = memberUserApi.getUserListByNickname(pageReqVO.getNickname()); + // 如果查询用户结果为空直接返回无需继续查询 + if (CollUtil.isEmpty(users)) { + return PageResult.empty(); + } + userIds = convertSet(users, MemberUserRespDTO::getId); + } + // 分页查询 + return signInRecordMapper.selectPage(pageReqVO, userIds); + } + + @Override + public PageResult getSignRecordPage(Long userId, PageParam pageParam) { + return signInRecordMapper.selectPage(userId, pageParam); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public MemberSignInRecordDO createSignRecord(Long userId) { + // 1. 获取当前用户最近的签到 + MemberSignInRecordDO lastRecord = signInRecordMapper.selectLastRecordByUserIdDesc(userId); + // 1.1. 判断是否重复签到 + validateSigned(lastRecord); + + // 2. 获取当前用户最早的一次前端记录,用于计算今天是第几天签到 + MemberSignInRecordDO firstRecord = signInRecordMapper.selectLastRecordByUserIdAsc(userId); + // 2.1. 获取所有的签到规则 + List signInConfigs = signInConfigService.getSignInConfigList(CommonStatusEnum.ENABLE.getStatus()); + signInConfigs.sort(Comparator.comparing(MemberSignInConfigDO::getDay)); + // 2.2. 组合数据 + MemberSignInRecordDO record = MemberSignInRecordConvert.INSTANCE.convert(userId, firstRecord, signInConfigs); + + // 3. 插入签到记录 + signInRecordMapper.insert(record); + + // 4. 增加积分 + if (!ObjectUtils.equalsAny(record.getPoint(), null, 0)) { + pointRecordService.createPointRecord(userId, record.getPoint(), MemberPointBizTypeEnum.SIGN, String.valueOf(record.getId())); + } + // 5. 增加经验 + if (!ObjectUtils.equalsAny(record.getExperience(), null, 0)) { + memberLevelService.addExperience(userId, record.getExperience(), MemberExperienceBizTypeEnum.SIGN_IN, String.valueOf(record.getId())); + } + return record; + } + + private void validateSigned(MemberSignInRecordDO signInRecordDO) { + if (signInRecordDO == null) { + return; + } + if (DateUtils.isToday(signInRecordDO.getCreateTime())) { + throw exception(SIGN_IN_RECORD_TODAY_EXISTS); + } + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/tag/MemberTagService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/tag/MemberTagService.java new file mode 100644 index 000000000..5e3393394 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/tag/MemberTagService.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.member.service.tag; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.controller.admin.tag.vo.MemberTagCreateReqVO; +import cn.iocoder.yudao.module.member.controller.admin.tag.vo.MemberTagPageReqVO; +import cn.iocoder.yudao.module.member.controller.admin.tag.vo.MemberTagUpdateReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO; + +import javax.validation.Valid; +import java.util.Collection; +import java.util.List; + +/** + * 会员标签 Service 接口 + * + * @author 芋道源码 + */ +public interface MemberTagService { + + /** + * 创建会员标签 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createTag(@Valid MemberTagCreateReqVO createReqVO); + + /** + * 更新会员标签 + * + * @param updateReqVO 更新信息 + */ + void updateTag(@Valid MemberTagUpdateReqVO updateReqVO); + + /** + * 删除会员标签 + * + * @param id 编号 + */ + void deleteTag(Long id); + + /** + * 获得会员标签 + * + * @param id 编号 + * @return 会员标签 + */ + MemberTagDO getTag(Long id); + + /** + * 获得会员标签列表 + * + * @param ids 编号 + * @return 会员标签列表 + */ + List getTagList(Collection ids); + + /** + * 获得会员标签分页 + * + * @param pageReqVO 分页查询 + * @return 会员标签分页 + */ + PageResult getTagPage(MemberTagPageReqVO pageReqVO); + + /** + * 获取标签列表 + * + * @return 标签列表 + */ + List getTagList(); + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/tag/MemberTagServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/tag/MemberTagServiceImpl.java new file mode 100644 index 000000000..b267227d9 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/tag/MemberTagServiceImpl.java @@ -0,0 +1,125 @@ +package cn.iocoder.yudao.module.member.service.tag; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.controller.admin.tag.vo.MemberTagCreateReqVO; +import cn.iocoder.yudao.module.member.controller.admin.tag.vo.MemberTagPageReqVO; +import cn.iocoder.yudao.module.member.controller.admin.tag.vo.MemberTagUpdateReqVO; +import cn.iocoder.yudao.module.member.convert.tag.MemberTagConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO; +import cn.iocoder.yudao.module.member.dal.mysql.tag.MemberTagMapper; +import cn.iocoder.yudao.module.member.service.user.MemberUserService; +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.*; + +/** + * 会员标签 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class MemberTagServiceImpl implements MemberTagService { + + @Resource + private MemberTagMapper memberTagMapper; + + @Resource + private MemberUserService memberUserService; + + @Override + public Long createTag(MemberTagCreateReqVO createReqVO) { + // 校验名称唯一 + validateTagNameUnique(null, createReqVO.getName()); + // 插入 + MemberTagDO tag = MemberTagConvert.INSTANCE.convert(createReqVO); + memberTagMapper.insert(tag); + // 返回 + return tag.getId(); + } + + @Override + public void updateTag(MemberTagUpdateReqVO updateReqVO) { + // 校验存在 + validateTagExists(updateReqVO.getId()); + // 校验名称唯一 + validateTagNameUnique(updateReqVO.getId(), updateReqVO.getName()); + // 更新 + MemberTagDO updateObj = MemberTagConvert.INSTANCE.convert(updateReqVO); + memberTagMapper.updateById(updateObj); + } + + @Override + public void deleteTag(Long id) { + // 校验存在 + validateTagExists(id); + // 校验标签下是否有用户 + validateTagHasUser(id); + // 删除 + memberTagMapper.deleteById(id); + } + + private void validateTagExists(Long id) { + if (memberTagMapper.selectById(id) == null) { + throw exception(TAG_NOT_EXISTS); + } + } + + private void validateTagNameUnique(Long id, String name) { + if (StrUtil.isBlank(name)) { + return; + } + MemberTagDO tag = memberTagMapper.selelctByName(name); + if (tag == null) { + return; + } + + // 如果 id 为空,说明不用比较是否为相同 id 的标签 + if (id == null) { + throw exception(TAG_NAME_EXISTS); + } + if (!tag.getId().equals(id)) { + throw exception(TAG_NAME_EXISTS); + } + } + + void validateTagHasUser(Long id) { + Long count = memberUserService.getUserCountByTagId(id); + if (count > 0) { + throw exception(TAG_HAS_USER); + } + } + + @Override + public MemberTagDO getTag(Long id) { + return memberTagMapper.selectById(id); + } + + @Override + public List getTagList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return ListUtil.empty(); + } + return memberTagMapper.selectBatchIds(ids); + } + + @Override + public PageResult getTagPage(MemberTagPageReqVO pageReqVO) { + return memberTagMapper.selectPage(pageReqVO); + } + + @Override + public List getTagList() { + return memberTagMapper.selectList(); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java new file mode 100644 index 000000000..8b640c850 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java @@ -0,0 +1,172 @@ +package cn.iocoder.yudao.module.member.service.user; + +import cn.iocoder.yudao.framework.common.enums.TerminalEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.validation.Mobile; +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.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.AppMemberUserUpdateReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; + +import javax.validation.Valid; +import java.util.Collection; +import java.util.List; + +/** + * 会员用户 Service 接口 + * + * @author 芋道源码 + */ +public interface MemberUserService { + + /** + * 通过手机查询用户 + * + * @param mobile 手机 + * @return 用户对象 + */ + MemberUserDO getUserByMobile(String mobile); + + /** + * 基于用户昵称,模糊匹配用户列表 + * + * @param nickname 用户昵称,模糊匹配 + * @return 用户信息的列表 + */ + List getUserListByNickname(String nickname); + + /** + * 基于手机号创建用户。 + * 如果用户已经存在,则直接进行返回 + * + * @param mobile 手机号 + * @param registerIp 注册 IP + * @param terminal 终端 {@link TerminalEnum} + * @return 用户对象 + */ + MemberUserDO createUserIfAbsent(@Mobile String mobile, String registerIp, Integer terminal); + + /** + * 更新用户的最后登陆信息 + * + * @param id 用户编号 + * @param loginIp 登陆 IP + */ + void updateUserLogin(Long id, String loginIp); + + /** + * 通过用户 ID 查询用户 + * + * @param id 用户ID + * @return 用户对象信息 + */ + MemberUserDO getUser(Long id); + + /** + * 通过用户 ID 查询用户们 + * + * @param ids 用户 ID + * @return 用户对象信息数组 + */ + List getUserList(Collection ids); + + /** + * 【会员】修改基本信息 + * + * @param userId 用户编号 + * @param reqVO 基本信息 + */ + void updateUser(Long userId, AppMemberUserUpdateReqVO reqVO); + + /** + * 【会员】修改手机 + * + * @param userId 用户编号 + * @param reqVO 请求信息 + */ + void updateUserMobile(Long userId, AppMemberUserUpdateMobileReqVO reqVO); + + /** + * 【会员】修改密码 + * + * @param userId 用户编号 + * @param reqVO 请求信息 + */ + void updateUserPassword(Long userId, AppMemberUserUpdatePasswordReqVO reqVO); + + /** + * 【会员】忘记密码 + * + * @param reqVO 请求信息 + */ + void resetUserPassword(AppMemberUserResetPasswordReqVO reqVO); + + /** + * 判断密码是否匹配 + * + * @param rawPassword 未加密的密码 + * @param encodedPassword 加密后的密码 + * @return 是否匹配 + */ + boolean isPasswordMatch(String rawPassword, String encodedPassword); + + /** + * 【管理员】更新会员用户 + * + * @param updateReqVO 更新信息 + */ + void updateUser(@Valid MemberUserUpdateReqVO updateReqVO); + + /** + * 【管理员】获得会员用户分页 + * + * @param pageReqVO 分页查询 + * @return 会员用户分页 + */ + PageResult getUserPage(MemberUserPageReqVO pageReqVO); + + /** + * 更新用户的等级和经验 + * + * @param id 用户编号 + * @param levelId 用户等级 + * @param experience 用户经验 + */ + void updateUserLevel(Long id, Long levelId, Integer experience); + + /** + * 获得指定用户分组下的用户数量 + * + * @param groupId 用户分组编号 + * @return 用户数量 + */ + Long getUserCountByGroupId(Long groupId); + + /** + * 获得指定用户等级下的用户数量 + * + * @param levelId 用户等级编号 + * @return 用户数量 + */ + Long getUserCountByLevelId(Long levelId); + + /** + * 获得指定会员标签下的用户数量 + * + * @param tagId 用户标签编号 + * @return 用户数量 + */ + Long getUserCountByTagId(Long tagId); + + /** + * 更新用户的积分 + * + * @param userId 用户编号 + * @param point 积分数量 + * @return 更新结果 + */ + boolean updateUserPoint(Long userId, Integer point); +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java new file mode 100644 index 000000000..0d53c388d --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java @@ -0,0 +1,294 @@ +package cn.iocoder.yudao.module.member.service.user; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +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.MemberUserUpdateReqVO; +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.AppMemberUserUpdateReqVO; +import cn.iocoder.yudao.module.member.convert.auth.AuthConvert; +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.mysql.user.MemberUserMapper; +import cn.iocoder.yudao.module.member.mq.producer.user.UserCreateProducer; +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.enums.sms.SmsSceneEnum; +import com.google.common.annotations.VisibleForTesting; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionSynchronization; +import org.springframework.transaction.support.TransactionSynchronizationManager; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; +import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*; + +/** + * 会员 User Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Valid +@Slf4j +public class MemberUserServiceImpl implements MemberUserService { + + @Resource + private MemberUserMapper memberUserMapper; + + @Resource + private FileApi fileApi; + @Resource + private SmsCodeApi smsCodeApi; + + @Resource + private PasswordEncoder passwordEncoder; + + @Resource + private UserCreateProducer registerCouponProducer; + + @Resource + private TransactionTemplate transactionTemplate; + + @Override + public MemberUserDO getUserByMobile(String mobile) { + return memberUserMapper.selectByMobile(mobile); + } + + @Override + public List getUserListByNickname(String nickname) { + return memberUserMapper.selectListByNicknameLike(nickname); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public MemberUserDO createUserIfAbsent(String mobile, String registerIp, Integer terminal) { + // 用户已经存在 + MemberUserDO user = memberUserMapper.selectByMobile(mobile); + if (user != null) { + return user; + } + // 用户不存在,则进行创建 + return createUser(mobile, registerIp, terminal); + } + + private MemberUserDO createUser(String mobile, String registerIp, Integer terminal) { + // 生成密码 + String password = IdUtil.fastSimpleUUID(); + // 插入用户 + MemberUserDO user = new MemberUserDO(); + user.setMobile(mobile); + user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启 + user.setPassword(encodePassword(password)); // 加密密码 + user.setRegisterIp(registerIp); + user.setRegisterTerminal(terminal); + memberUserMapper.insert(user); + + // 发送 MQ 消息:用户创建 + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + + @Override + public void afterCommit() { + registerCouponProducer.sendUserCreateMessage(user.getId()); + } + + }); + return user; + } + + @Override + public void updateUserLogin(Long id, String loginIp) { + memberUserMapper.updateById(new MemberUserDO().setId(id) + .setLoginIp(loginIp).setLoginDate(LocalDateTime.now())); + } + + @Override + public MemberUserDO getUser(Long id) { + return memberUserMapper.selectById(id); + } + + @Override + public List getUserList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return ListUtil.empty(); + } + return memberUserMapper.selectBatchIds(ids); + } + + @Override + public void updateUser(Long userId, AppMemberUserUpdateReqVO reqVO) { + memberUserMapper.updateById(new MemberUserDO().setId(userId) + .setNickname(reqVO.getNickname()).setAvatar(reqVO.getAvatar())); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateUserMobile(Long userId, AppMemberUserUpdateMobileReqVO reqVO) { + // 检测用户是否存在 + MemberUserDO user = validateUserExists(userId); + // 校验新手机是否已经被绑定 + validateMobileUnique(null, reqVO.getMobile()); + + // 校验旧手机和旧验证码 + smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(user.getMobile()).setCode(reqVO.getOldCode()) + .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP())); + // 使用新验证码 + smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(reqVO.getMobile()).setCode(reqVO.getCode()) + .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP())); + + // 更新用户手机 + memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(reqVO.getMobile()).build()); + } + + @Override + public void updateUserPassword(Long userId, AppMemberUserUpdatePasswordReqVO reqVO) { + // 检测用户是否存在 + MemberUserDO user = validateUserExists(userId); + // 校验验证码 + smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(user.getMobile()).setCode(reqVO.getCode()) + .setScene(SmsSceneEnum.MEMBER_UPDATE_PASSWORD.getScene()).setUsedIp(getClientIP())); + + // 更新用户密码 + memberUserMapper.updateById(MemberUserDO.builder().id(userId) + .password(passwordEncoder.encode(reqVO.getPassword())).build()); + } + + @Override + public void resetUserPassword(AppMemberUserResetPasswordReqVO reqVO) { + // 检验用户是否存在 + MemberUserDO user = validateUserExists(reqVO.getMobile()); + + // 使用验证码 + smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_RESET_PASSWORD, + getClientIP())); + + // 更新密码 + memberUserMapper.updateById(MemberUserDO.builder().id(user.getId()) + .password(passwordEncoder.encode(reqVO.getPassword())).build()); + } + + private MemberUserDO validateUserExists(String mobile) { + MemberUserDO user = memberUserMapper.selectByMobile(mobile); + if (user == null) { + throw exception(USER_MOBILE_NOT_EXISTS); + } + return user; + } + + @Override + public boolean isPasswordMatch(String rawPassword, String encodedPassword) { + return passwordEncoder.matches(rawPassword, encodedPassword); + } + + /** + * 对密码进行加密 + * + * @param password 密码 + * @return 加密后的密码 + */ + private String encodePassword(String password) { + return passwordEncoder.encode(password); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateUser(MemberUserUpdateReqVO updateReqVO) { + // 校验存在 + validateUserExists(updateReqVO.getId()); + // 校验手机唯一 + validateMobileUnique(updateReqVO.getId(), updateReqVO.getMobile()); + + // 更新 + MemberUserDO updateObj = MemberUserConvert.INSTANCE.convert(updateReqVO); + memberUserMapper.updateById(updateObj); + } + + @VisibleForTesting + MemberUserDO validateUserExists(Long id) { + if (id == null) { + return null; + } + MemberUserDO user = memberUserMapper.selectById(id); + if (user == null) { + throw exception(USER_NOT_EXISTS); + } + return user; + } + + @VisibleForTesting + void validateMobileUnique(Long id, String mobile) { + if (StrUtil.isBlank(mobile)) { + return; + } + MemberUserDO user = memberUserMapper.selectByMobile(mobile); + if (user == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的用户 + if (id == null) { + throw exception(USER_MOBILE_USED, mobile); + } + if (!user.getId().equals(id)) { + throw exception(USER_MOBILE_USED, mobile); + } + } + + @Override + public PageResult getUserPage(MemberUserPageReqVO pageReqVO) { + return memberUserMapper.selectPage(pageReqVO); + } + + @Override + public void updateUserLevel(Long id, Long levelId, Integer experience) { + // 0 代表无等级:防止UpdateById时,会被过滤掉的问题 + levelId = ObjectUtil.defaultIfNull(levelId, 0L); + memberUserMapper.updateById(new MemberUserDO() + .setId(id) + .setLevelId(levelId).setExperience(experience) + ); + } + + @Override + public Long getUserCountByGroupId(Long groupId) { + return memberUserMapper.selectCountByGroupId(groupId); + } + + @Override + public Long getUserCountByLevelId(Long levelId) { + return memberUserMapper.selectCountByLevelId(levelId); + } + + @Override + public Long getUserCountByTagId(Long tagId) { + return memberUserMapper.selectCountByTagId(tagId); + } + + @Override + public boolean updateUserPoint(Long id, Integer point) { + if (point > 0) { + memberUserMapper.updatePointIncr(id, point); + } else if (point < 0) { + return memberUserMapper.updatePointDecr(id, point) > 0; + } + return true; + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImplTest.java b/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImplTest.java new file mode 100644 index 000000000..e4337f2c9 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImplTest.java @@ -0,0 +1,98 @@ +package cn.iocoder.yudao.module.member.service.address; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressCreateReqVO; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressUpdateReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.address.MemberAddressDO; +import cn.iocoder.yudao.module.member.dal.mysql.address.MemberAddressMapper; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import javax.annotation.Resource; + +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.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.ADDRESS_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * {@link AddressServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(AddressServiceImpl.class) +public class AddressServiceImplTest extends BaseDbUnitTest { + + @Resource + private AddressServiceImpl addressService; + + @Resource + private MemberAddressMapper addressMapper; + + @Test + public void testCreateAddress_success() { + // 准备参数 + AppAddressCreateReqVO reqVO = randomPojo(AppAddressCreateReqVO.class); + + // 调用 + Long addressId = addressService.createAddress(randomLongId(), reqVO); + // 断言 + assertNotNull(addressId); + // 校验记录的属性是否正确 + MemberAddressDO address = addressMapper.selectById(addressId); + assertPojoEquals(reqVO, address); + } + + @Test + public void testUpdateAddress_success() { + // mock 数据 + MemberAddressDO dbAddress = randomPojo(MemberAddressDO.class); + addressMapper.insert(dbAddress);// @Sql: 先插入出一条存在的数据 + // 准备参数 + AppAddressUpdateReqVO reqVO = randomPojo(AppAddressUpdateReqVO.class, o -> { + o.setId(dbAddress.getId()); // 设置更新的 ID + }); + + // 调用 + addressService.updateAddress(dbAddress.getUserId(), reqVO); + // 校验是否更新正确 + MemberAddressDO address = addressMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, address); + } + + @Test + public void testUpdateAddress_notExists() { + // 准备参数 + AppAddressUpdateReqVO reqVO = randomPojo(AppAddressUpdateReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> addressService.updateAddress(randomLongId(), reqVO), ADDRESS_NOT_EXISTS); + } + + @Test + public void testDeleteAddress_success() { + // mock 数据 + MemberAddressDO dbAddress = randomPojo(MemberAddressDO.class); + addressMapper.insert(dbAddress);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbAddress.getId(); + + // 调用 + addressService.deleteAddress(dbAddress.getUserId(), id); + // 校验数据不存在了 + assertNull(addressMapper.selectById(id)); + } + + @Test + public void testDeleteAddress_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> addressService.deleteAddress(randomLongId(), id), ADDRESS_NOT_EXISTS); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceTest.java b/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceTest.java new file mode 100644 index 000000000..78ddc5677 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceTest.java @@ -0,0 +1,118 @@ +package cn.iocoder.yudao.module.member.service.auth; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; +import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; +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.service.user.MemberUserService; +import cn.iocoder.yudao.module.system.api.logger.LoginLogApi; +import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi; +import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; +import cn.iocoder.yudao.module.system.api.social.SocialUserApi; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.security.crypto.password.PasswordEncoder; + +import javax.annotation.Resource; +import java.util.function.Consumer; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; + +// TODO @芋艿:单测的 review,等逻辑都达成一致后 +/** + * {@link MemberAuthService} 的单元测试类 + * + * @author 宋天 + */ +@Import({MemberAuthServiceImpl.class, YudaoRedisAutoConfiguration.class}) +public class MemberAuthServiceTest extends BaseDbAndRedisUnitTest { + + // TODO @芋艿:登录相关的单测,待补全 + + @Resource + private MemberAuthServiceImpl authService; + + @MockBean + private MemberUserService userService; + @MockBean + private SmsCodeApi smsCodeApi; + @MockBean + private LoginLogApi loginLogApi; + @MockBean + private OAuth2TokenApi oauth2TokenApi; + @MockBean + private SocialUserApi socialUserApi; + @MockBean + private PasswordEncoder passwordEncoder; + + @Resource + private MemberUserMapper memberUserMapper; + + // TODO 芋艿:后续重构这个单测 +// @Test +// public void testUpdatePassword_success(){ +// // 准备参数 +// MemberUserDO userDO = randomUserDO(); +// memberUserMapper.insert(userDO); +// +// // 新密码 +// String newPassword = randomString(); +// +// // 请求实体 +// AppMemberUserUpdatePasswordReqVO reqVO = AppMemberUserUpdatePasswordReqVO.builder() +// .oldPassword(userDO.getPassword()) +// .password(newPassword) +// .build(); +// +// // 测试桩 +// // 这两个相等是为了返回ture这个结果 +// when(passwordEncoder.matches(reqVO.getOldPassword(),reqVO.getOldPassword())).thenReturn(true); +// when(passwordEncoder.encode(newPassword)).thenReturn(newPassword); +// +// // 更新用户密码 +// authService.updatePassword(userDO.getId(), reqVO); +// assertEquals(memberUserMapper.selectById(userDO.getId()).getPassword(),newPassword); +// } + + // TODO 芋艿:后续重构这个单测 +// @Test +// public void testResetPassword_success(){ +// // 准备参数 +// MemberUserDO userDO = randomUserDO(); +// memberUserMapper.insert(userDO); +// +// // 随机密码 +// String password = randomNumbers(11); +// // 随机验证码 +// String code = randomNumbers(4); +// +// // mock +// when(passwordEncoder.encode(password)).thenReturn(password); +// +// // 更新用户密码 +// AppMemberUserResetPasswordReqVO reqVO = new AppMemberUserResetPasswordReqVO(); +// reqVO.setMobile(userDO.getMobile()); +// reqVO.setPassword(password); +// reqVO.setCode(code); +// +// authService.resetPassword(reqVO); +// assertEquals(memberUserMapper.selectById(userDO.getId()).getPassword(),password); +// } + + // ========== 随机对象 ========== + + @SafeVarargs + private static MemberUserDO randomUserDO(Consumer... consumers) { + Consumer consumer = (o) -> { + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 + o.setPassword(randomString()); + }; + return randomPojo(MemberUserDO.class, ArrayUtils.append(consumer, consumers)); + } + + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/group/MemberGroupServiceImplTest.java b/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/group/MemberGroupServiceImplTest.java new file mode 100644 index 000000000..6d8e6f9ce --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/group/MemberGroupServiceImplTest.java @@ -0,0 +1,160 @@ +package cn.iocoder.yudao.module.member.service.group; + +import cn.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.mysql.group.MemberGroupMapper; +import cn.iocoder.yudao.module.member.service.user.MemberUserService; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +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.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +// TODO 芋艿:完全 review 完,在去 review 单测 +/** + * {@link MemberGroupServiceImpl} 的单元测试类 + * + * @author owen + */ +@Import(MemberGroupServiceImpl.class) +public class MemberGroupServiceImplTest extends BaseDbUnitTest { + + @Resource + private MemberGroupServiceImpl groupService; + + @Resource + private MemberGroupMapper groupMapper; + + @MockBean + private MemberUserService memberUserService; + + @Test + public void testCreateGroup_success() { + // 准备参数 + MemberGroupCreateReqVO reqVO = randomPojo(MemberGroupCreateReqVO.class, + o -> o.setStatus(randomCommonStatus())); + + // 调用 + 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 + o.setStatus(randomCommonStatus()); + }); + + // 调用 + 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 会员数据 + when(memberUserService.getUserCountByGroupId(eq(id))).thenReturn(1L); + + // 调用, 并断言异常 + 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 pageResult = groupService.getGroupPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbGroup, pageResult.getList().get(0)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/level/MemberLevelServiceImplTest.java b/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/level/MemberLevelServiceImplTest.java new file mode 100644 index 000000000..439322913 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/level/MemberLevelServiceImplTest.java @@ -0,0 +1,268 @@ +package cn.iocoder.yudao.module.member.service.level; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.level.MemberLevelCreateReqVO; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.level.MemberLevelListReqVO; +import cn.iocoder.yudao.module.member.controller.admin.level.vo.level.MemberLevelUpdateReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberLevelDO; +import cn.iocoder.yudao.module.member.dal.mysql.level.MemberLevelMapper; +import cn.iocoder.yudao.module.member.service.user.MemberUserService; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import javax.annotation.Resource; +import java.util.List; +import java.util.function.Consumer; + +import static cn.hutool.core.util.RandomUtil.randomInt; +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.*; +import static org.junit.jupiter.api.Assertions.*; + +// TODO 芋艿:完全 review 完,在去 review 单测 +/** + * {@link MemberLevelServiceImpl} 的单元测试类 + * + * @author owen + */ +@Import(MemberLevelServiceImpl.class) +public class MemberLevelServiceImplTest extends BaseDbUnitTest { + + @Resource + private MemberLevelServiceImpl levelService; + + @Resource + private MemberLevelMapper memberlevelMapper; + + @MockBean + private MemberLevelRecordService memberLevelRecordService; + @MockBean + private MemberExperienceRecordService memberExperienceRecordService; + @MockBean + private MemberUserService memberUserService; + + @Test + public void testCreateLevel_success() { + // 准备参数 + MemberLevelCreateReqVO reqVO = randomPojo(MemberLevelCreateReqVO.class, o -> { + o.setDiscountPercent(randomInt()); + o.setIcon(randomURL()); + o.setBackgroundUrl(randomURL()); + o.setStatus(randomCommonStatus()); + }); + + // 调用 + Long levelId = levelService.createLevel(reqVO); + // 断言 + assertNotNull(levelId); + // 校验记录的属性是否正确 + MemberLevelDO level = memberlevelMapper.selectById(levelId); + assertPojoEquals(reqVO, level); + } + + @Test + public void testUpdateLevel_success() { + // mock 数据 + MemberLevelDO dbLevel = randomPojo(MemberLevelDO.class); + memberlevelMapper.insert(dbLevel);// @Sql: 先插入出一条存在的数据 + // 准备参数 + MemberLevelUpdateReqVO reqVO = randomPojo(MemberLevelUpdateReqVO.class, o -> { + o.setId(dbLevel.getId()); // 设置更新的 ID + //以下要保持一致 + o.setName(dbLevel.getName()); + o.setLevel(dbLevel.getLevel()); + o.setExperience(dbLevel.getExperience()); + //以下是要修改的字段 + o.setDiscountPercent(randomInt()); + o.setIcon(randomURL()); + o.setBackgroundUrl(randomURL()); + o.setStatus(randomCommonStatus()); + }); + + // 调用 + levelService.updateLevel(reqVO); + // 校验是否更新正确 + MemberLevelDO level = memberlevelMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, level); + } + + @Test + public void testUpdateLevel_notExists() { + // 准备参数 + MemberLevelUpdateReqVO reqVO = randomPojo(MemberLevelUpdateReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> levelService.updateLevel(reqVO), LEVEL_NOT_EXISTS); + } + + @Test + public void testDeleteLevel_success() { + // mock 数据 + MemberLevelDO dbLevel = randomPojo(MemberLevelDO.class); + memberlevelMapper.insert(dbLevel);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbLevel.getId(); + + // 调用 + levelService.deleteLevel(id); + // 校验数据不存在了 + assertNull(memberlevelMapper.selectById(id)); + } + + @Test + public void testDeleteLevel_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> levelService.deleteLevel(id), LEVEL_NOT_EXISTS); + } + + @Test + public void testGetLevelList() { + // mock 数据 + MemberLevelDO dbLevel = randomPojo(MemberLevelDO.class, o -> { // 等会查询到 + o.setName("黄金会员"); + o.setStatus(1); + }); + memberlevelMapper.insert(dbLevel); + // 测试 name 不匹配 + memberlevelMapper.insert(cloneIgnoreId(dbLevel, o -> o.setName(""))); + // 测试 status 不匹配 + memberlevelMapper.insert(cloneIgnoreId(dbLevel, o -> o.setStatus(0))); + // 准备参数 + MemberLevelListReqVO reqVO = new MemberLevelListReqVO(); + reqVO.setName("黄金会员"); + reqVO.setStatus(1); + + // 调用 + List list = levelService.getLevelList(reqVO); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(dbLevel, list.get(0)); + } + + @Test + public void testCreateLevel_nameUnique() { + // 准备参数 + String name = randomString(); + + // mock 数据 + memberlevelMapper.insert(randomLevelDO(o -> o.setName(name))); + + // 调用,校验异常 + List list = memberlevelMapper.selectList(); + assertServiceException(() -> levelService.validateNameUnique(list, null, name), LEVEL_NAME_EXISTS, name); + } + + @Test + public void testUpdateLevel_nameUnique() { + // 准备参数 + Long id = randomLongId(); + String name = randomString(); + + // mock 数据 + memberlevelMapper.insert(randomLevelDO(o -> o.setName(name))); + + // 调用,校验异常 + List list = memberlevelMapper.selectList(); + assertServiceException(() -> levelService.validateNameUnique(list, id, name), LEVEL_NAME_EXISTS, name); + } + + @Test + public void testCreateLevel_levelUnique() { + // 准备参数 + Integer level = randomInteger(); + String name = randomString(); + + // mock 数据 + memberlevelMapper.insert(randomLevelDO(o -> { + o.setLevel(level); + o.setName(name); + })); + + // 调用,校验异常 + List list = memberlevelMapper.selectList(); + assertServiceException(() -> levelService.validateLevelUnique(list, null, level), LEVEL_VALUE_EXISTS, level, name); + } + + @Test + public void testUpdateLevel_levelUnique() { + // 准备参数 + Long id = randomLongId(); + Integer level = randomInteger(); + String name = randomString(); + + // mock 数据 + memberlevelMapper.insert(randomLevelDO(o -> { + o.setLevel(level); + o.setName(name); + })); + + // 调用,校验异常 + List list = memberlevelMapper.selectList(); + assertServiceException(() -> levelService.validateLevelUnique(list, id, level), LEVEL_VALUE_EXISTS, level, name); + } + + @Test + public void testCreateLevel_experienceOutRange() { + // 准备参数 + int level = 10; + int experience = 10; + String name = randomString(); + + // mock 数据 + memberlevelMapper.insert(randomLevelDO(o -> { + o.setLevel(level); + o.setExperience(experience); + o.setName(name); + })); + List list = memberlevelMapper.selectList(); + + // 调用,校验异常 + assertServiceException(() -> levelService.validateExperienceOutRange(list, null, level + 1, experience - 1), LEVEL_EXPERIENCE_MIN, name, level); + // 调用,校验异常 + assertServiceException(() -> levelService.validateExperienceOutRange(list, null, level - 1, experience + 1), LEVEL_EXPERIENCE_MAX, name, level); + } + + @Test + public void testUpdateLevel_experienceOutRange() { + // 准备参数 + int level = 10; + int experience = 10; + Long id = randomLongId(); + String name = randomString(); + + // mock 数据 + memberlevelMapper.insert(randomLevelDO(o -> { + o.setLevel(level); + o.setExperience(experience); + o.setName(name); + })); + List list = memberlevelMapper.selectList(); + + // 调用,校验异常 + assertServiceException(() -> levelService.validateExperienceOutRange(list, id, level + 1, experience - 1), LEVEL_EXPERIENCE_MIN, name, level); + // 调用,校验异常 + assertServiceException(() -> levelService.validateExperienceOutRange(list, id, level - 1, experience + 1), LEVEL_EXPERIENCE_MAX, name, level); + } + + // ========== 随机对象 ========== + + @SafeVarargs + private static MemberLevelDO randomLevelDO(Consumer... consumers) { + Consumer consumer = (o) -> { + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setDiscountPercent(randomInt(0, 100)); + o.setIcon(randomURL()); + o.setBackgroundUrl(randomURL()); + }; + return randomPojo(MemberLevelDO.class, ArrayUtils.append(consumer, consumers)); + } +} diff --git a/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/tag/MemberTagServiceImplTest.java b/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/tag/MemberTagServiceImplTest.java new file mode 100644 index 000000000..5fd4f7a9c --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/tag/MemberTagServiceImplTest.java @@ -0,0 +1,133 @@ +package cn.iocoder.yudao.module.member.service.tag; + +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.tag.vo.MemberTagCreateReqVO; +import cn.iocoder.yudao.module.member.controller.admin.tag.vo.MemberTagPageReqVO; +import cn.iocoder.yudao.module.member.controller.admin.tag.vo.MemberTagUpdateReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO; +import cn.iocoder.yudao.module.member.dal.mysql.tag.MemberTagMapper; +import cn.iocoder.yudao.module.member.service.user.MemberUserService; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +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.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.TAG_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; + +// TODO 芋艿:完全 review 完,在去 review 单测 +/** + * {@link MemberTagServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(MemberTagServiceImpl.class) +public class MemberTagServiceImplTest extends BaseDbUnitTest { + + @Resource + private MemberTagServiceImpl tagService; + + @Resource + private MemberTagMapper tagMapper; + + @MockBean + private MemberUserService memberUserService; + + @Test + public void testCreateTag_success() { + // 准备参数 + MemberTagCreateReqVO reqVO = randomPojo(MemberTagCreateReqVO.class); + + // 调用 + Long tagId = tagService.createTag(reqVO); + // 断言 + assertNotNull(tagId); + // 校验记录的属性是否正确 + MemberTagDO tag = tagMapper.selectById(tagId); + assertPojoEquals(reqVO, tag); + } + + @Test + public void testUpdateTag_success() { + // mock 数据 + MemberTagDO dbTag = randomPojo(MemberTagDO.class); + tagMapper.insert(dbTag);// @Sql: 先插入出一条存在的数据 + // 准备参数 + MemberTagUpdateReqVO reqVO = randomPojo(MemberTagUpdateReqVO.class, o -> { + o.setId(dbTag.getId()); // 设置更新的 ID + }); + + // 调用 + tagService.updateTag(reqVO); + // 校验是否更新正确 + MemberTagDO tag = tagMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, tag); + } + + @Test + public void testUpdateTag_notExists() { + // 准备参数 + MemberTagUpdateReqVO reqVO = randomPojo(MemberTagUpdateReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> tagService.updateTag(reqVO), TAG_NOT_EXISTS); + } + + @Test + public void testDeleteTag_success() { + // mock 数据 + MemberTagDO dbTag = randomPojo(MemberTagDO.class); + tagMapper.insert(dbTag);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbTag.getId(); + + // 调用 + tagService.deleteTag(id); + // 校验数据不存在了 + assertNull(tagMapper.selectById(id)); + } + + @Test + public void testDeleteTag_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> tagService.deleteTag(id), TAG_NOT_EXISTS); + } + + @Test + public void testGetTagPage() { + // mock 数据 + MemberTagDO dbTag = randomPojo(MemberTagDO.class, o -> { // 等会查询到 + o.setName("test"); + o.setCreateTime(buildTime(2023, 2, 18)); + }); + tagMapper.insert(dbTag); + // 测试 name 不匹配 + tagMapper.insert(cloneIgnoreId(dbTag, o -> o.setName("ne"))); + // 测试 createTime 不匹配 + tagMapper.insert(cloneIgnoreId(dbTag, o -> o.setCreateTime(null))); + // 准备参数 + MemberTagPageReqVO reqVO = new MemberTagPageReqVO(); + reqVO.setName("test"); + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + + // 调用 + PageResult pageResult = tagService.getTagPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbTag, pageResult.getList().get(0)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImplTest.java b/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImplTest.java new file mode 100644 index 000000000..bff2ae0c1 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImplTest.java @@ -0,0 +1,136 @@ +package cn.iocoder.yudao.module.member.service.user; + +import cn.hutool.core.util.RandomUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; +import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; +import cn.iocoder.yudao.module.infra.api.file.FileApi; +import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateMobileReqVO; +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.service.auth.MemberAuthServiceImpl; +import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.security.crypto.password.PasswordEncoder; + +import javax.annotation.Resource; +import java.util.function.Consumer; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.hutool.core.util.RandomUtil.randomNumbers; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static org.junit.jupiter.api.Assertions.assertEquals; + +// TODO @芋艿:单测的 review,等逻辑都达成一致后 +/** + * {@link MemberUserServiceImpl} 的单元测试类 + * + * @author 宋天 + */ +@Import({MemberUserServiceImpl.class, YudaoRedisAutoConfiguration.class}) +public class MemberUserServiceImplTest extends BaseDbAndRedisUnitTest { + + @Resource + private MemberUserServiceImpl memberUserService; + + @Resource + private StringRedisTemplate stringRedisTemplate; + + @Resource + private MemberUserMapper userMapper; + + @MockBean + private MemberAuthServiceImpl authService; + + @MockBean + private PasswordEncoder passwordEncoder; + + @MockBean + private SmsCodeApi smsCodeApi; + @MockBean + private FileApi fileApi; + + // TODO 芋艿:后续重构这个单测 +// @Test +// public void testUpdateNickName_success(){ +// // mock 数据 +// MemberUserDO userDO = randomUserDO(); +// userMapper.insert(userDO); +// +// // 随机昵称 +// String newNickName = randomString(); +// +// // 调用接口修改昵称 +// memberUserService.updateUser(userDO.getId(),newNickName); +// // 查询新修改后的昵称 +// String nickname = memberUserService.getUser(userDO.getId()).getNickname(); +// // 断言 +// assertEquals(newNickName,nickname); +// } +// +// @Test +// public void testUpdateAvatar_success() throws Exception { +// // mock 数据 +// MemberUserDO dbUser = randomUserDO(); +// userMapper.insert(dbUser); +// +// // 准备参数 +// Long userId = dbUser.getId(); +// byte[] avatarFileBytes = randomBytes(10); +// ByteArrayInputStream avatarFile = new ByteArrayInputStream(avatarFileBytes); +// // mock 方法 +// String avatar = randomString(); +// when(fileApi.createFile(eq(avatarFileBytes))).thenReturn(avatar); +// // 调用 +// String str = memberUserService.updateUserAvatar(userId, avatarFile); +// // 断言 +// assertEquals(avatar, str); +// } + + @Test + @Disabled // TODO 芋艿:后续再修复 + public void updateMobile_success(){ + // mock数据 + String oldMobile = randomNumbers(11); + MemberUserDO userDO = randomUserDO(); + userDO.setMobile(oldMobile); + userMapper.insert(userDO); + + // TODO 芋艿:需要修复该单元测试,重构多模块带来的 + // 旧手机和旧验证码 +// SmsCodeDO codeDO = new SmsCodeDO(); + String oldCode = RandomUtil.randomString(4); +// codeDO.setMobile(userDO.getMobile()); +// codeDO.setCode(oldCode); +// codeDO.setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()); +// codeDO.setUsed(Boolean.FALSE); +// when(smsCodeService.checkCodeIsExpired(codeDO.getMobile(),codeDO.getCode(),codeDO.getScene())).thenReturn(codeDO); + + // 更新手机号 + String newMobile = randomNumbers(11); + String newCode = randomNumbers(4); + AppMemberUserUpdateMobileReqVO reqVO = new AppMemberUserUpdateMobileReqVO(); + reqVO.setMobile(newMobile); + reqVO.setCode(newCode); + reqVO.setOldCode(oldCode); + memberUserService.updateUserMobile(userDO.getId(),reqVO); + + assertEquals(memberUserService.getUser(userDO.getId()).getMobile(),newMobile); + } + + // ========== 随机对象 ========== + + @SafeVarargs + private static MemberUserDO randomUserDO(Consumer... consumers) { + Consumer consumer = (o) -> { + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 + }; + return randomPojo(MemberUserDO.class, ArrayUtils.append(consumer, consumers)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/test/resources/application-unit-test.yaml b/yudao-module-member/yudao-module-member-biz/src/test/resources/application-unit-test.yaml new file mode 100644 index 000000000..a384353aa --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/test/resources/application-unit-test.yaml @@ -0,0 +1,49 @@ +spring: + main: + lazy-initialization: true # 开启懒加载,加快速度 + banner-mode: off # 单元测试,禁用 Banner + +--- #################### 数据库相关配置 #################### + +spring: + # 数据源配置项 + datasource: + name: ruoyi-vue-pro + url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写 + driver-class-name: org.h2.Driver + username: sa + password: + druid: + async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度 + initial-size: 1 # 单元测试,配置为 1,提升启动速度 + sql: + init: + schema-locations: classpath:/sql/create_tables.sql + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + redis: + host: 127.0.0.1 # 地址 + port: 16379 # 端口(单元测试,使用 16379 端口) + database: 0 # 数据库索引 + +mybatis: + lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 + +--- #################### 定时任务相关配置 #################### + +--- #################### 配置中心相关配置 #################### + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项(单元测试,禁用 Lock4j) + +# Resilience4j 配置项 + +--- #################### 监控相关配置 #################### + +--- #################### 芋道相关配置 #################### + +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + info: + base-package: cn.iocoder.yudao.module diff --git a/yudao-module-member/yudao-module-member-biz/src/test/resources/logback.xml b/yudao-module-member/yudao-module-member-biz/src/test/resources/logback.xml new file mode 100644 index 000000000..daf756bff --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/test/resources/logback.xml @@ -0,0 +1,4 @@ + + + + diff --git a/yudao-module-member/yudao-module-member-biz/src/test/resources/sql/clean.sql b/yudao-module-member/yudao-module-member-biz/src/test/resources/sql/clean.sql new file mode 100644 index 000000000..f972e048d --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/test/resources/sql/clean.sql @@ -0,0 +1,5 @@ +DELETE FROM "member_user"; +DELETE FROM "member_address"; +DELETE FROM "member_tag"; +DELETE FROM "member_level"; +DELETE FROM "member_group"; \ No newline at end of file diff --git a/yudao-module-member/yudao-module-member-biz/src/test/resources/sql/create_tables.sql b/yudao-module-member/yudao-module-member-biz/src/test/resources/sql/create_tables.sql new file mode 100644 index 000000000..782a81810 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/test/resources/sql/create_tables.sql @@ -0,0 +1,113 @@ +CREATE TABLE IF NOT EXISTS "member_user" +( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '编号', + "nickname" varchar(30) NOT NULL DEFAULT '' COMMENT '用户昵称', + "name" varchar(30) NULL COMMENT '真实名字', + sex tinyint null comment '性别', + birthday datetime null comment '出生日期', + area_id int null comment '所在地', + mark varchar(255) null comment '用户备注', + point int default 0 null comment '积分', + "avatar" varchar(255) NOT NULL DEFAULT '' COMMENT '头像', + "status" tinyint NOT NULL COMMENT '状态', + "mobile" varchar(11) NOT NULL COMMENT '手机号', + "password" varchar(100) NOT NULL DEFAULT '' COMMENT '密码', + "register_ip" varchar(32) NOT NULL COMMENT '注册 IP', + "login_ip" varchar(50) NULL DEFAULT '' COMMENT '最后登录IP', + "login_date" datetime NULL DEFAULT NULL COMMENT '最后登录时间', + "tag_ids" varchar(255) NULL DEFAULT NULL COMMENT '用户标签编号列表,以逗号分隔', + "level_id" bigint NULL DEFAULT NULL COMMENT '等级编号', + "experience" bigint NULL DEFAULT NULL COMMENT '经验', + "group_id" bigint NULL DEFAULT NULL COMMENT '用户分组编号', + "creator" varchar(64) NULL DEFAULT '' COMMENT '创建者', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + "updater" varchar(64) NULL DEFAULT '' COMMENT '更新者', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + "deleted" bit(1) NOT NULL DEFAULT '0' COMMENT '是否删除', + "tenant_id" bigint not null default '0', + PRIMARY KEY ("id") +) COMMENT '会员表'; + +CREATE TABLE IF NOT EXISTS "member_address" ( + "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "user_id" bigint(20) NOT NULL, + "name" varchar(10) NOT NULL, + "mobile" varchar(20) NOT NULL, + "area_id" bigint(20) NOT NULL, + "detail_address" varchar(250) NOT NULL, + "default_status" bit NOT NULL, + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "creator" varchar(64) DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "updater" varchar(64) DEFAULT '', + PRIMARY KEY ("id") +) COMMENT '用户收件地址'; + +CREATE TABLE IF NOT EXISTS "member_tag" +( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" 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 '会员标签'; + +CREATE TABLE IF NOT EXISTS "member_level" +( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "experience" int NOT NULL, + "level" int NOT NULL, + "discount_percent" int NOT NULL, + "icon" varchar NOT NULL, + "background_url" 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', + "status" tinyint NOT NULL DEFAULT '0', + PRIMARY KEY ("id") +) 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" tinyint NOT NULL DEFAULT '0', + "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 '用户分组'; +CREATE TABLE IF NOT EXISTS "member_brokerage_record" +( + "id" int NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "user_id" bigint NOT NULL, + "biz_id" varchar NOT NULL, + "biz_type" varchar NOT NULL, + "title" varchar NOT NULL, + "price" int NOT NULL, + "total_price" int NOT NULL, + "description" varchar NOT NULL, + "status" varchar NOT NULL, + "frozen_days" int NOT NULL, + "unfreeze_time" varchar, + "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 '佣金记录';