diff --git a/lover/__pycache__/models.cpython-314.pyc b/lover/__pycache__/models.cpython-314.pyc index 712cc9c..9144147 100644 Binary files a/lover/__pycache__/models.cpython-314.pyc and b/lover/__pycache__/models.cpython-314.pyc differ diff --git a/lover/migrations/add_invite_code_fields.sql b/lover/migrations/add_invite_code_fields.sql new file mode 100644 index 0000000..cb94fcb --- /dev/null +++ b/lover/migrations/add_invite_code_fields.sql @@ -0,0 +1,18 @@ +-- 添加邀请码相关字段 +ALTER TABLE nf_user +ADD COLUMN invite_code VARCHAR(10) UNIQUE COMMENT '我的邀请码', +ADD COLUMN invited_by VARCHAR(10) COMMENT '被谁邀请(邀请码)', +ADD COLUMN invite_count INT DEFAULT 0 COMMENT '邀请人数', +ADD COLUMN invite_reward_total DECIMAL(10,2) DEFAULT 0.00 COMMENT '邀请奖励总额'; + +-- 为已有用户生成邀请码(6位随机字符) +UPDATE nf_user +SET invite_code = CONCAT( + SUBSTRING('ABCDEFGHJKLMNPQRSTUVWXYZ23456789', FLOOR(1 + RAND() * 32), 1), + SUBSTRING('ABCDEFGHJKLMNPQRSTUVWXYZ23456789', FLOOR(1 + RAND() * 32), 1), + SUBSTRING('ABCDEFGHJKLMNPQRSTUVWXYZ23456789', FLOOR(1 + RAND() * 32), 1), + SUBSTRING('ABCDEFGHJKLMNPQRSTUVWXYZ23456789', FLOOR(1 + RAND() * 32), 1), + SUBSTRING('ABCDEFGHJKLMNPQRSTUVWXYZ23456789', FLOOR(1 + RAND() * 32), 1), + SUBSTRING('ABCDEFGHJKLMNPQRSTUVWXYZ23456789', FLOOR(1 + RAND() * 32), 1) +) +WHERE invite_code IS NULL; diff --git a/lover/models.py b/lover/models.py index e7b0111..28b1f06 100644 --- a/lover/models.py +++ b/lover/models.py @@ -31,6 +31,11 @@ class User(Base): owned_outfit_ids = Column(JSON) owned_voice_ids = Column(JSON) vip_endtime = Column(BigInteger, default=0) + # 邀请码相关 + invite_code = Column(String(10), unique=True) + invited_by = Column(String(10)) + invite_count = Column(Integer, default=0) + invite_reward_total = Column(DECIMAL(10, 2), default=0.00) class VoiceLibrary(Base): diff --git a/lover/routers/__pycache__/config.cpython-314.pyc b/lover/routers/__pycache__/config.cpython-314.pyc index 69aea36..e494acc 100644 Binary files a/lover/routers/__pycache__/config.cpython-314.pyc and b/lover/routers/__pycache__/config.cpython-314.pyc differ diff --git a/lover/routers/config.py b/lover/routers/config.py index 1382e25..7e568f7 100644 --- a/lover/routers/config.py +++ b/lover/routers/config.py @@ -492,3 +492,144 @@ def save_cloned_voice( except Exception as e: db.rollback() raise HTTPException(status_code=500, detail=f"保存失败: {str(e)}") + + + +# ===== 邀请码相关 ===== + +class InviteInfoResponse(BaseModel): + invite_code: str + invite_count: int + invite_reward_total: float + invite_url: str + + model_config = ConfigDict(from_attributes=True) + + +class InviteRewardRequest(BaseModel): + invite_code: str + + model_config = ConfigDict(from_attributes=True) + + +def _generate_invite_code() -> str: + """生成6位邀请码(不含易混淆字符)""" + import random + import string + chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789' # 去掉 0O1I 等易混淆字符 + return ''.join(random.choice(chars) for _ in range(6)) + + +@router.get("/invite/info", response_model=ApiResponse[InviteInfoResponse]) +def get_invite_info( + db: Session = Depends(get_db), + user: AuthedUser = Depends(get_current_user), +): + """ + 获取用户的邀请信息 + """ + user_row = db.query(User).filter(User.id == user.id).first() + if not user_row: + raise HTTPException(status_code=404, detail="用户不存在") + + # 如果没有邀请码,生成一个 + if not user_row.invite_code: + while True: + code = _generate_invite_code() + # 检查是否重复 + existing = db.query(User).filter(User.invite_code == code).first() + if not existing: + user_row.invite_code = code + db.add(user_row) + db.flush() + break + + # 生成邀请链接(这里使用简单的格式,实际可以是 H5 页面链接) + invite_url = f"https://your-domain.com/register?invite={user_row.invite_code}" + + return success_response( + InviteInfoResponse( + invite_code=user_row.invite_code, + invite_count=user_row.invite_count or 0, + invite_reward_total=float(user_row.invite_reward_total or 0), + invite_url=invite_url + ) + ) + + +@router.post("/invite/apply", response_model=ApiResponse[dict]) +def apply_invite_code( + payload: InviteRewardRequest, + db: Session = Depends(get_db), + user: AuthedUser = Depends(get_current_user), +): + """ + 新用户使用邀请码(注册后调用) + """ + user_row = db.query(User).filter(User.id == user.id).with_for_update().first() + if not user_row: + raise HTTPException(status_code=404, detail="用户不存在") + + # 检查是否已经使用过邀请码 + if user_row.invited_by: + raise HTTPException(status_code=400, detail="您已经使用过邀请码") + + # 不能使用自己的邀请码 + if user_row.invite_code == payload.invite_code: + raise HTTPException(status_code=400, detail="不能使用自己的邀请码") + + # 查找邀请人 + inviter = db.query(User).filter(User.invite_code == payload.invite_code).with_for_update().first() + if not inviter: + raise HTTPException(status_code=404, detail="邀请码不存在") + + # 奖励金额配置 + INVITER_REWARD = 10.00 # 邀请人获得10金币 + INVITEE_REWARD = 5.00 # 被邀请人获得5金币 + + # 给邀请人发放奖励 + inviter.money = float(inviter.money or 0) + INVITER_REWARD + inviter.invite_count = (inviter.invite_count or 0) + 1 + inviter.invite_reward_total = float(inviter.invite_reward_total or 0) + INVITER_REWARD + db.add(inviter) + + # 记录邀请人的金币日志 + db.add( + UserMoneyLog( + user_id=inviter.id, + money=Decimal(str(INVITER_REWARD)), + before=Decimal(str(float(inviter.money) - INVITER_REWARD)), + after=Decimal(str(inviter.money)), + memo=f"邀请新用户奖励", + createtime=int(time.time()), + ) + ) + + # 给被邀请人发放奖励 + user_row.money = float(user_row.money or 0) + INVITEE_REWARD + user_row.invited_by = payload.invite_code + db.add(user_row) + + # 记录被邀请人的金币日志 + db.add( + UserMoneyLog( + user_id=user.id, + money=Decimal(str(INVITEE_REWARD)), + before=Decimal(str(float(user_row.money) - INVITEE_REWARD)), + after=Decimal(str(user_row.money)), + memo=f"使用邀请码奖励", + createtime=int(time.time()), + ) + ) + + try: + db.flush() + except IntegrityError: + db.rollback() + raise HTTPException(status_code=409, detail="操作失败,请重试") + + return success_response({ + "message": f"邀请码使用成功!您获得了{INVITEE_REWARD}金币", + "reward": INVITEE_REWARD, + "balance": float(user_row.money) + }) diff --git a/xuniYou/pages/login/index.vue b/xuniYou/pages/login/index.vue index efb8ef0..08825dd 100644 --- a/xuniYou/pages/login/index.vue +++ b/xuniYou/pages/login/index.vue @@ -32,6 +32,19 @@ 🔧 开发模式:只需输入手机号即可登录 + + + + + + + + + ✅ 使用邀请码可获得5金币奖励 + + + + + 扫描二维码获取邀请码 + + + 长按保存二维码分享给好友 + + + + 邀请规则 + • 分享邀请码或二维码给好友 + • 好友扫码或输入邀请码注册 + • 您获得10金币,好友获得5金币 + • 金币可用于购买会员、音色等