feat(invite): 新增用户邀请码功能
新增实体、Mapper、Service及Controller接口,支持注册时自动生成与用户查询个人邀请码
This commit is contained in:
@@ -8,19 +8,19 @@ import com.yolo.keyborad.model.dto.AppleLoginReq;
|
||||
import com.yolo.keyborad.model.dto.user.*;
|
||||
import com.yolo.keyborad.model.entity.KeyboardFeedback;
|
||||
import com.yolo.keyborad.model.entity.KeyboardUser;
|
||||
import com.yolo.keyborad.model.entity.KeyboardUserInviteCodes;
|
||||
import com.yolo.keyborad.model.vo.user.InviteCodeRespVO;
|
||||
import com.yolo.keyborad.model.vo.user.KeyboardUserInfoRespVO;
|
||||
import com.yolo.keyborad.model.vo.user.KeyboardUserRespVO;
|
||||
import com.yolo.keyborad.service.IAppleService;
|
||||
import com.yolo.keyborad.service.KeyboardUserInviteCodesService;
|
||||
import com.yolo.keyborad.service.UserService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
@@ -46,6 +46,9 @@ public class UserController {
|
||||
@Resource
|
||||
private com.yolo.keyborad.service.KeyboardFeedbackService feedbackService;
|
||||
|
||||
@Resource
|
||||
private KeyboardUserInviteCodesService inviteCodesService;
|
||||
|
||||
/**
|
||||
* 苹果登录
|
||||
*
|
||||
@@ -130,5 +133,13 @@ public class UserController {
|
||||
return ResultUtils.success(feedbackService.save(feedback));
|
||||
}
|
||||
|
||||
@GetMapping("/inviteCode")
|
||||
@Operation(summary = "查询邀请码", description = "查询用户自己的邀请码")
|
||||
public BaseResponse<InviteCodeRespVO> getInviteCode() {
|
||||
long userId = StpUtil.getLoginIdAsLong();
|
||||
KeyboardUserInviteCodes inviteCode = inviteCodesService.getUserInviteCode(userId);
|
||||
InviteCodeRespVO respVO = BeanUtil.copyProperties(inviteCode, InviteCodeRespVO.class);
|
||||
return ResultUtils.success(respVO);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.yolo.keyborad.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.yolo.keyborad.model.entity.KeyboardUserInviteCodes;
|
||||
|
||||
/*
|
||||
* @author: ziin
|
||||
* @date: 2025/12/18 16:26
|
||||
*/
|
||||
|
||||
public interface KeyboardUserInviteCodesMapper extends BaseMapper<KeyboardUserInviteCodes> {
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.yolo.keyborad.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.util.Date;
|
||||
import lombok.Data;
|
||||
|
||||
/*
|
||||
* @author: ziin
|
||||
* @date: 2025/12/18 16:26
|
||||
*/
|
||||
|
||||
/**
|
||||
* 用户生成的邀请码表,用于邀请新用户注册/安装并建立邀请关系
|
||||
*/
|
||||
@Schema(description="用户生成的邀请码表,用于邀请新用户注册/安装并建立邀请关系")
|
||||
@Data
|
||||
@TableName(value = "keyboard_user_invite_codes")
|
||||
public class KeyboardUserInviteCodes {
|
||||
/**
|
||||
* 邀请码主键ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
@Schema(description="邀请码主键ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 邀请码字符串,对外展示,唯一
|
||||
*/
|
||||
@TableField(value = "code")
|
||||
@Schema(description="邀请码字符串,对外展示,唯一")
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 邀请码所属用户ID(邀请人)
|
||||
*/
|
||||
@TableField(value = "owner_user_id")
|
||||
@Schema(description="邀请码所属用户ID(邀请人)")
|
||||
private Long ownerUserId;
|
||||
|
||||
/**
|
||||
* 邀请码状态:1=启用,0=停用
|
||||
*/
|
||||
@TableField(value = "\"status\"")
|
||||
@Schema(description="邀请码状态:1=启用,0=停用")
|
||||
private Short status;
|
||||
|
||||
/**
|
||||
* 邀请码创建时间
|
||||
*/
|
||||
@TableField(value = "created_at")
|
||||
@Schema(description="邀请码创建时间")
|
||||
private Date createdAt;
|
||||
|
||||
/**
|
||||
* 邀请码过期时间,NULL表示永久有效
|
||||
*/
|
||||
@TableField(value = "expires_at")
|
||||
@Schema(description="邀请码过期时间,NULL表示永久有效")
|
||||
private Date expiresAt;
|
||||
|
||||
/**
|
||||
* 邀请码最大可使用次数,NULL表示不限次数
|
||||
*/
|
||||
@TableField(value = "max_uses")
|
||||
@Schema(description="邀请码最大可使用次数,NULL表示不限次数")
|
||||
private Integer maxUses;
|
||||
|
||||
/**
|
||||
* 邀请码已使用次数
|
||||
*/
|
||||
@TableField(value = "used_count")
|
||||
@Schema(description="邀请码已使用次数")
|
||||
private Integer usedCount;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.yolo.keyborad.model.vo.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 邀请码响应VO
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "邀请码信息")
|
||||
public class InviteCodeRespVO {
|
||||
|
||||
@Schema(description = "邀请码")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "邀请码状态:1=启用,0=停用")
|
||||
private Short status;
|
||||
|
||||
@Schema(description = "已使用次数")
|
||||
private Integer usedCount;
|
||||
|
||||
@Schema(description = "最大可使用次数")
|
||||
private Integer maxUses;
|
||||
|
||||
@Schema(description = "过期时间")
|
||||
private Date expiresAt;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.yolo.keyborad.service;
|
||||
|
||||
import com.yolo.keyborad.model.entity.KeyboardUserInviteCodes;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
/*
|
||||
* @author: ziin
|
||||
* @date: 2025/12/18 16:26
|
||||
*/
|
||||
|
||||
public interface KeyboardUserInviteCodesService extends IService<KeyboardUserInviteCodes>{
|
||||
|
||||
/**
|
||||
* 获取用户的邀请码
|
||||
* @param userId 用户ID
|
||||
* @return 邀请码实体
|
||||
*/
|
||||
KeyboardUserInviteCodes getUserInviteCode(Long userId);
|
||||
|
||||
/**
|
||||
* 为用户创建邀请码
|
||||
* @param userId 用户ID
|
||||
* @return 创建的邀请码实体
|
||||
*/
|
||||
KeyboardUserInviteCodes createInviteCode(Long userId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.yolo.keyborad.service.impl;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.yolo.keyborad.mapper.KeyboardUserInviteCodesMapper;
|
||||
import com.yolo.keyborad.model.entity.KeyboardUserInviteCodes;
|
||||
import com.yolo.keyborad.service.KeyboardUserInviteCodesService;
|
||||
/*
|
||||
* @author: ziin
|
||||
* @date: 2025/12/18 16:26
|
||||
*/
|
||||
|
||||
@Service
|
||||
public class KeyboardUserInviteCodesServiceImpl extends ServiceImpl<KeyboardUserInviteCodesMapper, KeyboardUserInviteCodes> implements KeyboardUserInviteCodesService{
|
||||
|
||||
@Override
|
||||
public KeyboardUserInviteCodes getUserInviteCode(Long userId) {
|
||||
QueryWrapper<KeyboardUserInviteCodes> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("owner_user_id", userId);
|
||||
return this.getOne(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyboardUserInviteCodes createInviteCode(Long userId) {
|
||||
// 生成唯一的邀请码
|
||||
String code;
|
||||
int maxRetries = 10;
|
||||
int retryCount = 0;
|
||||
|
||||
do {
|
||||
// 生成8位字母数字组合的邀请码
|
||||
code = RandomUtil.randomString(8).toUpperCase();
|
||||
|
||||
// 检查邀请码是否已存在
|
||||
QueryWrapper<KeyboardUserInviteCodes> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("code", code);
|
||||
KeyboardUserInviteCodes existingCode = this.getOne(queryWrapper);
|
||||
|
||||
if (existingCode == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
retryCount++;
|
||||
} while (retryCount < maxRetries);
|
||||
|
||||
// 创建邀请码实体
|
||||
KeyboardUserInviteCodes inviteCode = new KeyboardUserInviteCodes();
|
||||
inviteCode.setCode(code);
|
||||
inviteCode.setOwnerUserId(userId);
|
||||
inviteCode.setStatus((short) 1); // 启用状态
|
||||
inviteCode.setCreatedAt(new Date());
|
||||
inviteCode.setExpiresAt(null); // 永久有效
|
||||
inviteCode.setMaxUses(null); // 不限次数
|
||||
inviteCode.setUsedCount(0); // 初始使用次数为0
|
||||
|
||||
// 保存到数据库
|
||||
this.save(inviteCode);
|
||||
|
||||
return inviteCode;
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,9 @@ public class UserServiceImpl extends ServiceImpl<KeyboardUserMapper, KeyboardUse
|
||||
@Resource
|
||||
private KeyboardUserQuotaTotalService quotaTotalService;
|
||||
|
||||
@Resource
|
||||
private KeyboardUserInviteCodesService inviteCodesService;
|
||||
|
||||
private final NacosAppConfigCenter.DynamicAppConfig cfgHolder;
|
||||
|
||||
public UserServiceImpl(NacosAppConfigCenter.DynamicAppConfig cfgHolder) {
|
||||
@@ -107,6 +110,9 @@ public class UserServiceImpl extends ServiceImpl<KeyboardUserMapper, KeyboardUse
|
||||
quotaTotal.setUpdatedAt(new Date());
|
||||
quotaTotalService.save(quotaTotal);
|
||||
|
||||
// 初始化用户邀请码
|
||||
inviteCodesService.createInviteCode(keyboardUser.getId());
|
||||
|
||||
log.info("User registered with Apple Sign-In, userId={}, freeQuota={}",
|
||||
keyboardUser.getId(), appConfig.getUserRegisterProperties().getFreeTrialQuota());
|
||||
|
||||
@@ -246,6 +252,9 @@ public class UserServiceImpl extends ServiceImpl<KeyboardUserMapper, KeyboardUse
|
||||
quotaTotal.setUpdatedAt(new Date());
|
||||
quotaTotalService.save(quotaTotal);
|
||||
|
||||
// 初始化用户邀请码
|
||||
inviteCodesService.createInviteCode(keyboardUser.getId());
|
||||
|
||||
log.info("User registered with email, userId={}, email={}, freeQuota={}",
|
||||
keyboardUser.getId(), keyboardUser.getEmail(), appConfig.getUserRegisterProperties().getFreeTrialQuota());
|
||||
}
|
||||
|
||||
20
src/main/resources/mapper/KeyboardUserInviteCodesMapper.xml
Normal file
20
src/main/resources/mapper/KeyboardUserInviteCodesMapper.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?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="com.yolo.keyborad.mapper.KeyboardUserInviteCodesMapper">
|
||||
<resultMap id="BaseResultMap" type="com.yolo.keyborad.model.entity.KeyboardUserInviteCodes">
|
||||
<!--@mbg.generated-->
|
||||
<!--@Table keyboard_user_invite_codes-->
|
||||
<id column="id" jdbcType="BIGINT" property="id" />
|
||||
<result column="code" jdbcType="VARCHAR" property="code" />
|
||||
<result column="owner_user_id" jdbcType="BIGINT" property="ownerUserId" />
|
||||
<result column="status" jdbcType="SMALLINT" property="status" />
|
||||
<result column="created_at" jdbcType="TIMESTAMP" property="createdAt" />
|
||||
<result column="expires_at" jdbcType="TIMESTAMP" property="expiresAt" />
|
||||
<result column="max_uses" jdbcType="INTEGER" property="maxUses" />
|
||||
<result column="used_count" jdbcType="INTEGER" property="usedCount" />
|
||||
</resultMap>
|
||||
<sql id="Base_Column_List">
|
||||
<!--@mbg.generated-->
|
||||
id, code, owner_user_id, "status", created_at, expires_at, max_uses, used_count
|
||||
</sql>
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user