feat: integrate PYMS payment gateway, add blog/sitepage/image services, local file manager
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m44s

Payment Gateway:
- Add PYMSPaymentService: IPaymentGatewayService via gRPC to PYMS microservice
- Add ZarinPalPaymentService: direct ZarinPal integration (backup)
- Register 'pyms' payment provider in DI ConfigureServices
- Add PYMS proto files (pyms_transaction.proto, pyms_public_messages.proto)
- Fix VerifyDiscountWalletCharge: pass 'OK' as status instead of Authority
- Update appsettings: PaymentProvider=pyms, sandbox mode, merchant ID

Blog System:
- Add BlogCategory, BlogPost, BlogPostImage entities and CQRS
- Add proto files and gRPC services for blog management
- Add Mapster profiles for blog responses

Content Management:
- Add SitePage entity and CQRS for static pages
- Add proto and gRPC service for site pages

Image/File Management:
- Add LocalFileManager with disk storage + base64 serving + FMS fallback
- Add ImagePathResolverInterceptor for gRPC responses
- Add ImageResolverService for explicit image resolution
- Add UploadsController for public file serving with FMS fallback
- Add PaymentCallbackController for discount order payment callbacks

Database:
- Add blog and content entity migrations
- Remove ImagePath MaxLength constraints
- Remove old FileManagementService (replaced by LocalFileManager)
This commit is contained in:
masoodafar-web
2026-02-15 23:01:16 +03:30
parent 5a4e4a960d
commit 2502cbbda2
177 changed files with 16632 additions and 487 deletions
@@ -34,7 +34,7 @@ public class UserService : UserContract.UserContractBase
private readonly IApplicationDbContext _context;
private readonly ICurrentUserService _currentUserService;
private readonly IHashService _hashService;
private readonly IFileManagementService _fileManagementService;
private readonly CMSMicroservice.Application.Common.FileManager.IFileManager _fileManager;
public UserService(
IDispatchRequestToCQRS dispatchRequestToCQRS,
@@ -42,14 +42,14 @@ public class UserService : UserContract.UserContractBase
IApplicationDbContext context,
ICurrentUserService currentUserService,
IHashService hashService,
IFileManagementService fileManagementService)
CMSMicroservice.Application.Common.FileManager.IFileManager fileManager)
{
_dispatchRequestToCQRS = dispatchRequestToCQRS;
_sender = sender;
_context = context;
_currentUserService = currentUserService;
_hashService = hashService;
_fileManagementService = fileManagementService;
_fileManager = fileManager;
}
public override async Task<CreateNewUserResponse> CreateNewUser(CreateNewUserRequest request, ServerCallContext context)
{
@@ -65,6 +65,10 @@ public class UserService : UserContract.UserContractBase
}
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)
@@ -335,15 +339,28 @@ public class UserService : UserContract.UserContractBase
if (user == null)
throw new RpcException(new Status(StatusCode.NotFound, "کاربر یافت نشد"));
// Upload to FMS
// Upload to local file manager
var fileBytes = request.FileData.ToByteArray();
var fileName = $"avatar_{userId}_{DateTime.UtcNow.Ticks}";
var mime = request.FileMimeType ?? "image/jpeg";
var avatarUrl = await _fileManagementService.UploadFileAsync(
"Avatars", fileBytes, mime, fileName, context.CancellationToken);
if (string.IsNullOrEmpty(avatarUrl))
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
{
@@ -351,17 +368,6 @@ public class UserService : UserContract.UserContractBase
Message = "خطا در آپلود فایل. لطفاً مجدد تلاش کنید"
};
}
// Update user avatar path in DB
user.AvatarPath = avatarUrl;
await _context.SaveChangesAsync(context.CancellationToken);
return new UploadCustomerAvatarResponse
{
Success = true,
Message = "تصویر پروفایل با موفقیت آپلود شد",
AvatarUrl = avatarUrl
};
}
public override async Task<GetCustomerSettingsResponse> GetCustomerSettings(GetCustomerSettingsRequest request, ServerCallContext context)