/** * Zulip用户管理服务 * * 功能描述: * - 查询和验证Zulip用户信息 * - 检查用户是否存在 * - 获取用户详细信息 * - 验证用户凭据和权限 * * 主要方法: * - checkUserExists(): 检查用户是否存在 * - getUserInfo(): 获取用户详细信息 * - validateUserCredentials(): 验证用户凭据 * - getAllUsers(): 获取所有用户列表 * * 使用场景: * - 用户登录时验证用户存在性 * - 获取用户基本信息 * - 验证用户权限和状态 * - 管理员查看用户列表 * * @author angjustinl * @version 1.0.0 * @since 2025-01-06 */ import { Injectable, Logger, Inject } from '@nestjs/common'; import { IZulipConfigService } from '../interfaces/zulip-core.interfaces'; /** * Zulip API响应接口 */ interface ZulipApiResponse { result?: 'success' | 'error'; msg?: string; message?: string; } /** * 用户信息接口 */ interface ZulipUser { user_id: number; email: string; full_name: string; is_active: boolean; is_admin: boolean; is_owner: boolean; is_bot: boolean; date_joined: string; } /** * 用户列表响应接口 */ interface ZulipUsersResponse extends ZulipApiResponse { members?: ZulipUser[]; } /** * 用户查询请求接口 */ export interface UserQueryRequest { email: string; } /** * 用户信息响应接口 */ export interface UserInfoResponse { success: boolean; userId?: number; email?: string; fullName?: string; isActive?: boolean; isAdmin?: boolean; isBot?: boolean; dateJoined?: string; error?: string; } /** * 用户验证请求接口 */ export interface UserValidationRequest { email: string; apiKey?: string; } /** * 用户验证响应接口 */ export interface UserValidationResponse { success: boolean; isValid?: boolean; userId?: number; error?: string; } /** * 用户列表响应接口 */ export interface UsersListResponse { success: boolean; users?: Array<{ userId: number; email: string; fullName: string; isActive: boolean; isAdmin: boolean; isBot: boolean; }>; totalCount?: number; error?: string; } /** * Zulip用户管理服务类 * * 职责: * - 查询和验证Zulip用户信息 * - 检查用户是否存在于Zulip服务器 * - 获取用户详细信息和权限状态 * - 提供用户管理相关的API接口 */ @Injectable() export class UserManagementService { private readonly logger = new Logger(UserManagementService.name); constructor( @Inject('ZULIP_CONFIG_SERVICE') private readonly configService: IZulipConfigService, ) { this.logger.log('UserManagementService初始化完成'); } /** * 检查用户是否存在 * * 功能描述: * 通过Zulip API检查指定邮箱的用户是否存在 * * 业务逻辑: * 1. 获取所有用户列表 * 2. 在列表中查找指定邮箱 * 3. 返回用户存在性结果 * * @param email 用户邮箱 * @returns Promise 是否存在 */ async checkUserExists(email: string): Promise { const startTime = Date.now(); this.logger.log('开始检查用户是否存在', { operation: 'checkUserExists', email, timestamp: new Date().toISOString(), }); try { // 1. 验证邮箱格式 if (!email || !this.isValidEmail(email)) { this.logger.warn('邮箱格式无效', { operation: 'checkUserExists', email, }); return false; } // 2. 获取用户列表 const usersResult = await this.getAllUsers(); if (!usersResult.success) { this.logger.warn('获取用户列表失败', { operation: 'checkUserExists', email, error: usersResult.error, }); return false; } // 3. 检查用户是否存在 const userExists = usersResult.users?.some(user => user.email.toLowerCase() === email.toLowerCase() ) || false; const duration = Date.now() - startTime; this.logger.log('用户存在性检查完成', { operation: 'checkUserExists', email, exists: userExists, duration, timestamp: new Date().toISOString(), }); return userExists; } catch (error) { const err = error as Error; const duration = Date.now() - startTime; this.logger.error('检查用户存在性失败', { operation: 'checkUserExists', email, error: err.message, duration, timestamp: new Date().toISOString(), }, err.stack); return false; } } /** * 获取用户详细信息 * * 功能描述: * 根据邮箱获取用户的详细信息 * * @param request 用户查询请求 * @returns Promise */ async getUserInfo(request: UserQueryRequest): Promise { const startTime = Date.now(); this.logger.log('开始获取用户信息', { operation: 'getUserInfo', email: request.email, timestamp: new Date().toISOString(), }); try { // 1. 验证请求参数 if (!request.email || !this.isValidEmail(request.email)) { return { success: false, error: '邮箱格式无效', }; } // 2. 获取用户列表 const usersResult = await this.getAllUsers(); if (!usersResult.success) { return { success: false, error: usersResult.error || '获取用户列表失败', }; } // 3. 查找指定用户 const user = usersResult.users?.find(u => u.email.toLowerCase() === request.email.toLowerCase() ); if (!user) { return { success: false, error: '用户不存在', }; } const duration = Date.now() - startTime; this.logger.log('用户信息获取完成', { operation: 'getUserInfo', email: request.email, userId: user.userId, duration, timestamp: new Date().toISOString(), }); return { success: true, userId: user.userId, email: user.email, fullName: user.fullName, isActive: user.isActive, isAdmin: user.isAdmin, isBot: user.isBot, }; } catch (error) { const err = error as Error; const duration = Date.now() - startTime; this.logger.error('获取用户信息失败', { operation: 'getUserInfo', email: request.email, error: err.message, duration, timestamp: new Date().toISOString(), }, err.stack); return { success: false, error: '系统错误,请稍后重试', }; } } /** * 验证用户凭据 * * 功能描述: * 验证用户的API Key是否有效 * * @param request 用户验证请求 * @returns Promise */ async validateUserCredentials(request: UserValidationRequest): Promise { const startTime = Date.now(); this.logger.log('开始验证用户凭据', { operation: 'validateUserCredentials', email: request.email, hasApiKey: !!request.apiKey, timestamp: new Date().toISOString(), }); try { // 1. 验证请求参数 if (!request.email || !this.isValidEmail(request.email)) { return { success: false, error: '邮箱格式无效', }; } if (!request.apiKey) { return { success: false, error: 'API Key不能为空', }; } // 2. 使用用户的API Key测试连接 const isValid = await this.testUserApiKey(request.email, request.apiKey); // 3. 如果API Key有效,获取用户ID let userId = undefined; if (isValid) { const userInfo = await this.getUserInfo({ email: request.email }); if (userInfo.success) { userId = userInfo.userId; } } const duration = Date.now() - startTime; this.logger.log('用户凭据验证完成', { operation: 'validateUserCredentials', email: request.email, isValid, userId, duration, timestamp: new Date().toISOString(), }); return { success: true, isValid, userId, }; } catch (error) { const err = error as Error; const duration = Date.now() - startTime; this.logger.error('验证用户凭据失败', { operation: 'validateUserCredentials', email: request.email, error: err.message, duration, timestamp: new Date().toISOString(), }, err.stack); return { success: false, error: '系统错误,请稍后重试', }; } } /** * 获取所有用户列表 * * 功能描述: * 从Zulip服务器获取所有用户的列表 * * @returns Promise */ async getAllUsers(): Promise { this.logger.debug('开始获取用户列表', { operation: 'getAllUsers', timestamp: new Date().toISOString(), }); try { // 获取Zulip配置 const config = this.configService.getZulipConfig(); // 构建API URL const apiUrl = `${config.zulipServerUrl}/api/v1/users`; // 构建认证头 const auth = Buffer.from(`${config.zulipBotEmail}:${config.zulipBotApiKey}`).toString('base64'); // 发送请求 const response = await fetch(apiUrl, { method: 'GET', headers: { 'Authorization': `Basic ${auth}`, 'Content-Type': 'application/json', }, }); if (!response.ok) { this.logger.warn('获取用户列表失败', { operation: 'getAllUsers', status: response.status, statusText: response.statusText, }); return { success: false, error: `API调用失败: ${response.status} ${response.statusText}`, }; } const data: ZulipUsersResponse = await response.json(); // 转换数据格式 const users = data.members?.map(user => ({ userId: user.user_id, email: user.email, fullName: user.full_name, isActive: user.is_active, isAdmin: user.is_admin, isBot: user.is_bot, })) || []; this.logger.debug('用户列表获取完成', { operation: 'getAllUsers', userCount: users.length, timestamp: new Date().toISOString(), }); return { success: true, users, totalCount: users.length, }; } catch (error) { const err = error as Error; this.logger.error('获取用户列表异常', { operation: 'getAllUsers', error: err.message, timestamp: new Date().toISOString(), }, err.stack); return { success: false, error: '系统错误,请稍后重试', }; } } /** * 测试用户API Key是否有效 * * 功能描述: * 使用用户的API Key测试是否能够成功调用Zulip API * * @param email 用户邮箱 * @param apiKey 用户API Key * @returns Promise 是否有效 * @private */ private async testUserApiKey(email: string, apiKey: string): Promise { this.logger.debug('测试用户API Key', { operation: 'testUserApiKey', email, }); try { // 获取Zulip配置 const config = this.configService.getZulipConfig(); // 构建API URL - 使用获取用户自己信息的接口 const apiUrl = `${config.zulipServerUrl}/api/v1/users/me`; // 使用用户的API Key构建认证头 const auth = Buffer.from(`${email}:${apiKey}`).toString('base64'); // 发送请求 const response = await fetch(apiUrl, { method: 'GET', headers: { 'Authorization': `Basic ${auth}`, 'Content-Type': 'application/json', }, }); const isValid = response.ok; this.logger.debug('API Key测试完成', { operation: 'testUserApiKey', email, isValid, status: response.status, }); return isValid; } catch (error) { const err = error as Error; this.logger.error('测试API Key异常', { operation: 'testUserApiKey', email, error: err.message, }); return false; } } /** * 验证邮箱格式 * * @param email 邮箱地址 * @returns boolean 是否有效 * @private */ private isValidEmail(email: string): boolean { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); } }