resolve: 解决ANGJustinl-main与main分支的合并冲突

- 修复文件路径冲突(business/login -> business/auth结构调整)
- 保留ANGJustinl分支的验证码登录功能
- 合并main分支的用户状态管理和项目结构改进
- 修复邮件服务中缺失的login_verification模板问题
- 更新测试用例以包含验证码登录功能
- 统一导入路径以适配新的目录结构
This commit is contained in:
moyin
2025-12-25 15:11:14 +08:00
94 changed files with 8364 additions and 2029 deletions

View File

@@ -0,0 +1,595 @@
/**
* 登录业务服务
*
* 功能描述:
* - 处理登录相关的业务逻辑和流程控制
* - 整合核心服务,提供完整的业务功能
* - 处理业务规则、数据格式化和错误处理
*
* 职责分离:
* - 专注于业务流程和规则实现
* - 调用核心服务完成具体功能
* - 为控制器层提供业务接口
*
* @author moyin angjustinl
* @version 1.0.0
* @since 2025-12-17
*/
import { Injectable, Logger } from '@nestjs/common';
import { LoginCoreService, LoginRequest, RegisterRequest, GitHubOAuthRequest, PasswordResetRequest, AuthResult, VerificationCodeLoginRequest } 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; is_test_mode?: boolean }>> {
try {
this.logger.log(`发送密码重置验证码: ${identifier}`);
// 调用核心服务发送验证码
const result = await this.loginCoreService.sendPasswordResetCode(identifier);
this.logger.log(`密码重置验证码已发送: ${identifier}`);
// 根据是否为测试模式返回不同的状态和消息 by angjustinl 2025-12-17
if (result.isTestMode) {
// 测试模式:验证码生成但未真实发送
return {
success: false, // 测试模式下不算真正成功
data: {
verification_code: result.code,
is_test_mode: true
},
message: '⚠️ 测试模式:验证码已生成但未真实发送。请在控制台查看验证码,或配置邮件服务以启用真实发送。',
error_code: 'TEST_MODE_ONLY'
};
} else {
// 真实发送模式
return {
success: true,
data: {
is_test_mode: false
},
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; is_test_mode?: boolean }>> {
try {
this.logger.log(`发送邮箱验证码: ${email}`);
// 调用核心服务发送验证码
const result = await this.loginCoreService.sendEmailVerification(email);
this.logger.log(`邮箱验证码已发送: ${email}`);
// 根据是否为测试模式返回不同的状态和消息
if (result.isTestMode) {
// 测试模式:验证码生成但未真实发送
return {
success: false, // 测试模式下不算真正成功
data: {
verification_code: result.code,
is_test_mode: true
},
message: '⚠️ 测试模式:验证码已生成但未真实发送。请在控制台查看验证码,或配置邮件服务以启用真实发送。',
error_code: 'TEST_MODE_ONLY'
};
} else {
// 真实发送模式
return {
success: true,
data: {
is_test_mode: false
},
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; is_test_mode?: boolean }>> {
try {
this.logger.log(`重新发送邮箱验证码: ${email}`);
// 调用核心服务重新发送验证码
const result = await this.loginCoreService.resendEmailVerification(email);
this.logger.log(`邮箱验证码已重新发送: ${email}`);
// 根据是否为测试模式返回不同的状态和消息
if (result.isTestMode) {
// 测试模式:验证码生成但未真实发送
return {
success: false, // 测试模式下不算真正成功
data: {
verification_code: result.code,
is_test_mode: true
},
message: '⚠️ 测试模式:验证码已生成但未真实发送。请在控制台查看验证码,或配置邮件服务以启用真实发送。',
error_code: 'TEST_MODE_ONLY'
};
} else {
// 真实发送模式
return {
success: true,
data: {
is_test_mode: false
},
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');
}
/**
* 验证码登录
*
* @param loginRequest 验证码登录请求
* @returns 登录响应
*/
async verificationCodeLogin(loginRequest: VerificationCodeLoginRequest): Promise<ApiResponse<LoginResponse>> {
try {
this.logger.log(`验证码登录尝试: ${loginRequest.identifier}`);
// 调用核心服务进行验证码认证
const authResult = await this.loginCoreService.verificationCodeLogin(loginRequest);
// 生成访问令牌
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: 'VERIFICATION_CODE_LOGIN_FAILED'
};
}
}
/**
* 发送登录验证码
*
* @param identifier 邮箱或手机号
* @returns 响应结果
*/
async sendLoginVerificationCode(identifier: string): Promise<ApiResponse<{ verification_code?: string; is_test_mode?: boolean }>> {
try {
this.logger.log(`发送登录验证码: ${identifier}`);
// 调用核心服务发送验证码
const result = await this.loginCoreService.sendLoginVerificationCode(identifier);
this.logger.log(`登录验证码已发送: ${identifier}`);
// 根据是否为测试模式返回不同的状态和消息
if (result.isTestMode) {
// 测试模式:验证码生成但未真实发送
return {
success: false, // 测试模式下不算真正成功
data: {
verification_code: result.code,
is_test_mode: true
},
message: '⚠️ 测试模式:验证码已生成但未真实发送。请在控制台查看验证码,或配置邮件服务以启用真实发送。',
error_code: 'TEST_MODE_ONLY'
};
} else {
// 真实发送模式
return {
success: true,
data: {
is_test_mode: false
},
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_LOGIN_CODE_FAILED'
};
}
}
/**
* 调试验证码信息
*
* @param email 邮箱地址
* @returns 调试信息
*/
async debugVerificationCode(email: string): Promise<any> {
try {
this.logger.log(`调试验证码信息: ${email}`);
const debugInfo = await this.loginCoreService.debugVerificationCode(email);
return {
success: true,
data: debugInfo,
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: 'DEBUG_VERIFICATION_CODE_FAILED'
};
}
}
}