From 0140571d040178c6a1c4c6f66f8df0dcb46ab5ac Mon Sep 17 00:00:00 2001
From: KKSU <15274802129@163.com>
Date: Tue, 18 Feb 2025 11:01:09 +0800
Subject: [PATCH] feat(user): 重构用户注册逻辑并添加钱包功能

---
 src/main/java/cc/mrbird/febs/user/req/ApiRegisterDto.java                 |    9 
 src/main/java/cc/mrbird/febs/user/contant/MemberConstant.java             |   36 +++
 src/main/java/cc/mrbird/febs/user/service/IChatWalletService.java         |   13 +
 src/main/java/cc/mrbird/febs/user/service/Impl/ChatWalletServiceImpl.java |   69 +++++
 src/main/resources/mapper/modules/ChatWalletMapper.xml                    |   12 +
 src/main/java/cc/mrbird/febs/user/service/Impl/ChatMemberServiceImpl.java |  142 +++--------
 src/main/java/cc/mrbird/febs/user/service/Impl/AsyncServiceImpl.java      |   60 +++++
 src/main/java/cc/mrbird/febs/user/service/AsyncService.java               |   23 +
 src/main/java/cc/mrbird/febs/common/utils/ValidateEntityUtils.java        |  280 +++++++++++++++++++++++
 src/main/java/cc/mrbird/febs/user/mapper/ChatWalletMapper.java            |   10 
 src/main/java/cc/mrbird/febs/user/entity/ChatMember.java                  |    2 
 src/main/java/cc/mrbird/febs/user/entity/ChatWallet.java                  |   23 +
 12 files changed, 567 insertions(+), 112 deletions(-)

diff --git a/src/main/java/cc/mrbird/febs/common/utils/ValidateEntityUtils.java b/src/main/java/cc/mrbird/febs/common/utils/ValidateEntityUtils.java
new file mode 100644
index 0000000..83a8b90
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/common/utils/ValidateEntityUtils.java
@@ -0,0 +1,280 @@
+package cc.mrbird.febs.common.utils;
+
+import cc.mrbird.febs.common.exception.FebsException;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 实体验证工具类
+ */
+public class ValidateEntityUtils {
+
+    /**
+     * 确保指定列的有效性,并返回相应的实体对象
+     *      selectOne方法
+     *      此方法用于验证数据库中某列的值是否存在,并返回包含该值的实体对象
+     *      如果指定的值不存在或为null,将抛出异常
+     *
+     * @param valueToCheck 需要验证的值
+     * @param columnExtractor 列值提取器,用于指定需要验证的列
+     * @param queryWrapperExtractor 查询包装器提取器,用于执行数据库查询
+     * @param errMsg 错误消息格式字符串
+     * @param columnExtractors 可变参数(varargs)查询字段,可选,如果没有就是查询全部
+     * @param <T> 实体类类型
+     * @param <R> 列值类型
+     * @param <V> 需要验证的值的类型
+     * @return 返回包含指定列值的实体对象
+     * @throws IllegalArgumentException 如果需要验证的值为null
+     * @throws FebsException 如果查询结果为空或列值为null,或查询过程中发生异常
+     */
+    public static <T, R, V> T ensureColumnReturnEntity(
+            V valueToCheck,
+            SFunction<T, R> columnExtractor,
+            SFunction<LambdaQueryWrapper<T>, T> queryWrapperExtractor,
+            String errMsg,
+            SFunction<T, R>... columnExtractors) {
+
+        // 检查输入参数是否为null
+        if (valueToCheck == null) {
+            throw new IllegalArgumentException("The value to check cannot be null while ensureColumnReturnEntity column");
+        }
+        if (columnExtractor == null || queryWrapperExtractor == null) {
+            throw new IllegalArgumentException("Column extractor and query wrapper extractor cannot be null while ensureColumnReturnEntity column");
+        }
+
+        T entity = null;
+        try {
+            // 创建LambdaQueryWrapper并配置查询条件
+            LambdaQueryWrapper<T> wrapper = Wrappers.lambdaQuery();
+            if (columnExtractors != null && columnExtractors.length > 0) {
+                wrapper.select(columnExtractors);
+            }
+            wrapper.eq(columnExtractor, valueToCheck)
+                    .last("limit 1");
+
+            // 执行查询并获取结果实体
+            entity = queryWrapperExtractor.apply(wrapper);
+
+            // 如果查询结果为空,则抛出异常
+            if (entity == null) {
+                throw new FebsException(StrUtil.format(errMsg, valueToCheck));
+            }
+
+            // 提取查询结果中的列值
+            R columnValue = columnExtractor.apply(entity);
+            // 如果列值为null,则抛出异常
+            if (columnValue == null) {
+                throw new FebsException(StrUtil.format(errMsg, valueToCheck));
+            }
+
+        } catch (FebsException e) {
+            // 记录异常日志
+            throw e;
+        } catch (Exception e) {
+            // 记录异常日志
+            throw new FebsException(StrUtil.format("An error occurred while ensuring column return entity: {}", valueToCheck));
+        }
+
+        // 返回查询到的实体类对象
+        return entity;
+    }
+
+    /**
+     * 确保指定列的有效性,并返回相应的实体对象列表
+     *      selectList方法
+     *      此方法用于验证数据库中某列的值是否存在,并返回包含该值的实体对象列表
+     *      如果指定的值不存在或为null,将抛出异常
+     *
+     * @param valueToCheck 需要验证的值
+     * @param columnExtractor 列值提取器,用于指定需要验证的列
+     * @param queryWrapperExtractor 查询包装器提取器,用于执行数据库查询
+     * @param errMsg 错误消息格式字符串
+     * @param columnExtractors 可变参数(varargs)查询字段,可选,如果没有就是查询全部
+     * @param <T> 实体类类型
+     * @param <R> 列值类型
+     * @param <V> 需要验证的值的类型
+     * @return 返回包含指定列值的实体对象列表
+     * @throws IllegalArgumentException 如果需要验证的值为null
+     * @throws FebsException 如果查询结果为空或列值为null,或查询过程中发生异常
+     */
+    public static <T, R, V> List<T> ensureColumnReturnEntityList(
+            V valueToCheck,
+            SFunction<T, R> columnExtractor,
+            SFunction<LambdaQueryWrapper<T>, List<T>> queryWrapperExtractor,
+            String errMsg,
+            SFunction<T, R>... columnExtractors) {
+        // 检查需要验证的值是否为null
+        if (valueToCheck == null) {
+            throw new IllegalArgumentException("The value to check cannot be null while ensureColumnReturnEntityList column");
+        }
+        List<T> entities = new ArrayList<>();
+        try {
+            // 创建LambdaQueryWrapper并配置查询条件
+            LambdaQueryWrapper<T> wrapper = Wrappers.lambdaQuery();
+            if (columnExtractors != null && columnExtractors.length > 0) {
+                wrapper.select(columnExtractors);
+            }
+            wrapper.eq(columnExtractor, valueToCheck);
+
+            // 执行查询并获取结果实体列表
+            entities = queryWrapperExtractor.apply(wrapper);
+        } catch (Exception e) {
+            // 记录异常日志
+            throw new FebsException(StrUtil.format("An error occurred while ensureColumnReturnEntityList column: {}", valueToCheck));
+        }
+        // 如果查询结果为空,则抛出异常
+        if (entities == null || entities.isEmpty()) {
+            throw new FebsException(StrUtil.format(errMsg, valueToCheck));
+        }
+
+        // 返回查询到的实体类对象列表
+        return entities;
+    }
+
+    /**
+     * 确保指定列的值在数据库中是存在的
+     *      selectOne方法
+     *      该方法通过查询数据库来验证给定的列值是否存在如果不存在,则抛出异常
+     *
+     * @param valueToCheck 需要验证的列值
+     * @param columnExtractor 用于提取实体类中列值的函数式接口
+     * @param queryWrapperExtractor 用于构建查询条件并返回实体类的函数式接口
+     * @param errMsg 当列值无效时抛出的异常消息格式字符串
+     * @throws IllegalArgumentException 如果需要验证的值为null
+     * @throws FebsException 如果在数据库中找不到指定的列值或列值为null,或者在查询过程中发生异常
+     */
+    public static <T, R, V> void ensureColumnValid(
+            V valueToCheck,
+            SFunction<T, R> columnExtractor,
+            SFunction<LambdaQueryWrapper<T>, T> queryWrapperExtractor,
+            String errMsg) {
+        // 检查需要验证的值是否为null
+        if (valueToCheck == null) {
+            throw new IllegalArgumentException("The value to check cannot be null while ensureColumnValid column");
+        }
+
+        try {
+            // 创建LambdaQueryWrapper并配置查询条件
+            LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<>();
+            wrapper.select(columnExtractor)
+                    .eq(columnExtractor, valueToCheck)
+                    .last("limit 1");
+
+            // 执行查询并获取结果实体
+            T entity = queryWrapperExtractor.apply(wrapper);
+            // 如果查询结果为空,则抛出异常
+            if (entity == null) {
+                throw new FebsException(StrUtil.format(errMsg, valueToCheck));
+            }
+
+            // 提取查询结果中的列值
+            R columnValue = columnExtractor.apply(entity);
+            // 如果列值为null,则抛出异常
+            if (columnValue == null) {
+                throw new FebsException(StrUtil.format(errMsg, valueToCheck));
+            }
+        } catch (Exception e) {
+            // 记录异常日志
+            throw new FebsException(e.getMessage());
+        }
+    }
+
+    /**
+     * 确保指定值在数据库中是唯一的
+     *      selectCount方法
+     *      该方法通过查询数据库来验证给定的列值是否已经存在,如果存在,则抛出异常,以确保数据的唯一性
+     *
+     * @param valueToCheck 需要检查的值
+     * @param columnExtractor 用于提取实体类字段的函数式接口
+     * @param countWrapperExtractor 用于获取查询条件包装器中记录数的函数式接口
+     * @param errMsg 错误消息模板,当值不唯一时使用
+     * @param <T> 实体类类型
+     * @param <R> 字段类型
+     * @param <V> 需要检查的值的类型
+     * @throws IllegalArgumentException 如果需要检查的值为null,则抛出此异常
+     * @throws FebsException 如果值已存在或在检查过程中发生错误,则抛出此异常
+     */
+    public static <T, R, V> void ensureUnique(
+            V valueToCheck,
+            SFunction<T, R> columnExtractor,
+            SFunction<LambdaQueryWrapper<T>, Integer> countWrapperExtractor,
+            String errMsg) {
+        // 检查输入值是否为null,如果为null,则抛出IllegalArgumentException异常
+        if (valueToCheck == null) {
+            throw new IllegalArgumentException("The value to check cannot be null while ensureUnique column");
+        }
+        if (columnExtractor == null) {
+            throw new IllegalArgumentException("The columnExtractor cannot be null while ensureUnique column");
+        }
+        if (countWrapperExtractor == null) {
+            throw new IllegalArgumentException("The countWrapperExtractor cannot be null while ensureUnique column");
+        }
+
+        int count = 0;
+        try {
+            // 创建LambdaQueryWrapper对象,用于构建查询条件
+            LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<>();
+            // 添加等于条件,检查的字段=需要检查的值
+            wrapper.eq(columnExtractor, valueToCheck);
+            // 执行查询并获取结果数量
+            count = countWrapperExtractor.apply(wrapper);
+
+        } catch (Exception e) {
+            // 记录异常日志
+            throw new FebsException(StrUtil.format("An error occurred while ensureUnique column: {}", valueToCheck));
+        }
+
+        // 如果结果数量大于0,说明值已存在,抛出FebsException异常
+        if (count > 0) {
+            throw new FebsException(StrUtil.format(errMsg, valueToCheck));
+        }
+    }
+
+
+    /**
+     * 确保两个参数相等,如果不相等则抛出异常
+     *
+     * @param value1 第一个参数
+     * @param value2 第二个参数
+     * @param errMsg 当两个参数不相等时抛出的异常消息格式字符串
+     * @throws FebsException 如果两个参数不相等
+     */
+    public static <T> void ensureEqual(
+            T value1,
+            T value2,
+            String errMsg) {
+        // 使用 Objects.equals 处理 null 值,避免显式 null 检查
+        if (!Objects.equals(value1, value2)) {
+            // 延迟字符串格式化,只在需要抛出异常时进行
+            throw new FebsException(StrUtil.format(errMsg, value1, value2));
+        }
+    }
+
+
+    /**
+     * 确保两个参数不相等,如果相等则抛出异常
+     *
+     * @param value1 第一个参数
+     * @param value2 第二个参数
+     * @param errMsg 当两个参数相等时抛出的异常消息格式字符串
+     * @throws FebsException 如果两个参数相等
+     */
+    public static <T> void ensureNotEqual(
+            T value1,
+            T value2,
+            String errMsg) {
+        // 使用 Objects.equals 处理 null 值,避免显式 null 检查
+        if (Objects.equals(value1, value2)) {
+            // 延迟字符串格式化,只在需要抛出异常时进行
+            throw new FebsException(StrUtil.format(errMsg, value1, value2));
+        }
+    }
+
+
+}
diff --git a/src/main/java/cc/mrbird/febs/user/contant/MemberConstant.java b/src/main/java/cc/mrbird/febs/user/contant/MemberConstant.java
new file mode 100644
index 0000000..b898d7d
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/user/contant/MemberConstant.java
@@ -0,0 +1,36 @@
+package cc.mrbird.febs.user.contant;
+
+public class MemberConstant {
+    //添加途径 1-正常注册 0-后台添加
+    public static final Integer ADD_TYPE_NORMAL = 1;
+    public static final Integer ADD_TYPE_BACK = 0;
+
+    //性别 0-女 1-男性 2-未填写
+    public static final Integer FEMALE = 0;
+    public static final Integer MALE = 1;
+    public static final Integer UNKNOWN = 2;
+
+
+
+    /**
+     * 账户状态;1-正常 0-禁用
+     */
+    public static final Integer ACCOUNT_STATUS_NORMAL = 1;
+    public static final Integer ACCOUNT_STATUS_TEST = 0;
+
+
+    /**
+     * 账户类型;1-正常用户 0-测试账户
+     */
+    public static final Integer ACCOUNT_TYPE_NORMAL = 1;
+    public static final Integer ACCOUNT_TYPE_TEST = 0;
+
+
+    /**
+     * 是否是主账号 1-是 0-否
+     */
+    public static final Integer DIRECTER_YES = 1;
+    public static final Integer DIRECTER_NO = 0;
+
+
+}
diff --git a/src/main/java/cc/mrbird/febs/user/entity/ChatMember.java b/src/main/java/cc/mrbird/febs/user/entity/ChatMember.java
index 7d38bba..9c15768 100644
--- a/src/main/java/cc/mrbird/febs/user/entity/ChatMember.java
+++ b/src/main/java/cc/mrbird/febs/user/entity/ChatMember.java
@@ -18,7 +18,6 @@
  `invite_id` varchar(8) DEFAULT NULL COMMENT '邀请码',
  `referrer_id` varchar(8) DEFAULT NULL COMMENT '推荐人邀请码',
  `referrer_ids` text COMMENT '推荐人线路',
- `level` varchar(255) DEFAULT NULL COMMENT '代理层级',
  `account_status` int DEFAULT '1' COMMENT '账户状态;1-正常 0-禁用',
  `account_type` int DEFAULT '0' COMMENT '账户类型;1-正常用户 0-测试账户',
  `avatar` varchar(500) DEFAULT NULL COMMENT '头像',
@@ -42,7 +41,6 @@
     private String inviteId;//邀请码
     private String referrerId;//推荐人邀请码
     private String referrerIds;//推荐人线路
-    private String level;//代理层级
     private Integer accountStatus;//账户状态;1-正常 0-禁用
     private Integer accountType;//账户类型;1-正常用户 0-测试账户
     private String avatar;//头像
diff --git a/src/main/java/cc/mrbird/febs/user/entity/ChatWallet.java b/src/main/java/cc/mrbird/febs/user/entity/ChatWallet.java
new file mode 100644
index 0000000..177acae
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/user/entity/ChatWallet.java
@@ -0,0 +1,23 @@
+package cc.mrbird.febs.user.entity;
+
+import cc.mrbird.febs.common.entity.BaseEntity;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+@TableName("chat_wallet")
+public class ChatWallet extends BaseEntity {
+
+    /**
+     * 用户ID
+     */
+    private Long memberId;
+
+    /**
+     * 余额
+     */
+    private BigDecimal balance;
+
+}
diff --git a/src/main/java/cc/mrbird/febs/user/mapper/ChatWalletMapper.java b/src/main/java/cc/mrbird/febs/user/mapper/ChatWalletMapper.java
new file mode 100644
index 0000000..7a4d8f0
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/user/mapper/ChatWalletMapper.java
@@ -0,0 +1,10 @@
+package cc.mrbird.febs.user.mapper;
+
+import cc.mrbird.febs.user.entity.ChatWallet;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+public interface ChatWalletMapper extends BaseMapper<ChatWallet> {
+
+    int updateBalanceWithVersion(ChatWallet wallet);
+
+}
diff --git a/src/main/java/cc/mrbird/febs/user/req/ApiRegisterDto.java b/src/main/java/cc/mrbird/febs/user/req/ApiRegisterDto.java
index 9aa774d..d4480b3 100644
--- a/src/main/java/cc/mrbird/febs/user/req/ApiRegisterDto.java
+++ b/src/main/java/cc/mrbird/febs/user/req/ApiRegisterDto.java
@@ -14,10 +14,6 @@
     @ApiModelProperty(value = "邮箱", example = "152@163.com")
     private String account;
 
-    @NotBlank(message = "类型不能为空!")
-    @ApiModelProperty(value = "类型 1-手机号 2-邮箱", example = "1")
-    private String type = "1";
-
     @NotBlank(message = "验证码不能为空")
     @ApiModelProperty(value = "验证码", example = "123456")
     private String code;
@@ -33,7 +29,6 @@
     @ApiModelProperty(value = "邀请码")
     private String inviteId;
 
-    @NotBlank(message = "注册类型不能为空")
-    @ApiModelProperty(value = "注册类型 1-正常注册 0-手动添加子账号", example = "1")
-    private Integer registerType;//注册类型 1-正常注册 0-手动添加子账号
+    @ApiModelProperty(hidden = true)
+    private Integer addType;//添加途径 1-正常注册 0-后台添加
 }
diff --git a/src/main/java/cc/mrbird/febs/user/service/AsyncService.java b/src/main/java/cc/mrbird/febs/user/service/AsyncService.java
new file mode 100644
index 0000000..f8156ef
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/user/service/AsyncService.java
@@ -0,0 +1,23 @@
+package cc.mrbird.febs.user.service;
+
+import cc.mrbird.febs.common.entity.FebsConstant;
+import cc.mrbird.febs.user.entity.ChatMember;
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.springframework.scheduling.annotation.Async;
+
+public interface AsyncService extends IService<ChatMember> {
+
+    /**
+     * 初始化钱包信息
+     * @param memberId
+     */
+    @Async(FebsConstant.ASYNC_POOL)
+    void initWallet(Long memberId);
+
+    /**
+     * 初始化推荐人信息
+     * @param memberId
+     */
+    @Async(FebsConstant.ASYNC_POOL)
+    void initMemberReferrerIds(Long memberId,String referrerId);
+}
diff --git a/src/main/java/cc/mrbird/febs/user/service/IChatWalletService.java b/src/main/java/cc/mrbird/febs/user/service/IChatWalletService.java
new file mode 100644
index 0000000..bc8340a
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/user/service/IChatWalletService.java
@@ -0,0 +1,13 @@
+package cc.mrbird.febs.user.service;
+
+import cc.mrbird.febs.user.entity.ChatWallet;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+import java.math.BigDecimal;
+
+public interface IChatWalletService extends IService<ChatWallet> {
+
+    void addBalance(BigDecimal amount, Long member);
+
+    void reduceBalance(BigDecimal amount, Long memberId);
+}
diff --git a/src/main/java/cc/mrbird/febs/user/service/Impl/AsyncServiceImpl.java b/src/main/java/cc/mrbird/febs/user/service/Impl/AsyncServiceImpl.java
new file mode 100644
index 0000000..b776de8
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/user/service/Impl/AsyncServiceImpl.java
@@ -0,0 +1,60 @@
+package cc.mrbird.febs.user.service.Impl;
+
+import cc.mrbird.febs.user.entity.ChatMember;
+import cc.mrbird.febs.user.entity.ChatWallet;
+import cc.mrbird.febs.user.mapper.ChatMemberMapper;
+import cc.mrbird.febs.user.mapper.ChatWalletMapper;
+import cc.mrbird.febs.user.service.AsyncService;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class AsyncServiceImpl extends ServiceImpl<ChatMemberMapper, ChatMember> implements AsyncService {
+
+    private final ChatWalletMapper chatWalletMapper;
+    @Override
+    public void initWallet(Long memberId) {
+        ChatWallet wallet = new ChatWallet();
+        wallet.setBalance(BigDecimal.ZERO);
+        wallet.setMemberId(memberId);
+        chatWalletMapper.insert(wallet);
+    }
+
+    @Override
+    public void initMemberReferrerIds(Long memberId, String parentId) {
+        boolean flag = false;
+        if (StrUtil.isBlank(parentId)) {
+            flag = true;
+        }
+        String ids = "";
+        while (!flag) {
+            if (StrUtil.isBlank(ids)) {
+                ids += parentId;
+            } else {
+                ids += ("," + parentId);
+            }
+            ChatMember chatMemberParent = this.baseMapper.selectOne(new LambdaQueryWrapper<ChatMember>().eq(ChatMember::getInviteId, parentId));
+            if (chatMemberParent == null) {
+                break;
+            }
+            parentId = chatMemberParent.getReferrerId();
+            if (StrUtil.isBlank(chatMemberParent.getReferrerId())) {
+                flag = true;
+            }
+        }
+
+        ChatMember chatMember = this.baseMapper.selectById(memberId);
+        if (StrUtil.isNotBlank(ids)) {
+            chatMember.setReferrerIds(ids);
+            this.baseMapper.updateById(chatMember);
+        }
+    }
+}
diff --git a/src/main/java/cc/mrbird/febs/user/service/Impl/ChatMemberServiceImpl.java b/src/main/java/cc/mrbird/febs/user/service/Impl/ChatMemberServiceImpl.java
index e7963ea..3f2896a 100644
--- a/src/main/java/cc/mrbird/febs/user/service/Impl/ChatMemberServiceImpl.java
+++ b/src/main/java/cc/mrbird/febs/user/service/Impl/ChatMemberServiceImpl.java
@@ -1,30 +1,24 @@
 package cc.mrbird.febs.user.service.Impl;
 
 import cc.mrbird.febs.common.entity.FebsResponse;
-import cc.mrbird.febs.common.enumerates.YesOrNoEnum;
 import cc.mrbird.febs.common.exception.FebsException;
-import cc.mrbird.febs.common.utils.AppContants;
 import cc.mrbird.febs.common.utils.ShareCodeUtil;
-import cc.mrbird.febs.mall.entity.MallMember;
-import cc.mrbird.febs.mall.entity.MallMemberWallet;
-import cc.mrbird.febs.mall.entity.RunVip;
+import cc.mrbird.febs.common.utils.ValidateEntityUtils;
+import cc.mrbird.febs.mall.service.impl.CommonService;
+import cc.mrbird.febs.user.contant.MemberConstant;
 import cc.mrbird.febs.user.entity.ChatMember;
 import cc.mrbird.febs.user.mapper.ChatMemberMapper;
 import cc.mrbird.febs.user.req.ApiRegisterDto;
+import cc.mrbird.febs.user.service.AsyncService;
 import cc.mrbird.febs.user.service.IChatMemberService;
-import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.crypto.SecureUtil;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
-
-import java.math.BigDecimal;
-import java.util.List;
 
 @Slf4j
 @Service
@@ -32,116 +26,58 @@
 @Transactional
 public class ChatMemberServiceImpl extends ServiceImpl<ChatMemberMapper, ChatMember> implements IChatMemberService {
 
+    private final CommonService commonService;
+    private final AsyncService asyncService;
+
     @Override
     public FebsResponse register(ApiRegisterDto apiRegisterDto) {
         String account = apiRegisterDto.getAccount();
-
-        MallMember mallMember = this.baseMapper.selectInfoByAccount(account);
-        if (mallMember != null) {
-            throw new FebsException("手机号已注册");
-        }
-        /**
-         * 验证两次密码是否一致
-         */
-        String password = registerDto.getPassword();
-        String passwordAgain = registerDto.getPasswordAgain();
-        if(!password.equals(passwordAgain)){
-            throw new FebsException("密码不一致");
-        }
-
-        //邀请码为admin的时候(后台添加用户),不需要验证验证码
-        if (!"admin".equals(registerDto.getRegistType())) {
-            String code = registerDto.getCode();
+        String password = apiRegisterDto.getPassword();
+        String passwordAgain = apiRegisterDto.getPasswordAgain();
+        String refInviteId = apiRegisterDto.getInviteId();
+        String code = apiRegisterDto.getCode();
+        //后台添加用户,不需要验证验证码
+        Integer addType = apiRegisterDto.getAddType();
+        if (MemberConstant.ADD_TYPE_NORMAL == addType) {
             boolean flags = commonService.verifyCode(account, code);
             if (!flags) {
                 throw new FebsException("验证码错误");
             }
         }
+        ValidateEntityUtils.ensureEqual(password, passwordAgain, "两次密码不一致");
+        ValidateEntityUtils.ensureUnique(account, ChatMember::getEmail, this.baseMapper::selectCount, "邮箱已使用");
 
-        mallMember = new MallMember();
-        mallMember.setPassword(SecureUtil.md5(registerDto.getPassword()));
+        ChatMember chatMember = new ChatMember();
+        chatMember.setEmail(account);
+        chatMember.setPassword(SecureUtil.md5(password));
+        chatMember.setName(account);
+        chatMember.setSex(MemberConstant.UNKNOWN);
 
-        // 判断账号类型
-        if (AppContants.ACCOUNT_TYPE_MOBILE.equals(registerDto.getType())) {
-            mallMember.setPhone(registerDto.getAccount());
-        } else {
-            mallMember.setEmail(registerDto.getAccount());
-        }
         //对于邀请码的验证和上级联系人的验证
         Integer count = this.baseMapper.selectCount(null);
         if (count != null && count != 0) {
-            if(StrUtil.isEmpty(registerDto.getInviteId())){
-                throw new FebsException("请输入邀请码");
-            }
-            String inviteId = registerDto.getInviteId();
-            MallMember inviteMember = this.baseMapper.selectInfoByInviteId(inviteId);
-            if (inviteMember == null) {
-                throw new FebsException("邀请码不存在");
-            }
-            mallMember.setReferrerId(registerDto.getInviteId());
-//            if(!StrUtil.isEmpty(registerDto.getInviteId())){
-//                String inviteId = registerDto.getInviteId();
-//                MallMember inviteMember = this.baseMapper.selectInfoByInviteId(inviteId);
-//                if (inviteMember == null) {
-//                    throw new FebsException("邀请码不存在");
-//                }
-//                mallMember.setReferrerId(registerDto.getInviteId());
-//            }
-        }
-        mallMember.setName(account);
-        mallMember.setAvatar("https://res.runstep.cc/rslogo.png");
-        mallMember.setAccountStatus(YesOrNoEnum.YES.getValue());
-        mallMember.setAccountType(MallMember.ACCOUNT_TYPE_NORMAL);
-        mallMember.setSex("男");
-        mallMember.setDirector(YesOrNoEnum.NO.getValue());
-        mallMember.setDirectorTime(DateUtil.date());
-        mallMember.setBindPhone(account);
-
-        this.baseMapper.insert(mallMember);
-
-        String inviteId = ShareCodeUtil.toSerialCode(mallMember.getId());
-        mallMember.setInviteId(inviteId);
-
-        //推荐人和推荐人链
-        boolean flag = false;
-        String parentId = mallMember.getReferrerId();
-        if (StrUtil.isBlank(parentId)) {
-            flag = true;
-        }
-        String ids = "";
-        while (!flag) {
-            if (StrUtil.isBlank(ids)) {
-                ids += parentId;
-            } else {
-                ids += ("," + parentId);
-            }
-            MallMember parentMember = this.baseMapper.selectInfoByInviteId(parentId);
-            if (parentMember == null) {
-                break;
-            }
-            parentId = parentMember.getReferrerId();
-            if (StrUtil.isBlank(parentMember.getReferrerId())) {
-                flag = true;
+            if(StrUtil.isNotEmpty(refInviteId)){
+                ChatMember chatMemberInvite = ValidateEntityUtils.ensureColumnReturnEntity(refInviteId, ChatMember::getInviteId, this.baseMapper::selectOne, "邀请码不存在");
+                chatMember.setReferrerId(chatMemberInvite.getInviteId());
             }
         }
+        chatMember.setAccountStatus(MemberConstant.ACCOUNT_STATUS_NORMAL);
+        chatMember.setAccountType(MemberConstant.ACCOUNT_TYPE_NORMAL);
+        // --todo 默认头像为空
+//        chatMember.setAvatar("https://res.runstep.cc/rslogo.png");
+        chatMember.setDirector(MemberConstant.DIRECTER_YES);
+        chatMember.setLastLoginTime(DateUtil.date());
+        // -- todo 默认主账号的角色设置没有设置
+        chatMember.setRoleId(0L);
+        this.baseMapper.insert(chatMember);
 
-        if (StrUtil.isNotBlank(ids)) {
-            mallMember.setReferrerIds(ids);
-        }
-        //会员VIP等级
-        List<RunVip> runVips = runVipMapper.selectList(new LambdaQueryWrapper<RunVip>().orderByAsc(RunVip::getOrderNumber));
-        if (StrUtil.isBlank(mallMember.getLevel()) && CollUtil.isNotEmpty(runVips)) {
-            RunVip runVip = runVips.get(0);
-            mallMember.setLevel(runVip.getVipCode());
-        }
-        this.baseMapper.updateById(mallMember);
+        //公司ID默认为主账号的会员ID
+        chatMember.setCompanyId(chatMember.getId());
+        chatMember.setInviteId(ShareCodeUtil.toSerialCode(chatMember.getId()));
+        //推荐人链
+        asyncService.initMemberReferrerIds(chatMember.getId(),chatMember.getReferrerId());
+        asyncService.initWallet(chatMember.getId());
 
-        MallMemberWallet wallet = new MallMemberWallet();
-        wallet.setBalance(BigDecimal.ZERO);
-        wallet.setMemberId(mallMember.getId());
-        mallMemberWalletMapper.insert(wallet);
-
-        agentProducer.sendNodeUpMsg(mallMember.getId());
         return new FebsResponse().success().message("注册成功");
     }
 }
diff --git a/src/main/java/cc/mrbird/febs/user/service/Impl/ChatWalletServiceImpl.java b/src/main/java/cc/mrbird/febs/user/service/Impl/ChatWalletServiceImpl.java
new file mode 100644
index 0000000..788caa7
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/user/service/Impl/ChatWalletServiceImpl.java
@@ -0,0 +1,69 @@
+package cc.mrbird.febs.user.service.Impl;
+
+import cc.mrbird.febs.common.exception.FebsException;
+import cc.mrbird.febs.common.utils.ValidateEntityUtils;
+import cc.mrbird.febs.user.entity.ChatWallet;
+import cc.mrbird.febs.user.mapper.ChatWalletMapper;
+import cc.mrbird.febs.user.service.IChatWalletService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ChatWalletServiceImpl extends ServiceImpl<ChatWalletMapper, ChatWallet> implements IChatWalletService {
+
+    @Override
+    public void addBalance(BigDecimal amount, Long memberId) {
+        if (BigDecimal.ZERO.compareTo(amount) > 0) {
+            throw new FebsException("增加余额异常");
+        }
+        int i = 0;
+        boolean flag = true;
+
+        while (flag) {
+            i++;
+            ChatWallet wallet = ValidateEntityUtils.ensureColumnReturnEntity(memberId, ChatWallet::getMemberId, this.baseMapper::selectOne, "钱包不存在");
+            wallet.setBalance(wallet.getBalance().add(amount));
+            int result = this.baseMapper.updateBalanceWithVersion(wallet);
+            if (result > 0) {
+                flag = false;
+            } else {
+                if (i > 2) {
+                    throw new FebsException("操作失败");
+                }
+            }
+        }
+    }
+
+    @Override
+    public void reduceBalance(BigDecimal amount, Long memberId) {
+        int i = 0;
+        boolean flag = true;
+
+        while (flag) {
+            i++;
+            ChatWallet wallet = ValidateEntityUtils.ensureColumnReturnEntity(memberId, ChatWallet::getMemberId, this.baseMapper::selectOne, "钱包不存在");
+            if (amount.compareTo(wallet.getBalance()) > 0) {
+                throw new FebsException("余额不足");
+            }
+
+            wallet.setBalance(wallet.getBalance().subtract(amount));
+
+            int result = this.baseMapper.updateBalanceWithVersion(wallet);
+            if (result > 0) {
+                flag = false;
+            } else {
+                if (i > 2) {
+                    throw new FebsException("操作失败");
+                }
+            }
+        }
+    }
+
+
+}
diff --git a/src/main/resources/mapper/modules/ChatWalletMapper.xml b/src/main/resources/mapper/modules/ChatWalletMapper.xml
new file mode 100644
index 0000000..47b09df
--- /dev/null
+++ b/src/main/resources/mapper/modules/ChatWalletMapper.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="cc.mrbird.febs.user.mapper.ChatWalletMapper">
+
+    <update id="updateBalanceWithVersion">
+        update chat_wallet
+        set revision = revision + 1,
+            balance = #{record.balance}
+        where id=#{record.id} and revision=#{record.revision}
+    </update>
+
+</mapper>
\ No newline at end of file

--
Gitblit v1.9.1