- 用户模型添加:手机号、邀请码、邀请统计、邮件通知设置 - 邀请好友系统:专属邀请码、邀请奖励(¥5/人) - 邮件通知:翻译完成通知(含附件)、欢迎邮件、到期提醒 - 新增模型:UserInvitation, InviteRewardConfig, EmailNotification, EmailTemplateConfig - 个人中心添加:手机号绑定、通知设置、邀请好友模块 - email_service.py:邮件发送服务(支持附件) 新用户注册奖励:¥2 邀请人奖励:¥5/人
210 lines
10 KiB
Python
210 lines
10 KiB
Python
"""
|
||
邮件发送服务
|
||
支持SMTP邮件发送、附件发送、模板渲染
|
||
"""
|
||
|
||
import os
|
||
import smtplib
|
||
from email.mime.text import MIMEText
|
||
from email.mime.multipart import MIMEMultipart
|
||
from email.mime.base import MIMEBase
|
||
from email import encoders
|
||
from datetime import datetime
|
||
from flask import current_app
|
||
|
||
|
||
class EmailService:
|
||
"""邮件发送服务"""
|
||
|
||
def __init__(self, smtp_host=None, smtp_port=None, smtp_user=None, smtp_pass=None):
|
||
self.smtp_host = smtp_host or os.environ.get('SMTP_HOST', 'mail.tphai.com')
|
||
self.smtp_port = int(smtp_port or os.environ.get('SMTP_PORT', '587'))
|
||
self.smtp_user = smtp_user or os.environ.get('SMTP_USER', 'favor@tphai.com')
|
||
self.smtp_pass = smtp_pass or os.environ.get('SMTP_PASS', 'favor@!')
|
||
|
||
def send_email(self, to_email, subject, body, attachment_path=None, attachment_name=None):
|
||
"""发送邮件(支持附件)"""
|
||
try:
|
||
# 创建邮件对象
|
||
msg = MIMEMultipart()
|
||
msg['From'] = self.smtp_user
|
||
msg['To'] = to_email
|
||
msg['Subject'] = subject
|
||
msg['Date'] = datetime.now().strftime('%a, %d %b %Y %H:%M:%S +0800')
|
||
msg['Reply-To'] = to_email
|
||
|
||
# 正文
|
||
msg.attach(MIMEText(body, 'html', 'utf-8'))
|
||
|
||
# 附件
|
||
if attachment_path and os.path.exists(attachment_path):
|
||
with open(attachment_path, 'rb') as f:
|
||
part = MIMEBase('application', 'octet-stream')
|
||
part.set_payload(f.read())
|
||
encoders.encode_base64(part)
|
||
part.add_header('Content-Disposition', 'attachment',
|
||
filename=attachment_name or os.path.basename(attachment_path))
|
||
msg.attach(part)
|
||
|
||
# 发送
|
||
server = smtplib.SMTP(self.smtp_host, self.smtp_port)
|
||
server.ehlo()
|
||
server.login(self.smtp_user, self.smtp_pass)
|
||
server.sendmail(self.smtp_user, to_email, msg.as_string())
|
||
server.quit()
|
||
|
||
return True, "发送成功"
|
||
|
||
except Exception as e:
|
||
return False, str(e)
|
||
|
||
def send_translation_complete(self, user_email, username, filename, output_path, translation_id):
|
||
"""翻译完成通知"""
|
||
subject = f"【PDF翻译助手】翻译完成 - {filename}"
|
||
|
||
body = f"""
|
||
<html>
|
||
<body style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
|
||
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 30px; text-align: center; border-radius: 10px 10px 0 0;">
|
||
<h1 style="color: white; margin: 0;">📄 PDF翻译助手</h1>
|
||
</div>
|
||
<div style="background: white; padding: 30px; border: 1px solid #eee; border-radius: 0 0 10px 10px;">
|
||
<p style="color: #333; font-size: 16px;">您好,{username}!</p>
|
||
<p style="color: #666;">您的翻译任务已完成:</p>
|
||
|
||
<div style="background: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
|
||
<p style="margin: 5px 0;"><strong>文件:</strong>{filename}</p>
|
||
<p style="margin: 5px 0;"><strong>状态:</strong><span style="color: #28a745;">✅ 完成</span></p>
|
||
<p style="margin: 5px 0;"><strong>时间:</strong>{datetime.now().strftime('%Y-%m-%d %H:%M')}</p>
|
||
</div>
|
||
|
||
<p style="color: #666;">翻译结果已作为附件发送,您也可以登录网站查看详情。</p>
|
||
|
||
<div style="text-align: center; margin: 30px 0;">
|
||
<a href="http://localhost:19000/translation/{translation_id}"
|
||
style="background: #667eea; color: white; padding: 12px 30px; text-decoration: none; border-radius: 5px;">
|
||
查看翻译结果
|
||
</a>
|
||
</div>
|
||
|
||
<hr style="border: none; border-top: 1px solid #eee; margin: 30px 0;">
|
||
<p style="color: #999; font-size: 12px; text-align: center;">
|
||
此邮件由系统自动发送,请勿回复。<br>
|
||
PDF翻译助手 - 让翻译更简单
|
||
</p>
|
||
</div>
|
||
</body>
|
||
</html>
|
||
"""
|
||
|
||
# 附件名称
|
||
attachment_name = f"{filename}_translated.md"
|
||
|
||
return self.send_email(user_email, subject, body, output_path, attachment_name)
|
||
|
||
def send_welcome_email(self, user_email, username, invite_code=None):
|
||
"""欢迎邮件"""
|
||
subject = "欢迎加入PDF翻译助手"
|
||
|
||
body = f"""
|
||
<html>
|
||
<body style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
|
||
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 30px; text-align: center; border-radius: 10px 10px 0 0;">
|
||
<h1 style="color: white; margin: 0;">📄 PDF翻译助手</h1>
|
||
</div>
|
||
<div style="background: white; padding: 30px; border: 1px solid #eee; border-radius: 0 0 10px 10px;">
|
||
<p style="color: #333; font-size: 16px;">您好,{username}!</p>
|
||
<p style="color: #666;">欢迎加入PDF翻译助手!您已获得:</p>
|
||
|
||
<div style="background: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
|
||
<p style="margin: 5px 0;">✅ 每日免费翻译 10 次</p>
|
||
<p style="margin: 5px 0;">✅ 单文件最大 50 页</p>
|
||
<p style="margin: 5px 0;">✅ 翻译历史记录</p>
|
||
<p style="margin: 5px 0;">✅ 不满意重新翻译</p>
|
||
</div>
|
||
|
||
{"<p style='color: #666;'>您的专属邀请码:<strong style='color: #667eea;'>" + invite_code + "</strong>,分享给好友可获得奖励!</p>" if invite_code else ""}
|
||
|
||
<div style="text-align: center; margin: 30px 0;">
|
||
<a href="http://localhost:19000/"
|
||
style="background: #667eea; color: white; padding: 12px 30px; text-decoration: none; border-radius: 5px;">
|
||
开始使用
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</body>
|
||
</html>
|
||
"""
|
||
|
||
return self.send_email(user_email, subject, body)
|
||
|
||
def send_expire_reminder(self, user_email, username, expire_date, user_type):
|
||
"""会员到期提醒"""
|
||
subject = "【PDF翻译助手】会员即将到期提醒"
|
||
|
||
body = f"""
|
||
<html>
|
||
<body style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
|
||
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 30px; text-align: center; border-radius: 10px 10px 0 0;">
|
||
<h1 style="color: white; margin: 0;">📄 PDF翻译助手</h1>
|
||
</div>
|
||
<div style="background: white; padding: 30px; border: 1px solid #eee; border-radius: 0 0 10px 10px;">
|
||
<p style="color: #333; font-size: 16px;">您好,{username}!</p>
|
||
|
||
<div style="background: #fff3cd; padding: 15px; border-radius: 5px; margin: 20px 0; border-left: 4px solid #ffc107;">
|
||
<p style="margin: 5px 0; color: #856404;">⚠️ 您的会员即将到期</p>
|
||
<p style="margin: 5px 0;"><strong>会员类型:</strong>{user_type}</p>
|
||
<p style="margin: 5px 0;"><strong>到期时间:</strong>{expire_date}</p>
|
||
</div>
|
||
|
||
<p style="color: #666;">到期后将降级为免费用户,每日翻译次数限制为10次。续费可继续享受会员权益。</p>
|
||
|
||
<div style="text-align: center; margin: 30px 0;">
|
||
<a href="http://localhost:19000/pricing"
|
||
style="background: #667eea; color: white; padding: 12px 30px; text-decoration: none; border-radius: 5px;">
|
||
续费会员
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</body>
|
||
</html>
|
||
"""
|
||
|
||
return self.send_email(user_email, subject, body)
|
||
|
||
def send_invite_reward(self, user_email, username, reward_amount, invitee_count):
|
||
"""邀请奖励通知"""
|
||
subject = f"【PDF翻译助手】邀请奖励 - ¥{reward_amount}"
|
||
|
||
body = f"""
|
||
<html>
|
||
<body style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
|
||
<div style="background: linear-gradient(135deg, #28a745 0%, #20c997 100%); padding: 30px; text-align: center; border-radius: 10px 10px 0 0;">
|
||
<h1 style="color: white; margin: 0;">🎉 邀请奖励已发放</h1>
|
||
</div>
|
||
<div style="background: white; padding: 30px; border: 1px solid #eee; border-radius: 0 0 10px 10px;">
|
||
<p style="color: #333; font-size: 16px;">您好,{username}!</p>
|
||
|
||
<div style="background: #d4edda; padding: 15px; border-radius: 5px; margin: 20px 0; border-left: 4px solid #28a745;">
|
||
<p style="margin: 5px 0; color: #155724;"><strong>奖励金额:</strong>¥{reward_amount}</p>
|
||
<p style="margin: 5px 0; color: #155724;"><strong>累计邀请:</strong>{invitee_count}人</p>
|
||
</div>
|
||
|
||
<p style="color: #666;">奖励已发放到您的账户余额,可用于购买会员或翻译服务。</p>
|
||
|
||
<div style="text-align: center; margin: 30px 0;">
|
||
<a href="http://localhost:19000/profile"
|
||
style="background: #28a745; color: white; padding: 12px 30px; text-decoration: none; border-radius: 5px;">
|
||
查看余额
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</body>
|
||
</html>
|
||
"""
|
||
|
||
return self.send_email(user_email, subject, body)
|
||
|
||
|
||
# 全局邮件服务实例
|
||
email_service = EmailService() |