feat: 添加手机号、邀请好友、邮件通知功能

- 用户模型添加:手机号、邀请码、邀请统计、邮件通知设置
- 邀请好友系统:专属邀请码、邀请奖励(¥5/人)
- 邮件通知:翻译完成通知(含附件)、欢迎邮件、到期提醒
- 新增模型:UserInvitation, InviteRewardConfig, EmailNotification, EmailTemplateConfig
- 个人中心添加:手机号绑定、通知设置、邀请好友模块
- email_service.py:邮件发送服务(支持附件)

新用户注册奖励:¥2
邀请人奖励:¥5/人
This commit is contained in:
2026-04-14 18:58:40 +08:00
parent 4aac8ab04c
commit 71a613ff5f
4 changed files with 725 additions and 4 deletions
+135 -3
View File
@@ -15,7 +15,9 @@ from werkzeug.utils import secure_filename
from config import *
from models import (db, User, Translation, TranslationCache, GuestTranslation,
DataPackage, UserPackage, DynamicConfig, UserRecharge, UserRefund,
MembershipPurchase, AccountTransaction, MembershipPlanConfig, UserTypeConfig)
MembershipPurchase, AccountTransaction, MembershipPlanConfig, UserTypeConfig,
UserInvitation, InviteRewardConfig, EmailNotification, EmailTemplateConfig)
from email_service import email_service
from services import TranslationService, CacheService, TranslationTask
from admin import admin_bp
@@ -591,12 +593,15 @@ def login():
def register():
"""注册"""
if request.method == 'GET':
return render_template('register.html')
# 检查邀请码参数
invite_code = request.args.get('invite', None)
return render_template('register.html', invite_code=invite_code)
data = request.json
username = data.get('username')
email = data.get('email')
password = data.get('password')
invite_code = data.get('invite_code', None) # 邀请码
# 检查用户是否存在
if User.query.filter_by(username=username).first():
@@ -608,11 +613,76 @@ def register():
# 创建用户
user = User(username=username, email=email, user_type='free')
user.set_password(password)
# 生成邀请码(6位随机字符)
import random
import string
user.invite_code = ''.join(random.choices(string.ascii_uppercase + string.digits, k=6))
# 处理邀请
if invite_code:
inviter = User.query.filter_by(invite_code=invite_code).first()
if inviter:
user.invited_by = inviter.id
# 创建邀请记录
invitation = UserInvitation(
inviter_id=inviter.id,
invite_code=invite_code,
invitee_id=user.id,
invitee_email=email,
reward_amount=5.0,
status='registered',
registered_at=datetime.utcnow()
)
db.session.add(invitation)
# 给邀请人奖励
inviter.invite_count += 1
inviter.invite_rewards += 5.0
inviter.balance += 5.0
# 给被邀请人奖励
user.balance += 2.0
# 创建流水
inviter_tx = AccountTransaction(
user_id=inviter.id,
transaction_type='invite_reward',
amount=5.0,
balance_before=inviter.balance - 5.0,
balance_after=inviter.balance,
description=f'邀请用户{username}注册奖励'
)
db.session.add(inviter_tx)
user_tx = AccountTransaction(
user_id=user.id,
transaction_type='invite_bonus',
amount=2.0,
balance_before=0,
balance_after=2.0,
description='新用户注册奖励'
)
db.session.add(user_tx)
# 发送邀请奖励邮件
if inviter.email_notify:
email_service.send_invite_reward(inviter.email, inviter.username, 5.0, inviter.invite_count)
db.session.add(user)
db.session.commit()
session['user_id'] = user.id
return jsonify({'success': True, 'user': user.to_dict()})
# 发送欢迎邮件
email_service.send_welcome_email(user.email, user.username, user.invite_code)
return jsonify({
'success': True,
'user': user.to_dict(),
'invite_reward': user.balance > 0 # 是否获得邀请奖励
})
@app.route('/logout')
@@ -775,6 +845,68 @@ def request_refund():
})
@app.route('/api/profile/settings', methods=['POST'])
def update_settings():
"""更新账户设置"""
user = get_current_user()
if not user:
return jsonify({'error': '请登录'}), 401
data = request.json
# 手机号
if 'phone' in data:
phone = data.get('phone', '')
if phone and len(phone) >= 10:
user.phone = phone
user.phone_verified = False
# 通知设置
if 'notify_on_complete' in data:
user.notify_on_complete = data.get('notify_on_complete', True)
if 'notify_on_expire' in data:
user.notify_on_expire = data.get('notify_on_expire', True)
if 'email_notify' in data:
user.email_notify = data.get('email_notify', True)
db.session.commit()
return jsonify({
'success': True,
'user': user.to_dict()
})
@app.route('/api/profile/invitations')
def get_invitations():
"""获取邀请记录"""
user = get_current_user()
if not user:
return jsonify({'error': '请登录'}), 401
invitations = UserInvitation.query.filter_by(inviter_id=user.id)\
.order_by(UserInvitation.created_at.desc()).limit(20).all()
return jsonify({
'success': True,
'invitations': [inv.to_dict() for inv in invitations]
})
@app.route('/api/invite/<invite_code>')
def check_invite(invite_code):
"""检查邀请码"""
inviter = User.query.filter_by(invite_code=invite_code).first()
if not inviter:
return jsonify({'valid': False, 'error': '邀请码无效'})
return jsonify({
'valid': True,
'inviter': inviter.username,
'reward': 5 # 被邀请人奖励
})
# ==================== 初始化 ====================
def init_app():
"""初始化应用"""