1e7c17f090
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m22s
- Refactor ActivateClubMembershipCommandHandler to use UserPackagePurchases instead of UserOrders for package activation. - Modify PlaceOrderCommandHandler to redirect payment callbacks to the front office. - Update ChargeDiscountWalletCommandHandler and ChargeMagicWalletCommandHandler to direct payment callbacks to the front office. - Remove PaymentCallbackController and integrate payment verification directly into DiscountOrderService and UserWalletService. - Add CustomerVerifyDiscountOrderPayment RPC to DiscountOrderService for verifying discount order payments. - Implement VerifyMagicCharge and VerifyDiscountCharge methods in UserWalletService for wallet charge verifications. - Update appsettings.json to use local URLs for development. - Remove appsettings.Development.json as it is no longer needed. - Comment out history tracking methods in ClubMembershipCycle and Package classes. - Update PackageService to automatically activate club membership after successful payment verification. - Adjust UserService to generate JWT tokens with user details.
433 lines
19 KiB
C#
433 lines
19 KiB
C#
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<CreateNewUserResponse> CreateNewUser(CreateNewUserRequest request, ServerCallContext context)
|
|
{
|
|
return await _dispatchRequestToCQRS.Handle<CreateNewUserRequest, CreateNewUserCommand, CreateNewUserResponse>(request, context);
|
|
}
|
|
public override async Task<Empty> UpdateUser(UpdateUserRequest request, ServerCallContext context)
|
|
{
|
|
return await _dispatchRequestToCQRS.Handle<UpdateUserRequest, UpdateUserCommand>(request, context);
|
|
}
|
|
public override async Task<Empty> DeleteUser(DeleteUserRequest request, ServerCallContext context)
|
|
{
|
|
return await _dispatchRequestToCQRS.Handle<DeleteUserRequest, DeleteUserCommand>(request, context);
|
|
}
|
|
public override async Task<GetUserResponse> GetUser(GetUserRequest request, ServerCallContext context)
|
|
{
|
|
// اگر Id ارسال نشده، از JWT بخون (برای کلاینت مشتری)
|
|
if (request.Id == 0)
|
|
request.Id = GetCurrentUserId();
|
|
|
|
return await _dispatchRequestToCQRS.Handle<GetUserRequest, GetUserQuery, GetUserResponse>(request, context);
|
|
}
|
|
public override async Task<GetAllUserByFilterResponse> GetAllUserByFilter(GetAllUserByFilterRequest request, ServerCallContext context)
|
|
{
|
|
return await _dispatchRequestToCQRS.Handle<GetAllUserByFilterRequest, GetAllUserByFilterQuery, GetAllUserByFilterResponse>(request, context);
|
|
}
|
|
public override async Task<GetJwtTokenResponse> GetJwtToken(GetJwtTokenRequest request, ServerCallContext context)
|
|
{
|
|
return await _dispatchRequestToCQRS.Handle<GetJwtTokenRequest, GetJwtTokenQuery, GetJwtTokenResponse>(request, context);
|
|
}
|
|
public override async Task<AdminGetJwtTokenResponse> AdminGetJwtToken(AdminGetJwtTokenRequest request, ServerCallContext context)
|
|
{
|
|
return await _dispatchRequestToCQRS.Handle<AdminGetJwtTokenRequest, AdminGetJwtTokenQuery, AdminGetJwtTokenResponse>(request, context);
|
|
}
|
|
public override async Task<Empty> SetPasswordForUser(SetPasswordForUserRequest request, ServerCallContext context)
|
|
{
|
|
return await _dispatchRequestToCQRS.Handle<SetPasswordForUserRequest, SetPasswordForUserCommand>(request, context);
|
|
}
|
|
public override async Task<RefreshTokenResponse> RefreshToken(RefreshTokenRequest request, ServerCallContext context)
|
|
{
|
|
return await _dispatchRequestToCQRS.Handle<RefreshTokenRequest, RefreshTokenCommand, RefreshTokenResponse>(request, context);
|
|
}
|
|
|
|
// ============= Customer-specific Methods =============
|
|
|
|
public override async Task<CreateNewOtpTokenResponse> CreateNewOtpToken(CreateNewOtpTokenRequest request, ServerCallContext context)
|
|
{
|
|
return await _dispatchRequestToCQRS.Handle<CreateNewOtpTokenRequest, CreateNewOtpTokenCommand, CreateNewOtpTokenResponse>(request, context);
|
|
}
|
|
|
|
public override async Task<VerifyOtpTokenResponse> VerifyOtpToken(VerifyOtpTokenRequest request, ServerCallContext context)
|
|
{
|
|
return await _dispatchRequestToCQRS.Handle<VerifyOtpTokenRequest, VerifyOtpTokenCommand, VerifyOtpTokenResponse>(request, context);
|
|
}
|
|
|
|
public override async Task<AcceptContractResponse> AcceptContract(AcceptContractRequest request, ServerCallContext context)
|
|
{
|
|
return await _dispatchRequestToCQRS.Handle<AcceptContractRequest, AcceptContractCommand, AcceptContractResponse>(request, context);
|
|
}
|
|
|
|
public override async Task<GetUserForCustomerResponse> 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<Empty> 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<GetCustomerProfileResponse> 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<ChangeCustomerPasswordResponse> 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<GetCustomerReferralsResponse> GetCustomerReferrals(GetCustomerReferralsRequest request, ServerCallContext context)
|
|
{
|
|
var query = new GetCustomerReferralsQuery
|
|
{
|
|
UserId = 0,
|
|
PaginationState = request.PaginationState?.Adapt<AppModels.PaginationState>(),
|
|
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<UploadCustomerAvatarResponse> 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<GetCustomerSettingsResponse> 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<Empty> 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, "لطفاً وارد حساب کاربری خود شوید"));
|
|
}
|
|
}
|