2025-12-02 03:30:36 +03:30
|
|
|
using CMSMicroservice.Application.DayaLoanCQ.Services;
|
|
|
|
|
using CMSMicroservice.Domain.Enums;
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
2025-12-06 21:02:51 +03:30
|
|
|
using Microsoft.Extensions.Configuration;
|
2025-12-02 03:30:36 +03:30
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Net.Http;
|
2025-12-06 21:02:51 +03:30
|
|
|
using System.Net.Http.Json;
|
2025-12-02 03:30:36 +03:30
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
2025-12-06 21:02:51 +03:30
|
|
|
using System.Text.Json.Serialization;
|
|
|
|
|
using System.Linq;
|
2025-12-02 03:30:36 +03:30
|
|
|
|
|
|
|
|
namespace CMSMicroservice.Infrastructure.Services;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Mock Implementation برای شبیهسازی Daya API
|
|
|
|
|
/// این کلاس فقط برای تست و توسعه است و باید با Implementation واقعی جایگزین شود
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class MockDayaLoanApiService : IDayaLoanApiService
|
|
|
|
|
{
|
|
|
|
|
private readonly ILogger<MockDayaLoanApiService> _logger;
|
|
|
|
|
|
|
|
|
|
public MockDayaLoanApiService(ILogger<MockDayaLoanApiService> logger)
|
|
|
|
|
{
|
|
|
|
|
_logger = logger;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<List<DayaLoanStatusResult>> CheckLoanStatusAsync(
|
|
|
|
|
List<string> nationalCodes,
|
|
|
|
|
CancellationToken cancellationToken = default)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning("⚠️ Using MOCK Daya API Service - Replace with real implementation!");
|
|
|
|
|
|
|
|
|
|
// شبیهسازی تاخیر شبکه
|
|
|
|
|
await Task.Delay(100, cancellationToken);
|
|
|
|
|
|
|
|
|
|
var results = new List<DayaLoanStatusResult>();
|
|
|
|
|
|
|
|
|
|
foreach (var nationalCode in nationalCodes)
|
|
|
|
|
{
|
|
|
|
|
// شبیهسازی: کدملیهایی که با 1 شروع میشوند وام گرفتهاند
|
|
|
|
|
if (nationalCode.StartsWith("1"))
|
|
|
|
|
{
|
|
|
|
|
results.Add(new DayaLoanStatusResult
|
|
|
|
|
{
|
|
|
|
|
NationalCode = nationalCode,
|
|
|
|
|
Status = DayaLoanStatus.PendingReceive,
|
|
|
|
|
ContractNumber = $"MOCK-DAYA-{nationalCode}-{DateTime.Now.Ticks}"
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// شبیهسازی: کدملیهایی که با 2 شروع میشوند رد شدهاند
|
|
|
|
|
else if (nationalCode.StartsWith("2"))
|
|
|
|
|
{
|
|
|
|
|
results.Add(new DayaLoanStatusResult
|
|
|
|
|
{
|
|
|
|
|
NationalCode = nationalCode,
|
|
|
|
|
Status = DayaLoanStatus.Rejected,
|
|
|
|
|
ContractNumber = null
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// بقیه: هنوز بررسی نشدهاند
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
results.Add(new DayaLoanStatusResult
|
|
|
|
|
{
|
|
|
|
|
NationalCode = nationalCode,
|
|
|
|
|
Status = DayaLoanStatus.PendingReceive,
|
|
|
|
|
ContractNumber = null // هنوز قرارداد صادر نشده
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("Mock Daya API returned {Count} results", results.Count);
|
|
|
|
|
return results;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Real Implementation برای API واقعی دایا
|
2025-12-06 21:02:51 +03:30
|
|
|
/// مبتنی بر مستند TA-DAYA-S10-G-MerchantServices
|
2025-12-02 03:30:36 +03:30
|
|
|
/// </summary>
|
|
|
|
|
public class DayaLoanApiService : IDayaLoanApiService
|
|
|
|
|
{
|
|
|
|
|
private readonly HttpClient _httpClient;
|
|
|
|
|
private readonly ILogger<DayaLoanApiService> _logger;
|
2025-12-06 21:02:51 +03:30
|
|
|
private readonly IConfiguration _configuration;
|
2025-12-02 03:30:36 +03:30
|
|
|
|
2025-12-06 21:02:51 +03:30
|
|
|
public DayaLoanApiService(
|
|
|
|
|
HttpClient httpClient,
|
|
|
|
|
ILogger<DayaLoanApiService> logger,
|
|
|
|
|
IConfiguration configuration)
|
2025-12-02 03:30:36 +03:30
|
|
|
{
|
|
|
|
|
_httpClient = httpClient;
|
|
|
|
|
_logger = logger;
|
2025-12-06 21:02:51 +03:30
|
|
|
_configuration = configuration;
|
|
|
|
|
|
|
|
|
|
// تنظیم Base Address از Configuration
|
|
|
|
|
var baseAddress = _configuration["DayaApi:BaseAddress"] ?? "https://testdaya.tadbirandishan.com";
|
|
|
|
|
_httpClient.BaseAddress = new Uri(baseAddress);
|
|
|
|
|
|
|
|
|
|
// تنظیم merchant-permission-key از Configuration
|
|
|
|
|
var permissionKey = _configuration["DayaApi:MerchantPermissionKey"];
|
|
|
|
|
if (!string.IsNullOrEmpty(permissionKey))
|
|
|
|
|
{
|
|
|
|
|
_httpClient.DefaultRequestHeaders.Add("merchant-permission-key", permissionKey);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning("⚠️ DayaApi:MerchantPermissionKey is not configured in appsettings!");
|
|
|
|
|
}
|
2025-12-02 03:30:36 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<List<DayaLoanStatusResult>> CheckLoanStatusAsync(
|
|
|
|
|
List<string> nationalCodes,
|
|
|
|
|
CancellationToken cancellationToken = default)
|
|
|
|
|
{
|
2025-12-06 21:02:51 +03:30
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("Calling Daya API for {Count} national codes", nationalCodes.Count);
|
|
|
|
|
|
|
|
|
|
// ساخت Request Body
|
|
|
|
|
var requestBody = new DayaContractsRequest
|
|
|
|
|
{
|
|
|
|
|
NationalCodes = nationalCodes
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// فراخوانی API
|
|
|
|
|
var response = await _httpClient.PostAsJsonAsync(
|
|
|
|
|
"/api/merchant/contracts",
|
|
|
|
|
requestBody,
|
|
|
|
|
cancellationToken);
|
|
|
|
|
|
|
|
|
|
// خواندن پاسخ
|
|
|
|
|
var apiResponse = await response.Content.ReadFromJsonAsync<DayaContractsResponse>(cancellationToken);
|
|
|
|
|
|
|
|
|
|
// بررسی موفقیت
|
|
|
|
|
if (apiResponse == null)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogError("Daya API returned null response");
|
|
|
|
|
return CreateEmptyResults(nationalCodes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!apiResponse.Succeed)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning("Daya API call failed. Code: {Code}, Message: {Message}",
|
|
|
|
|
apiResponse.Code, apiResponse.Message);
|
|
|
|
|
return CreateEmptyResults(nationalCodes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (apiResponse.Data == null || !apiResponse.Data.Any())
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("Daya API returned no contract data");
|
|
|
|
|
return CreateEmptyResults(nationalCodes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// تبدیل نتایج API به مدل داخلی
|
|
|
|
|
var results = MapApiResponseToResults(apiResponse.Data, nationalCodes);
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("Daya API returned {Count} contracts for {RequestCount} national codes",
|
|
|
|
|
results.Count(r => !string.IsNullOrEmpty(r.ContractNumber)),
|
|
|
|
|
nationalCodes.Count);
|
|
|
|
|
|
|
|
|
|
return results;
|
|
|
|
|
}
|
|
|
|
|
catch (HttpRequestException ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogError(ex, "HTTP error calling Daya API. Status: {StatusCode}", ex.StatusCode);
|
|
|
|
|
return CreateEmptyResults(nationalCodes);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogError(ex, "Unexpected error calling Daya API");
|
|
|
|
|
return CreateEmptyResults(nationalCodes);
|
|
|
|
|
}
|
2025-12-02 03:30:36 +03:30
|
|
|
}
|
2025-12-06 21:02:51 +03:30
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// تبدیل پاسخ API دایا به مدل داخلی
|
|
|
|
|
/// </summary>
|
|
|
|
|
private List<DayaLoanStatusResult> MapApiResponseToResults(
|
|
|
|
|
List<DayaContractData> apiData,
|
|
|
|
|
List<string> requestedNationalCodes)
|
|
|
|
|
{
|
|
|
|
|
var results = new List<DayaLoanStatusResult>();
|
|
|
|
|
|
|
|
|
|
// برای هر کد ملی درخواستی
|
|
|
|
|
foreach (var nationalCode in requestedNationalCodes)
|
|
|
|
|
{
|
|
|
|
|
// پیدا کردن آخرین قرارداد این کد ملی (براساس تاریخ)
|
|
|
|
|
var latestContract = apiData
|
|
|
|
|
.Where(d => d.NationalCode == nationalCode)
|
|
|
|
|
.OrderByDescending(d => d.DateTime)
|
|
|
|
|
.FirstOrDefault();
|
|
|
|
|
|
|
|
|
|
if (latestContract != null)
|
|
|
|
|
{
|
|
|
|
|
// تعیین وضعیت براساس StatusDescription
|
|
|
|
|
var status = MapStatusDescription(latestContract.StatusDescription);
|
|
|
|
|
|
|
|
|
|
results.Add(new DayaLoanStatusResult
|
|
|
|
|
{
|
|
|
|
|
NationalCode = nationalCode,
|
|
|
|
|
Status = status,
|
|
|
|
|
ContractNumber = latestContract.ApplicationNo
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
_logger.LogDebug("Daya contract found for {NationalCode}: {ContractNo}, Status: {Status}",
|
|
|
|
|
nationalCode, latestContract.ApplicationNo, latestContract.StatusDescription);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// هیچ قراردادی برای این کد ملی پیدا نشد
|
|
|
|
|
results.Add(new DayaLoanStatusResult
|
|
|
|
|
{
|
|
|
|
|
NationalCode = nationalCode,
|
|
|
|
|
Status = DayaLoanStatus.NotRequested,
|
|
|
|
|
ContractNumber = null
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return results;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// تبدیل StatusDescription دایا به Enum داخلی
|
|
|
|
|
/// </summary>
|
|
|
|
|
private DayaLoanStatus MapStatusDescription(string statusDescription)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(statusDescription))
|
|
|
|
|
return DayaLoanStatus.NotRequested;
|
|
|
|
|
|
|
|
|
|
// براساس مستند دایا: "فعال شده (در انتظار تسویه)" = وام تایید شده
|
|
|
|
|
if (statusDescription.Contains("فعال شده") || statusDescription.Contains("در انتظار تسویه"))
|
|
|
|
|
return DayaLoanStatus.PendingReceive;
|
|
|
|
|
|
|
|
|
|
if (statusDescription.Contains("رد شده") || statusDescription.Contains("رد"))
|
|
|
|
|
return DayaLoanStatus.Rejected;
|
|
|
|
|
|
|
|
|
|
if (statusDescription.Contains("بررسی") || statusDescription.Contains("در حال"))
|
|
|
|
|
return DayaLoanStatus.UnderReview;
|
|
|
|
|
|
|
|
|
|
// پیشفرض
|
|
|
|
|
return DayaLoanStatus.NotRequested;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// ساخت نتایج خالی در صورت خطا
|
|
|
|
|
/// </summary>
|
|
|
|
|
private List<DayaLoanStatusResult> CreateEmptyResults(List<string> nationalCodes)
|
|
|
|
|
{
|
|
|
|
|
return nationalCodes.Select(nc => new DayaLoanStatusResult
|
|
|
|
|
{
|
|
|
|
|
NationalCode = nc,
|
|
|
|
|
Status = DayaLoanStatus.NotRequested,
|
|
|
|
|
ContractNumber = null
|
|
|
|
|
}).ToList();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region Daya API Models
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Request body برای /api/merchant/contracts
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal class DayaContractsRequest
|
|
|
|
|
{
|
|
|
|
|
[JsonPropertyName("NationalCodes")]
|
|
|
|
|
public List<string> NationalCodes { get; set; } = new();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Response از /api/merchant/contracts
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal class DayaContractsResponse
|
|
|
|
|
{
|
|
|
|
|
[JsonPropertyName("succeed")]
|
|
|
|
|
public bool Succeed { get; set; }
|
|
|
|
|
|
|
|
|
|
[JsonPropertyName("code")]
|
|
|
|
|
public int Code { get; set; }
|
|
|
|
|
|
|
|
|
|
[JsonPropertyName("message")]
|
|
|
|
|
public string? Message { get; set; }
|
|
|
|
|
|
|
|
|
|
[JsonPropertyName("data")]
|
|
|
|
|
public List<DayaContractData>? Data { get; set; }
|
2025-12-02 03:30:36 +03:30
|
|
|
}
|
2025-12-06 21:02:51 +03:30
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// دادههای قرارداد در پاسخ API
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal class DayaContractData
|
|
|
|
|
{
|
|
|
|
|
[JsonPropertyName("nationalCode")]
|
|
|
|
|
public string NationalCode { get; set; } = string.Empty;
|
|
|
|
|
|
|
|
|
|
[JsonPropertyName("applicationNo")]
|
|
|
|
|
public string ApplicationNo { get; set; } = string.Empty;
|
|
|
|
|
|
|
|
|
|
[JsonPropertyName("statusDescription")]
|
|
|
|
|
public string StatusDescription { get; set; } = string.Empty;
|
|
|
|
|
|
|
|
|
|
[JsonPropertyName("dateTime")]
|
|
|
|
|
public DateTime DateTime { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|