/** * 邮件服务 * * 功能描述: * - 提供邮件发送的核心功能 * - 支持多种邮件模板和场景 * - 集成主流邮件服务提供商 * * 支持的邮件类型: * - 邮箱验证码 * - 密码重置验证码 * - 欢迎邮件 * - 系统通知 * * @author moyin * @version 1.0.0 * @since 2025-12-17 */ import { Injectable, Logger, BadRequestException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import * as nodemailer from 'nodemailer'; import { Transporter } from 'nodemailer'; /** * 邮件发送选项接口 */ export interface EmailOptions { /** 收件人邮箱 */ to: string; /** 邮件主题 */ subject: string; /** 邮件内容(HTML格式) */ html: string; /** 邮件内容(纯文本格式) */ text?: string; } /** * 验证码邮件选项接口 */ export interface VerificationEmailOptions { /** 收件人邮箱 */ email: string; /** 验证码 */ code: string; /** 用户昵称 */ nickname?: string; /** 验证码用途 */ purpose: 'email_verification' | 'password_reset'; } @Injectable() export class EmailService { private readonly logger = new Logger(EmailService.name); private transporter: Transporter; constructor(private readonly configService: ConfigService) { this.initializeTransporter(); } /** * 初始化邮件传输器 */ private initializeTransporter(): void { const emailConfig = { host: this.configService.get('EMAIL_HOST', 'smtp.gmail.com'), port: this.configService.get('EMAIL_PORT', 587), secure: this.configService.get('EMAIL_SECURE', false), // true for 465, false for other ports auth: { user: this.configService.get('EMAIL_USER'), pass: this.configService.get('EMAIL_PASS'), }, }; // 如果没有配置邮件服务,使用测试模式 if (!emailConfig.auth.user || !emailConfig.auth.pass) { this.logger.warn('邮件服务未配置,将使用测试模式(邮件不会真实发送)'); this.transporter = nodemailer.createTransport({ streamTransport: true, newline: 'unix', buffer: true }); } else { this.transporter = nodemailer.createTransport(emailConfig); this.logger.log('邮件服务初始化成功'); } } /** * 发送邮件 * * @param options 邮件选项 * @returns 发送结果 */ async sendEmail(options: EmailOptions): Promise { try { const mailOptions = { from: this.configService.get('EMAIL_FROM', '"Whale Town Game" '), to: options.to, subject: options.subject, html: options.html, text: options.text, }; const result = await this.transporter.sendMail(mailOptions); // 如果是测试模式,输出邮件内容到控制台 if ((this.transporter.options as any).streamTransport) { this.logger.log('=== 邮件发送(测试模式) ==='); this.logger.log(`收件人: ${options.to}`); this.logger.log(`主题: ${options.subject}`); this.logger.log(`内容: ${options.text || '请查看HTML内容'}`); this.logger.log('========================'); } this.logger.log(`邮件发送成功: ${options.to}`); return true; } catch (error) { this.logger.error(`邮件发送失败: ${options.to}`, error instanceof Error ? error.stack : String(error)); return false; } } /** * 发送邮箱验证码 * * @param options 验证码邮件选项 * @returns 发送结果 */ async sendVerificationCode(options: VerificationEmailOptions): Promise { const { email, code, nickname, purpose } = options; let subject: string; let template: string; if (purpose === 'email_verification') { subject = '【Whale Town】邮箱验证码'; template = this.getEmailVerificationTemplate(code, nickname); } else { subject = '【Whale Town】密码重置验证码'; template = this.getPasswordResetTemplate(code, nickname); } return await this.sendEmail({ to: email, subject, html: template, text: `您的验证码是:${code},5分钟内有效,请勿泄露给他人。` }); } /** * 发送欢迎邮件 * * @param email 邮箱地址 * @param nickname 用户昵称 * @returns 发送结果 */ async sendWelcomeEmail(email: string, nickname: string): Promise { const subject = '🎮 欢迎加入 Whale Town!'; const template = this.getWelcomeTemplate(nickname); return await this.sendEmail({ to: email, subject, html: template, text: `欢迎 ${nickname} 加入 Whale Town 像素游戏世界!` }); } /** * 获取邮箱验证模板 * * @param code 验证码 * @param nickname 用户昵称 * @returns HTML模板 */ private getEmailVerificationTemplate(code: string, nickname?: string): string { return ` 邮箱验证

🐋 Whale Town

邮箱验证

你好${nickname ? ` ${nickname}` : ''}!

感谢您注册 Whale Town 像素游戏!为了确保您的账户安全,请使用以下验证码完成邮箱验证:

${code}

验证码

⚠️ 安全提醒:
  • 验证码 5 分钟内有效
  • 请勿将验证码泄露给他人
  • 如非本人操作,请忽略此邮件

完成验证后,您就可以开始您的像素世界冒险之旅了!

`; } /** * 获取密码重置模板 * * @param code 验证码 * @param nickname 用户昵称 * @returns HTML模板 */ private getPasswordResetTemplate(code: string, nickname?: string): string { return ` 密码重置

🔐 密码重置

Whale Town 账户安全

你好${nickname ? ` ${nickname}` : ''}!

我们收到了您的密码重置请求。请使用以下验证码来重置您的密码:

${code}

密码重置验证码

🛡️ 安全提醒:
  • 验证码 5 分钟内有效
  • 请勿将验证码泄露给他人
  • 如非本人操作,请立即联系客服
  • 重置密码后请妥善保管新密码

如果您没有请求重置密码,请忽略此邮件,您的账户仍然安全。

`; } /** * 获取欢迎邮件模板 * * @param nickname 用户昵称 * @returns HTML模板 */ private getWelcomeTemplate(nickname: string): string { return ` 欢迎加入 Whale Town

🎮 欢迎加入 Whale Town!

像素世界的冒险即将开始

欢迎你,${nickname}!

恭喜您成功注册 Whale Town 像素游戏!您现在已经成为我们像素世界大家庭的一员了。

🏗️ 建造与创造

在像素世界中建造您的梦想家园,发挥无限创意!

🤝 社交互动

与其他玩家交流互动,结交志同道合的朋友!

🎯 任务挑战

完成各种有趣的任务,获得丰厚的奖励!

现在就开始您的像素冒险之旅吧!

如果您在游戏过程中遇到任何问题,随时可以联系我们的客服团队。

`; } /** * 验证邮件服务配置 * * @returns 验证结果 */ async verifyConnection(): Promise { try { await this.transporter.verify(); this.logger.log('邮件服务连接验证成功'); return true; } catch (error) { this.logger.error('邮件服务连接验证失败', error instanceof Error ? error.stack : String(error)); return false; } } }