KKSU
2025-02-17 1e6f177e264b130cb69e79e4325156989629ec85
feat(user): 新增客服用户注册功能

- 添加用户注册相关的数据传输对象 ApiRegisterDto
- 创建 ChatMember 实体类用于表示客服用户
- 实现用户注册的业务逻辑,包括验证邀请码、生成用户信息等
- 添加用户注册的 API 接口和控制器
- 更新登录拦截器和登录
7 files added
3 files modified
322 ■■■■■ changed files
src/main/java/cc/mrbird/febs/common/interceptor/LoginInterceptor.java 13 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/common/utils/LoginUserUtil.java 6 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/common/utils/SubMailSend.java 2 ●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/user/controller/ChatUserController.java 33 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/user/conversion/ChatMemberConversion.java 9 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/user/entity/ChatMember.java 58 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/user/mapper/ChatMemberMapper.java 7 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/user/req/ApiRegisterDto.java 39 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/user/service/IChatMemberService.java 8 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/user/service/Impl/ChatMemberServiceImpl.java 147 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/common/interceptor/LoginInterceptor.java
@@ -4,7 +4,7 @@
import cc.mrbird.febs.common.utils.AppContants;
import cc.mrbird.febs.common.utils.RedisUtils;
import cc.mrbird.febs.common.utils.SpringContextUtil;
import cc.mrbird.febs.mall.entity.MallMember;
import cc.mrbird.febs.user.entity.ChatMember;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
@@ -20,10 +20,6 @@
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * @author xxx
 * @date 2020-08-24
 **/
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@@ -51,10 +47,9 @@
            responseUnAuth(response);
            return false;
        }
        MallMember member = JSON.parseObject(userJsonStr, MallMember.class);
        request.getSession().setAttribute("member", member);
//        log.info("登录用户token:"+token+",PHONE:"+member.getPhone());
        ChatMember chatMember = JSON.parseObject(userJsonStr, ChatMember.class);
        request.getSession().setAttribute("chatMember", chatMember);
        log.info("登录用户{},{}",chatMember.getEmail(),token);
        return true;
    }
src/main/java/cc/mrbird/febs/common/utils/LoginUserUtil.java
@@ -1,6 +1,7 @@
package cc.mrbird.febs.common.utils;
import cc.mrbird.febs.mall.entity.MallMember;
import cc.mrbird.febs.user.entity.ChatMember;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@@ -16,4 +17,9 @@
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        return (MallMember) request.getSession().getAttribute("member");
    }
    public static ChatMember getLoginChatMember() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        return (ChatMember) request.getSession().getAttribute("chatMember");
    }
}
src/main/java/cc/mrbird/febs/common/utils/SubMailSend.java
@@ -87,8 +87,8 @@
    private static boolean request(JSONObject vars, String project, String to) {
        TreeMap<String, Object> requestData = new TreeMap<String, Object>();
        requestData.put("appid", APP_ID);
        requestData.put("project", project);
        requestData.put("to", to);
        requestData.put("project", project);
        requestData.put("from", FROM);
        if (!vars.isEmpty()) {
            requestData.put("vars", vars.toString());
src/main/java/cc/mrbird/febs/user/controller/ChatUserController.java
New file
@@ -0,0 +1,33 @@
package cc.mrbird.febs.user.controller;
import cc.mrbird.febs.common.entity.FebsResponse;
import cc.mrbird.febs.user.req.ApiRegisterDto;
import cc.mrbird.febs.user.service.IChatMemberService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@RequestMapping(value = "/api/chatUser")
@RequiredArgsConstructor
@CrossOrigin("*")
@Api(value = "ChatUserController", tags = "客服用户")
public class ChatUserController {
    private final IChatMemberService chatMemberService;
    /**
     * 主账号注册 主账号添加子账号
     * @param apiRegisterDto
     * @return
     */
    @ApiOperation(value = "注册", notes = "注册")
    @PostMapping(value = "/register")
    public FebsResponse register(@RequestBody ApiRegisterDto apiRegisterDto) {
        return chatMemberService.register(apiRegisterDto);
    }
}
src/main/java/cc/mrbird/febs/user/conversion/ChatMemberConversion.java
New file
@@ -0,0 +1,9 @@
package cc.mrbird.febs.user.conversion;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public abstract class ChatMemberConversion {
    public static final ChatMemberConversion INSTANCE = Mappers.getMapper(ChatMemberConversion.class);
}
src/main/java/cc/mrbird/febs/user/entity/ChatMember.java
New file
@@ -0,0 +1,58 @@
package cc.mrbird.febs.user.entity;
import cc.mrbird.febs.common.entity.BaseEntity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
/**
 *
 `phone` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '联系手机号码',
 `email` varchar(90) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '注册邮箱',
 `password` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '登录密码',
 `trade_password` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '支付密码',
 `name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '昵称',
 `sex` int DEFAULT '0' COMMENT '性别 0-女 1-男性 2-未填写',
 `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 '头像',
 `director` int DEFAULT '2' COMMENT '是否是主账号 1-是 0-否',
 `director_time` datetime DEFAULT NULL COMMENT '购买套餐的时间',
 `director_fail_time` datetime DEFAULT NULL COMMENT '套餐到期时间',
 `last_login_time` datetime DEFAULT NULL COMMENT '上一次登录时间',
 `company_id` bigint DEFAULT NULL COMMENT '公司ID',
 `role_id` bigint DEFAULT NULL COMMENT '角色ID',
 */
@Data
@TableName("chat_member")
public class ChatMember extends BaseEntity {
    private String phone;//联系手机号码
    private String email;//注册邮箱
    private String password;//登录密码
    private String tradePassword;//支付密码
    private String name;//昵称
    private Integer sex;//性别 0-女 1-男性 2-未填写
    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;//头像
    private Integer director;//是否是主账号 1-是 0-否
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    private Date directorTime;//购买套餐的时间
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    private Date directorFailTime;//套餐到期时间
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    private Date lastLoginTime;//上一次登录时间
    private Long companyId;//公司ID
    private Long roleId;//角色ID
}
src/main/java/cc/mrbird/febs/user/mapper/ChatMemberMapper.java
New file
@@ -0,0 +1,7 @@
package cc.mrbird.febs.user.mapper;
import cc.mrbird.febs.user.entity.ChatMember;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface ChatMemberMapper extends BaseMapper<ChatMember> {
}
src/main/java/cc/mrbird/febs/user/req/ApiRegisterDto.java
New file
@@ -0,0 +1,39 @@
package cc.mrbird.febs.user.req;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
@ApiModel(value = "ApiRegisterDto", description = "用户注册参数类")
public class ApiRegisterDto {
    @NotBlank(message = "邮箱不能为空!")
    @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;
    @NotBlank(message = "密码不能为空")
    @ApiModelProperty(value = "密码", example = "123456")
    private String password;
    @NotBlank(message = "密码不能为空")
    @ApiModelProperty(value = "重复密码", example = "123456")
    private String passwordAgain;
    @ApiModelProperty(value = "邀请码")
    private String inviteId;
    @NotBlank(message = "注册类型不能为空")
    @ApiModelProperty(value = "注册类型 1-正常注册 0-手动添加子账号", example = "1")
    private Integer registerType;//注册类型 1-正常注册 0-手动添加子账号
}
src/main/java/cc/mrbird/febs/user/service/IChatMemberService.java
New file
@@ -0,0 +1,8 @@
package cc.mrbird.febs.user.service;
import cc.mrbird.febs.common.entity.FebsResponse;
import cc.mrbird.febs.user.req.ApiRegisterDto;
public interface IChatMemberService {
    FebsResponse register(ApiRegisterDto apiRegisterDto);
}
src/main/java/cc/mrbird/febs/user/service/Impl/ChatMemberServiceImpl.java
New file
@@ -0,0 +1,147 @@
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.user.entity.ChatMember;
import cc.mrbird.febs.user.mapper.ChatMemberMapper;
import cc.mrbird.febs.user.req.ApiRegisterDto;
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
@RequiredArgsConstructor
@Transactional
public class ChatMemberServiceImpl extends ServiceImpl<ChatMemberMapper, ChatMember> implements IChatMemberService {
    @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();
            boolean flags = commonService.verifyCode(account, code);
            if (!flags) {
                throw new FebsException("验证码错误");
            }
        }
        mallMember = new MallMember();
        mallMember.setPassword(SecureUtil.md5(registerDto.getPassword()));
        // 判断账号类型
        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.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);
        MallMemberWallet wallet = new MallMemberWallet();
        wallet.setBalance(BigDecimal.ZERO);
        wallet.setMemberId(mallMember.getId());
        mallMemberWalletMapper.insert(wallet);
        agentProducer.sendNodeUpMsg(mallMember.getId());
        return new FebsResponse().success().message("注册成功");
    }
}