using CMSMicroservice.Protobuf.Protos.User; using CMSMicroservice.Protobuf.Protos.City; using CMSMicroservice.WebApi.Common.Services; using CMSMicroservice.Application.UserCQ.Commands.CreateNewUser; using CMSMicroservice.Application.UserCQ.Commands.UpdateUser; using CMSMicroservice.Application.UserCQ.Commands.DeleteUser; using CMSMicroservice.Application.UserCQ.Queries.GetUser; using CMSMicroservice.Application.UserCQ.Queries.GetAllUserByFilter; using CMSMicroservice.Application.UserCQ.Queries.GetJwtToken; using CMSMicroservice.Application.UserCQ.Queries.AdminGetJwtToken; using CMSMicroservice.Application.UserCQ.Commands.SetPasswordForUser; using CMSMicroservice.Application.UserCQ.Commands.RefreshToken; using CMSMicroservice.Application.UserCQ.Commands.CreateNewOtpToken; using CMSMicroservice.Application.UserCQ.Commands.VerifyOtpToken; using CMSMicroservice.Application.UserCQ.Commands.AcceptContract; using CMSMicroservice.Application.UserCQ.Queries.GetCustomerProfile; using CMSMicroservice.Application.UserCQ.Queries.GetCustomerReferrals; using CMSMicroservice.Application.UserCQ.Queries.GetCustomerSettings; using CMSMicroservice.Application.Common.Interfaces; using Google.Protobuf.WellKnownTypes; using Microsoft.EntityFrameworkCore; using Grpc.Core; using System.Collections.Generic; using System.Linq; using MediatR; using Mapster; using AppModels = CMSMicroservice.Application.Common.Models; namespace CMSMicroservice.WebApi.Services; public class UserService : UserContract.UserContractBase { private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS; private readonly ISender _sender; private readonly IApplicationDbContext _context; private readonly ICurrentUserService _currentUserService; private readonly IHashService _hashService; private readonly CMSMicroservice.Application.Common.FileManager.IFileManager _fileManager; private readonly IGenerateJwtToken _generateJwt; public UserService( IDispatchRequestToCQRS dispatchRequestToCQRS, ISender sender, IApplicationDbContext context, ICurrentUserService currentUserService, IHashService hashService, CMSMicroservice.Application.Common.FileManager.IFileManager fileManager, IGenerateJwtToken generateJwt) { _dispatchRequestToCQRS = dispatchRequestToCQRS; _sender = sender; _context = context; _currentUserService = currentUserService; _hashService = hashService; _fileManager = fileManager; _generateJwt = generateJwt; } public override async Task CreateNewUser(CreateNewUserRequest request, ServerCallContext context) { return await _dispatchRequestToCQRS.Handle(request, context); } public override async Task UpdateUser(UpdateUserRequest request, ServerCallContext context) { return await _dispatchRequestToCQRS.Handle(request, context); } public override async Task DeleteUser(DeleteUserRequest request, ServerCallContext context) { return await _dispatchRequestToCQRS.Handle(request, context); } public override async Task GetUser(GetUserRequest request, ServerCallContext context) { // اگر Id ارسال نشده، از JWT بخون (برای کلاینت مشتری) if (request.Id == 0) request.Id = GetCurrentUserId(); return await _dispatchRequestToCQRS.Handle(request, context); } public override async Task GetAllUserByFilter(GetAllUserByFilterRequest request, ServerCallContext context) { return await _dispatchRequestToCQRS.Handle(request, context); } public override async Task GetJwtToken(GetJwtTokenRequest request, ServerCallContext context) { return await _dispatchRequestToCQRS.Handle(request, context); } public override async Task AdminGetJwtToken(AdminGetJwtTokenRequest request, ServerCallContext context) { return await _dispatchRequestToCQRS.Handle(request, context); } public override async Task SetPasswordForUser(SetPasswordForUserRequest request, ServerCallContext context) { return await _dispatchRequestToCQRS.Handle(request, context); } public override async Task RefreshToken(RefreshTokenRequest request, ServerCallContext context) { return await _dispatchRequestToCQRS.Handle(request, context); } // ============= Customer-specific Methods ============= public override async Task CreateNewOtpToken(CreateNewOtpTokenRequest request, ServerCallContext context) { return await _dispatchRequestToCQRS.Handle(request, context); } public override async Task VerifyOtpToken(VerifyOtpTokenRequest request, ServerCallContext context) { return await _dispatchRequestToCQRS.Handle(request, context); } public override async Task AcceptContract(AcceptContractRequest request, ServerCallContext context) { return await _dispatchRequestToCQRS.Handle(request, context); } public override async Task GetUserForCustomer(GetUserForCustomerRequest request, ServerCallContext context) { var userId = GetCurrentUserId(); var user = await _context.Users .AsNoTracking() .Include(u => u.UserContracts) .ThenInclude(uc => uc.Contract) .Include(u => u.UserRoles) .ThenInclude(ur => ur.Role) .Include(u => u.ClubMembership) .Where(u => u.Id == userId && !u.IsDeleted) .FirstOrDefaultAsync(context.CancellationToken); if (user == null) throw new RpcException(new Status(StatusCode.NotFound, "کاربر یافت نشد")); // تولید توکن JWT با آخرین اطلاعات کاربر var token = await _generateJwt.GenerateJwtToken(user); return new GetUserForCustomerResponse { Id = user.Id, FirstName = user.FirstName ?? string.Empty, LastName = user.LastName ?? string.Empty, Mobile = user.Mobile, Email = user.Email ?? string.Empty, NationalCode = user.NationalCode ?? string.Empty, AvatarPath = user.AvatarPath ?? string.Empty, ParentId = user.NetworkParentId, ReferralCode = user.ReferralCode ?? string.Empty, IsMobileVerified = user.IsMobileVerified, MobileVerifiedAt = user.MobileVerifiedAt.HasValue ? Timestamp.FromDateTime(DateTime.SpecifyKind(user.MobileVerifiedAt.Value, DateTimeKind.Utc)) : null, EmailNotifications = user.EmailNotifications, SmsNotifications = user.SmsNotifications, PushNotifications = user.PushNotifications, BirthDate = user.BirthDate.HasValue ? Timestamp.FromDateTime(DateTime.SpecifyKind(user.BirthDate.Value, DateTimeKind.Utc)) : null, Token = token }; } public override async Task UpdateCustomerProfile(UpdateCustomerProfileRequest request, ServerCallContext context) { var userId = GetCurrentUserId(); var user = await _context.Users .Where(u => u.Id == userId && !u.IsDeleted) .FirstOrDefaultAsync(context.CancellationToken); if (user == null) throw new RpcException(new Status(StatusCode.NotFound, "کاربر یافت نشد")); if (request.FirstName != null) user.FirstName = request.FirstName; if (request.LastName != null) user.LastName = request.LastName; if (request.Email != null) user.Email = request.Email; if (request.NationalCode != null) user.NationalCode = request.NationalCode; if (request.BirthDate != null) user.BirthDate = request.BirthDate.ToDateTime(); await _context.SaveChangesAsync(context.CancellationToken); return new Empty(); } public override async Task GetCustomerProfile(GetCustomerProfileRequest request, ServerCallContext context) { var query = new GetCustomerProfileQuery { UserId = 0 }; var result = await _sender.Send(query, context.CancellationToken); return new GetCustomerProfileResponse { Id = result.Id, FirstName = result.FirstName, LastName = result.LastName, Mobile = result.Mobile, Email = result.Email, NationalCode = result.NationalCode, AvatarPath = result.AvatarPath, ParentId = result.ParentId, ReferralCode = result.ReferralCode, IsMobileVerified = result.IsMobileVerified, MobileVerifiedAt = result.MobileVerifiedAt.HasValue ? Timestamp.FromDateTime(DateTime.SpecifyKind(result.MobileVerifiedAt.Value, DateTimeKind.Utc)) : null, EmailNotifications = result.EmailNotifications, SmsNotifications = result.SmsNotifications, PushNotifications = result.PushNotifications, BirthDate = result.BirthDate.HasValue ? Timestamp.FromDateTime(DateTime.SpecifyKind(result.BirthDate.Value, DateTimeKind.Utc)) : null, FullName = result.FullName, ProfileCompletionPercentage = result.ProfileCompletionPercentage }; } public override async Task ChangeCustomerPassword(ChangeCustomerPasswordRequest request, ServerCallContext context) { if (request.NewPassword != request.ConfirmPassword) { return new ChangeCustomerPasswordResponse { Success = false, Message = "رمز عبور جدید و تکرار آن یکسان نیستند" }; } if (request.NewPassword.Length < 6) { return new ChangeCustomerPasswordResponse { Success = false, Message = "رمز عبور باید حداقل 6 کاراکتر باشد" }; } var userId = GetCurrentUserId(); var user = await _context.Users .Where(u => u.Id == userId && !u.IsDeleted) .FirstOrDefaultAsync(context.CancellationToken); if (user == null) throw new RpcException(new Status(StatusCode.NotFound, "کاربر یافت نشد")); // Verify current password if (!string.IsNullOrEmpty(user.HashPassword)) { if (!_hashService.VerifyPassword(request.CurrentPassword, user.HashPassword)) { return new ChangeCustomerPasswordResponse { Success = false, Message = "رمز عبور فعلی نادرست است" }; } } // Hash and save new password user.HashPassword = _hashService.HashPassword(request.NewPassword); await _context.SaveChangesAsync(context.CancellationToken); return new ChangeCustomerPasswordResponse { Success = true, Message = "رمز عبور با موفقیت تغییر یافت" }; } public override async Task GetCustomerReferrals(GetCustomerReferralsRequest request, ServerCallContext context) { var query = new GetCustomerReferralsQuery { UserId = 0, PaginationState = request.PaginationState?.Adapt(), StatusFilter = request.StatusFilter }; var result = await _sender.Send(query, context.CancellationToken); var response = new GetCustomerReferralsResponse { MetaData = new MetaData { CurrentPage = result.MetaData.CurrentPage, TotalPage = result.MetaData.TotalPage, PageSize = result.MetaData.PageSize, TotalCount = result.MetaData.TotalCount, HasPrevious = result.MetaData.HasPrevious, HasNext = result.MetaData.HasNext }, Stats = new CMSMicroservice.Protobuf.Protos.User.CustomerReferralStats { TotalReferrals = result.Stats.TotalReferrals, ActiveReferrals = result.Stats.ActiveReferrals, TotalCommissionEarned = result.Stats.TotalCommissionEarned, ThisMonthCommission = result.Stats.ThisMonthCommission } }; foreach (var referral in result.Referrals) { response.Referrals.Add(new CMSMicroservice.Protobuf.Protos.User.CustomerReferralModel { Id = referral.Id, FirstName = referral.FirstName, LastName = referral.LastName, Mobile = referral.Mobile, JoinDate = Timestamp.FromDateTime(DateTime.SpecifyKind(referral.JoinDate, DateTimeKind.Utc)), IsActive = referral.IsActive, StatusMessage = referral.StatusMessage, Level = referral.Level, TotalCommission = referral.TotalCommission }); } return response; } public override async Task UploadCustomerAvatar(UploadCustomerAvatarRequest request, ServerCallContext context) { if (request.FileData == null || request.FileData.Length == 0) { return new UploadCustomerAvatarResponse { Success = false, Message = "فایل انتخاب نشده است" }; } if (request.FileData.Length > 5 * 1024 * 1024) // 5MB limit { return new UploadCustomerAvatarResponse { Success = false, Message = "حجم فایل نباید بیش از 5 مگابایت باشد" }; } var allowedTypes = new[] { "image/jpeg", "image/jpg", "image/png", "image/gif" }; if (!allowedTypes.Contains(request.FileMimeType?.ToLower())) { return new UploadCustomerAvatarResponse { Success = false, Message = "فرمت فایل مجاز نیست. فقط JPG, PNG و GIF مجاز هستند" }; } var userId = GetCurrentUserId(); var user = await _context.Users .Where(u => u.Id == userId && !u.IsDeleted) .FirstOrDefaultAsync(context.CancellationToken); if (user == null) throw new RpcException(new Status(StatusCode.NotFound, "کاربر یافت نشد")); // Upload to local file manager var fileBytes = request.FileData.ToByteArray(); var fileName = $"avatar_{userId}_{DateTime.UtcNow.Ticks}"; var mime = request.FileMimeType ?? "image/jpeg"; try { var result = await _fileManager.UploadImageAsync( "Avatars", fileBytes, mime, fileName, context.CancellationToken); // Update user avatar path in DB user.AvatarPath = result.Main.Path; await _context.SaveChangesAsync(context.CancellationToken); return new UploadCustomerAvatarResponse { Success = true, Message = "تصویر پروفایل با موفقیت آپلود شد", AvatarUrl = result.Main.Path }; } catch (Exception ex) { return new UploadCustomerAvatarResponse { Success = false, Message = "خطا در آپلود فایل. لطفاً مجدد تلاش کنید" }; } } public override async Task GetCustomerSettings(GetCustomerSettingsRequest request, ServerCallContext context) { var query = new GetCustomerSettingsQuery { UserId = 0 }; var result = await _sender.Send(query, context.CancellationToken); return new GetCustomerSettingsResponse { EmailNotifications = result.EmailNotifications, SmsNotifications = result.SmsNotifications, PushNotifications = result.PushNotifications, MarketingNotifications = result.MarketingNotifications, PreferredLanguage = result.PreferredLanguage, TimeZone = result.TimeZone, TwoFactorAuthEnabled = result.TwoFactorAuthEnabled }; } public override async Task UpdateCustomerSettings(UpdateCustomerSettingsRequest request, ServerCallContext context) { var userId = GetCurrentUserId(); var user = await _context.Users .Where(u => u.Id == userId && !u.IsDeleted) .FirstOrDefaultAsync(context.CancellationToken); if (user == null) throw new RpcException(new Status(StatusCode.NotFound, "کاربر یافت نشد")); user.EmailNotifications = request.EmailNotifications; user.SmsNotifications = request.SmsNotifications; user.PushNotifications = request.PushNotifications; // MarketingNotifications, PreferredLanguage, TimeZone, TwoFactorAuth // are stored at application level if needed in future await _context.SaveChangesAsync(context.CancellationToken); return new Empty(); } // ============= Helper Methods ============= private long GetCurrentUserId() { if (long.TryParse(_currentUserService.UserId, out var userId) && userId > 0) return userId; throw new RpcException(new Status(StatusCode.Unauthenticated, "لطفاً وارد حساب کاربری خود شوید")); } }