Files
whale-town-end/src/business/login/login.service.ts
moyin c2ddb67b3e service:更新登录业务服务支持邮箱验证
- 添加发送邮箱验证码服务方法
- 添加验证邮箱验证码服务方法
- 添加重新发送邮箱验证码服务方法
- 集成验证码服务和邮件服务
- 更新相关的单元测试
2025-12-17 20:22:10 +08:00

430 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 登录业务服务
*
* 功能描述:
* - 处理登录相关的业务逻辑和流程控制
* - 整合核心服务,提供完整的业务功能
* - 处理业务规则、数据格式化和错误处理
*
* 职责分离:
* - 专注于业务流程和规则实现
* - 调用核心服务完成具体功能
* - 为控制器层提供业务接口
*
* @author moyin
* @version 1.0.0
* @since 2025-12-17
*/
import { Injectable, Logger } from '@nestjs/common';
import { LoginCoreService, LoginRequest, RegisterRequest, GitHubOAuthRequest, PasswordResetRequest, AuthResult } from '../../core/login_core/login_core.service';
import { Users } from '../../core/db/users/users.entity';
/**
* 登录响应数据接口
*/
export interface LoginResponse {
/** 用户信息 */
user: {
id: string;
username: string;
nickname: string;
email?: string;
phone?: string;
avatar_url?: string;
role: number;
created_at: Date;
};
/** 访问令牌实际应用中应生成JWT */
access_token: string;
/** 刷新令牌 */
refresh_token?: string;
/** 是否为新用户 */
is_new_user?: boolean;
/** 消息 */
message: string;
}
/**
* 通用响应接口
*/
export interface ApiResponse<T = any> {
/** 是否成功 */
success: boolean;
/** 响应数据 */
data?: T;
/** 消息 */
message: string;
/** 错误代码 */
error_code?: string;
}
@Injectable()
export class LoginService {
private readonly logger = new Logger(LoginService.name);
constructor(
private readonly loginCoreService: LoginCoreService,
) {}
/**
* 用户登录
*
* @param loginRequest 登录请求
* @returns 登录响应
*/
async login(loginRequest: LoginRequest): Promise<ApiResponse<LoginResponse>> {
try {
this.logger.log(`用户登录尝试: ${loginRequest.identifier}`);
// 调用核心服务进行认证
const authResult = await this.loginCoreService.login(loginRequest);
// 生成访问令牌实际应用中应使用JWT
const accessToken = this.generateAccessToken(authResult.user);
// 格式化响应数据
const response: LoginResponse = {
user: this.formatUserInfo(authResult.user),
access_token: accessToken,
is_new_user: authResult.isNewUser,
message: '登录成功'
};
this.logger.log(`用户登录成功: ${authResult.user.username} (ID: ${authResult.user.id})`);
return {
success: true,
data: response,
message: '登录成功'
};
} catch (error) {
this.logger.error(`用户登录失败: ${loginRequest.identifier}`, error instanceof Error ? error.stack : String(error));
return {
success: false,
message: error instanceof Error ? error.message : '登录失败',
error_code: 'LOGIN_FAILED'
};
}
}
/**
* 用户注册
*
* @param registerRequest 注册请求
* @returns 注册响应
*/
async register(registerRequest: RegisterRequest): Promise<ApiResponse<LoginResponse>> {
try {
this.logger.log(`用户注册尝试: ${registerRequest.username}`);
// 调用核心服务进行注册
const authResult = await this.loginCoreService.register(registerRequest);
// 生成访问令牌
const accessToken = this.generateAccessToken(authResult.user);
// 格式化响应数据
const response: LoginResponse = {
user: this.formatUserInfo(authResult.user),
access_token: accessToken,
is_new_user: true,
message: '注册成功'
};
this.logger.log(`用户注册成功: ${authResult.user.username} (ID: ${authResult.user.id})`);
return {
success: true,
data: response,
message: '注册成功'
};
} catch (error) {
this.logger.error(`用户注册失败: ${registerRequest.username}`, error instanceof Error ? error.stack : String(error));
return {
success: false,
message: error instanceof Error ? error.message : '注册失败',
error_code: 'REGISTER_FAILED'
};
}
}
/**
* GitHub OAuth登录
*
* @param oauthRequest OAuth请求
* @returns 登录响应
*/
async githubOAuth(oauthRequest: GitHubOAuthRequest): Promise<ApiResponse<LoginResponse>> {
try {
this.logger.log(`GitHub OAuth登录尝试: ${oauthRequest.github_id}`);
// 调用核心服务进行OAuth认证
const authResult = await this.loginCoreService.githubOAuth(oauthRequest);
// 生成访问令牌
const accessToken = this.generateAccessToken(authResult.user);
// 格式化响应数据
const response: LoginResponse = {
user: this.formatUserInfo(authResult.user),
access_token: accessToken,
is_new_user: authResult.isNewUser,
message: authResult.isNewUser ? 'GitHub账户绑定成功' : 'GitHub登录成功'
};
this.logger.log(`GitHub OAuth成功: ${authResult.user.username} (ID: ${authResult.user.id})`);
return {
success: true,
data: response,
message: response.message
};
} catch (error) {
this.logger.error(`GitHub OAuth失败: ${oauthRequest.github_id}`, error instanceof Error ? error.stack : String(error));
return {
success: false,
message: error instanceof Error ? error.message : 'GitHub登录失败',
error_code: 'GITHUB_OAUTH_FAILED'
};
}
}
/**
* 发送密码重置验证码
*
* @param identifier 邮箱或手机号
* @returns 响应结果
*/
async sendPasswordResetCode(identifier: string): Promise<ApiResponse<{ verification_code?: string }>> {
try {
this.logger.log(`发送密码重置验证码: ${identifier}`);
// 调用核心服务发送验证码
const verificationCode = await this.loginCoreService.sendPasswordResetCode(identifier);
this.logger.log(`密码重置验证码已发送: ${identifier}`);
// 实际应用中不应返回验证码,这里仅用于演示
return {
success: true,
data: { verification_code: verificationCode },
message: '验证码已发送,请查收'
};
} catch (error) {
this.logger.error(`发送密码重置验证码失败: ${identifier}`, error instanceof Error ? error.stack : String(error));
return {
success: false,
message: error instanceof Error ? error.message : '发送验证码失败',
error_code: 'SEND_CODE_FAILED'
};
}
}
/**
* 重置密码
*
* @param resetRequest 重置请求
* @returns 响应结果
*/
async resetPassword(resetRequest: PasswordResetRequest): Promise<ApiResponse> {
try {
this.logger.log(`密码重置尝试: ${resetRequest.identifier}`);
// 调用核心服务重置密码
await this.loginCoreService.resetPassword(resetRequest);
this.logger.log(`密码重置成功: ${resetRequest.identifier}`);
return {
success: true,
message: '密码重置成功'
};
} catch (error) {
this.logger.error(`密码重置失败: ${resetRequest.identifier}`, error instanceof Error ? error.stack : String(error));
return {
success: false,
message: error instanceof Error ? error.message : '密码重置失败',
error_code: 'RESET_PASSWORD_FAILED'
};
}
}
/**
* 修改密码
*
* @param userId 用户ID
* @param oldPassword 旧密码
* @param newPassword 新密码
* @returns 响应结果
*/
async changePassword(userId: bigint, oldPassword: string, newPassword: string): Promise<ApiResponse> {
try {
this.logger.log(`修改密码尝试: 用户ID ${userId}`);
// 调用核心服务修改密码
await this.loginCoreService.changePassword(userId, oldPassword, newPassword);
this.logger.log(`修改密码成功: 用户ID ${userId}`);
return {
success: true,
message: '密码修改成功'
};
} catch (error) {
this.logger.error(`修改密码失败: 用户ID ${userId}`, error instanceof Error ? error.stack : String(error));
return {
success: false,
message: error instanceof Error ? error.message : '密码修改失败',
error_code: 'CHANGE_PASSWORD_FAILED'
};
}
}
/**
* 发送邮箱验证码
*
* @param email 邮箱地址
* @returns 响应结果
*/
async sendEmailVerification(email: string): Promise<ApiResponse<{ verification_code?: string }>> {
try {
this.logger.log(`发送邮箱验证码: ${email}`);
// 调用核心服务发送验证码
const verificationCode = await this.loginCoreService.sendEmailVerification(email);
this.logger.log(`邮箱验证码已发送: ${email}`);
// 实际应用中不应返回验证码,这里仅用于演示
return {
success: true,
data: { verification_code: verificationCode },
message: '验证码已发送,请查收邮件'
};
} catch (error) {
this.logger.error(`发送邮箱验证码失败: ${email}`, error instanceof Error ? error.stack : String(error));
return {
success: false,
message: error instanceof Error ? error.message : '发送验证码失败',
error_code: 'SEND_EMAIL_VERIFICATION_FAILED'
};
}
}
/**
* 验证邮箱验证码
*
* @param email 邮箱地址
* @param code 验证码
* @returns 响应结果
*/
async verifyEmailCode(email: string, code: string): Promise<ApiResponse> {
try {
this.logger.log(`验证邮箱验证码: ${email}`);
// 调用核心服务验证验证码
const isValid = await this.loginCoreService.verifyEmailCode(email, code);
if (isValid) {
this.logger.log(`邮箱验证成功: ${email}`);
return {
success: true,
message: '邮箱验证成功'
};
} else {
return {
success: false,
message: '验证码错误',
error_code: 'INVALID_VERIFICATION_CODE'
};
}
} catch (error) {
this.logger.error(`邮箱验证失败: ${email}`, error instanceof Error ? error.stack : String(error));
return {
success: false,
message: error instanceof Error ? error.message : '邮箱验证失败',
error_code: 'EMAIL_VERIFICATION_FAILED'
};
}
}
/**
* 重新发送邮箱验证码
*
* @param email 邮箱地址
* @returns 响应结果
*/
async resendEmailVerification(email: string): Promise<ApiResponse<{ verification_code?: string }>> {
try {
this.logger.log(`重新发送邮箱验证码: ${email}`);
// 调用核心服务重新发送验证码
const verificationCode = await this.loginCoreService.resendEmailVerification(email);
this.logger.log(`邮箱验证码已重新发送: ${email}`);
// 实际应用中不应返回验证码,这里仅用于演示
return {
success: true,
data: { verification_code: verificationCode },
message: '验证码已重新发送,请查收邮件'
};
} catch (error) {
this.logger.error(`重新发送邮箱验证码失败: ${email}`, error instanceof Error ? error.stack : String(error));
return {
success: false,
message: error instanceof Error ? error.message : '重新发送验证码失败',
error_code: 'RESEND_EMAIL_VERIFICATION_FAILED'
};
}
}
/**
* 格式化用户信息
*
* @param user 用户实体
* @returns 格式化的用户信息
*/
private formatUserInfo(user: Users) {
return {
id: user.id.toString(), // 将bigint转换为字符串
username: user.username,
nickname: user.nickname,
email: user.email,
phone: user.phone,
avatar_url: user.avatar_url,
role: user.role,
created_at: user.created_at
};
}
/**
* 生成访问令牌
*
* @param user 用户信息
* @returns 访问令牌
*/
private generateAccessToken(user: Users): string {
// 实际应用中应使用JWT库生成真正的JWT令牌
// 这里仅用于演示,生成一个简单的令牌
const payload = {
userId: user.id.toString(),
username: user.username,
role: user.role,
timestamp: Date.now()
};
// 简单的Base64编码实际应用中应使用JWT
return Buffer.from(JSON.stringify(payload)).toString('base64');
}
}