Compare commits
43 Commits
Feature/co
...
Feature/In
| Author | SHA1 | Date | |
|---|---|---|---|
| f112bab021 | |||
| 8b39eed7bb | |||
| 07851ff7c8 | |||
| 07b2596a6a | |||
| 7dce7f5bc8 | |||
| 7ac078c631 | |||
| 265d5f8b22 | |||
| cf5c9f29cf | |||
| 7448ddc79c | |||
| 3f1a6f3387 | |||
| 95d4dfe568 | |||
| 3258deeb2c | |||
| 783fffa0c2 | |||
| 16c29bc2d0 | |||
|
|
57ef47473b | ||
|
|
e00c93b23d | ||
| 8bd248c6a7 | |||
| 7f9531f07b | |||
| 0b439d0268 | |||
|
|
861b214b0c | ||
|
|
690f574240 | ||
|
|
05abc67cdd | ||
| 3282825719 | |||
| cd1cf93ad1 | |||
| adef1fc15a | |||
| 597f54c062 | |||
| c0ead0a917 | |||
| 59284ffd29 | |||
| 5c7dd76e3f | |||
| dff6bc2541 | |||
| 22b67b344a | |||
|
|
d8b432ca1e | ||
| f4853b6f39 | |||
| 6ab418c4ab | |||
|
|
74dca1d2d2 | ||
|
|
a9b4eb6195 | ||
|
|
2125e15fb9 | ||
| b7f7d3b223 | |||
| 752c7c72ab | |||
| 73da938bc9 | |||
| 08574b5bb5 | |||
| 288e3a8988 | |||
| 026d8da74a |
@@ -0,0 +1,25 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace _0_Framework.Application.FaceEmbedding;
|
||||
|
||||
/// <summary>
|
||||
/// سرویس اطلاعرسانی تغییرات Face Embedding
|
||||
/// </summary>
|
||||
public interface IFaceEmbeddingNotificationService
|
||||
{
|
||||
/// <summary>
|
||||
/// اطلاعرسانی ایجاد یا بهروزرسانی Embedding
|
||||
/// </summary>
|
||||
Task NotifyEmbeddingCreatedAsync(long workshopId, long employeeId, string employeeFullName);
|
||||
|
||||
/// <summary>
|
||||
/// اطلاعرسانی حذف Embedding
|
||||
/// </summary>
|
||||
Task NotifyEmbeddingDeletedAsync(long workshopId, long employeeId);
|
||||
|
||||
/// <summary>
|
||||
/// اطلاعرسانی بهبود Embedding
|
||||
/// </summary>
|
||||
Task NotifyEmbeddingRefinedAsync(long workshopId, long employeeId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace _0_Framework.Application.FaceEmbedding;
|
||||
|
||||
public interface IFaceEmbeddingService
|
||||
{
|
||||
Task<OperationResult> GenerateEmbeddingsAsync(long employeeId, long workshopId, string employeeFullName, string picture1Path, string picture2Path);
|
||||
Task<OperationResult> GenerateEmbeddingsFromStreamAsync(long employeeId, long workshopId, string employeeFullName, Stream picture1Stream, Stream picture2Stream);
|
||||
Task<OperationResult> RefineEmbeddingAsync(long employeeId, long workshopId, float[] embedding, float confidence, Dictionary<string, object> metadata = null);
|
||||
Task<OperationResult> DeleteEmbeddingAsync(long employeeId, long workshopId);
|
||||
Task<OperationResult<FaceEmbeddingResponse>> GetEmbeddingAsync(long employeeId, long workshopId);
|
||||
}
|
||||
|
||||
public class FaceEmbeddingResponse
|
||||
{
|
||||
public long EmployeeId { get; set; }
|
||||
public long WorkshopId { get; set; }
|
||||
public string EmployeeFullName { get; set; }
|
||||
public float[] Embedding { get; set; }
|
||||
public float Confidence { get; set; }
|
||||
public Dictionary<string, object> Metadata { get; set; }
|
||||
}
|
||||
@@ -29,9 +29,11 @@ public class PaymentGatewayResponse
|
||||
public int? ErrorCode { get; set; }
|
||||
|
||||
[JsonPropertyName("transid")]
|
||||
public string TransactionId { get; set; }
|
||||
public string Token { get; set; }
|
||||
|
||||
public bool IsSuccess => Status == "success";
|
||||
public bool IsSuccess { get; set; }
|
||||
|
||||
public string Message { get; set; }
|
||||
}
|
||||
|
||||
public class WalletAmountResponse
|
||||
@@ -53,10 +55,12 @@ public class CreatePaymentGatewayRequest
|
||||
public string Mobile { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string Description { get; set; }
|
||||
public IDictionary<string, object> ExtraData { get; set; }
|
||||
}
|
||||
|
||||
public class VerifyPaymentGateWayRequest
|
||||
{
|
||||
public string TransactionId { get; set; }
|
||||
public string DigitalReceipt { get; set; }
|
||||
public string TransactionId { get; set; }
|
||||
public double Amount { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace _0_Framework.Application.PaymentGateway;
|
||||
|
||||
public class SepehrPaymentGateway:IPaymentGateway
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private const long TerminalId = 99213700;
|
||||
|
||||
public SepehrPaymentGateway(IHttpClientFactory httpClient)
|
||||
{
|
||||
_httpClient = httpClient.CreateClient();
|
||||
_httpClient.BaseAddress = new Uri("https://sepehr.shaparak.ir/Rest/V1/PeymentApi/");
|
||||
|
||||
|
||||
}
|
||||
|
||||
public async Task<PaymentGatewayResponse> Create(CreatePaymentGatewayRequest command, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var extraData = JsonConvert.SerializeObject(command.ExtraData);
|
||||
var res = await _httpClient.PostAsJsonAsync("GetToken", new
|
||||
{
|
||||
TerminalID = TerminalId,
|
||||
Amount = command.Amount,
|
||||
InvoiceID = command.InvoiceId,
|
||||
callbackURL = command.CallBackUrl,
|
||||
payload = extraData
|
||||
}, cancellationToken: cancellationToken);
|
||||
// خواندن محتوای پاسخ
|
||||
var content = await res.Content.ReadAsStringAsync(cancellationToken);
|
||||
|
||||
// تبدیل پاسخ JSON به آبجکت داتنت
|
||||
var json = System.Text.Json.JsonDocument.Parse(content);
|
||||
|
||||
// گرفتن مقدار AccessToken
|
||||
var accessToken = json.RootElement.GetProperty("Accesstoken").ToString();
|
||||
var status = json.RootElement.GetProperty("Status").ToString();
|
||||
|
||||
return new PaymentGatewayResponse
|
||||
{
|
||||
Status = status,
|
||||
IsSuccess = status == "0",
|
||||
Token = accessToken
|
||||
};
|
||||
}
|
||||
|
||||
public string GetStartPayUrl(string token)=>
|
||||
$"https://sepehr.shaparak.ir/Payment/Pay?token={token}&terminalId={TerminalId}";
|
||||
|
||||
public async Task<PaymentGatewayResponse> Verify(VerifyPaymentGateWayRequest command, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var res = await _httpClient.PostAsJsonAsync("Advice", new
|
||||
{
|
||||
digitalreceipt = command.DigitalReceipt,
|
||||
Tid = TerminalId,
|
||||
}, cancellationToken: cancellationToken);
|
||||
|
||||
// خواندن محتوای پاسخ
|
||||
var content = await res.Content.ReadAsStringAsync(cancellationToken);
|
||||
|
||||
// تبدیل پاسخ JSON به آبجکت داتنت
|
||||
var json = System.Text.Json.JsonDocument.Parse(content);
|
||||
|
||||
|
||||
var message = json.RootElement.GetProperty("Message").GetString();
|
||||
var status = json.RootElement.GetProperty("Status").GetString();
|
||||
return new PaymentGatewayResponse
|
||||
{
|
||||
Status = status,
|
||||
IsSuccess = status.ToLower() == "ok",
|
||||
Message = message
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public Task<PaymentGatewayResponse> CreateSandBox(CreatePaymentGatewayRequest command, CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public string GetStartPaySandBoxUrl(string transactionId)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<WalletAmountResponse> GetWalletAmount(CancellationToken cancellationToken)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ public interface ISmsService
|
||||
|
||||
Task<double> GetCreditAmount();
|
||||
|
||||
public Task<bool> SendInstitutionVerificationLink(string number, string fullName, Guid institutionId, long contractingPartyId, long institutionContractId);
|
||||
public Task<bool> SendInstitutionCreationVerificationLink(string number, string fullName, Guid institutionId, long contractingPartyId, long institutionContractId);
|
||||
|
||||
public Task<bool> SendInstitutionVerificationCode(string number, string code, string contractingPartyFullName,
|
||||
long contractingPartyId, long institutionContractId);
|
||||
|
||||
346
0_Framework/InfraStructure/FaceEmbeddingService.cs
Normal file
346
0_Framework/InfraStructure/FaceEmbeddingService.cs
Normal file
@@ -0,0 +1,346 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using _0_Framework.Application;
|
||||
using _0_Framework.Application.FaceEmbedding;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace _0_Framework.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// پیادهسازی سرویس ارتباط با API پایتون برای مدیریت Embeddings چهره
|
||||
/// </summary>
|
||||
public class FaceEmbeddingService : IFaceEmbeddingService
|
||||
{
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly ILogger<FaceEmbeddingService> _logger;
|
||||
private readonly IFaceEmbeddingNotificationService _notificationService;
|
||||
private readonly string _apiBaseUrl;
|
||||
|
||||
public FaceEmbeddingService(IHttpClientFactory httpClientFactory, ILogger<FaceEmbeddingService> logger,
|
||||
IFaceEmbeddingNotificationService notificationService = null)
|
||||
{
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_logger = logger;
|
||||
_notificationService = notificationService;
|
||||
_apiBaseUrl = "http://localhost:8000";
|
||||
}
|
||||
|
||||
public async Task<OperationResult> GenerateEmbeddingsAsync(long employeeId, long workshopId,
|
||||
string employeeFullName, string picture1Path, string picture2Path)
|
||||
{
|
||||
try
|
||||
{
|
||||
var httpClient = _httpClientFactory.CreateClient();
|
||||
httpClient.BaseAddress = new Uri(_apiBaseUrl);
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(30);
|
||||
|
||||
using var content = new MultipartFormDataContent();
|
||||
|
||||
// Add form fields
|
||||
content.Add(new StringContent(employeeId.ToString()), "employee_id");
|
||||
content.Add(new StringContent(workshopId.ToString()), "workshop_id");
|
||||
content.Add(new StringContent(employeeFullName ?? ""), "employee_full_name");
|
||||
|
||||
// Add picture files
|
||||
if (File.Exists(picture1Path))
|
||||
{
|
||||
var picture1Bytes = await File.ReadAllBytesAsync(picture1Path);
|
||||
var picture1Content = new ByteArrayContent(picture1Bytes);
|
||||
picture1Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
|
||||
content.Add(picture1Content, "picture1", "1.jpg");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Picture1 not found at path: {Path}", picture1Path);
|
||||
return new OperationResult { IsSuccedded = false, Message = "تصویر اول یافت نشد" };
|
||||
}
|
||||
|
||||
if (File.Exists(picture2Path))
|
||||
{
|
||||
var picture2Bytes = await File.ReadAllBytesAsync(picture2Path);
|
||||
var picture2Content = new ByteArrayContent(picture2Bytes);
|
||||
picture2Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
|
||||
content.Add(picture2Content, "picture2", "2.jpg");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Picture2 not found at path: {Path}", picture2Path);
|
||||
return new OperationResult { IsSuccedded = false, Message = "تصویر دوم یافت نشد" };
|
||||
}
|
||||
|
||||
// Send request to Python API
|
||||
var response = await httpClient.PostAsync("embeddings", content);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
_logger.LogInformation("Embeddings generated successfully for Employee {EmployeeId}, Workshop {WorkshopId}",
|
||||
employeeId, workshopId);
|
||||
|
||||
// ارسال اطلاعرسانی به سایر سیستمها
|
||||
if (_notificationService != null)
|
||||
{
|
||||
await _notificationService.NotifyEmbeddingCreatedAsync(workshopId, employeeId, employeeFullName);
|
||||
}
|
||||
|
||||
return new OperationResult
|
||||
{
|
||||
IsSuccedded = true,
|
||||
Message = "Embedding با موفقیت ایجاد شد"
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
var errorContent = await response.Content.ReadAsStringAsync();
|
||||
_logger.LogError("Failed to generate embeddings. Status: {StatusCode}, Error: {Error}",
|
||||
response.StatusCode, errorContent);
|
||||
|
||||
return new OperationResult
|
||||
{
|
||||
IsSuccedded = false,
|
||||
Message = $"خطا در تولید Embedding: {response.StatusCode}"
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
_logger.LogError(ex, "HTTP error while calling embeddings API for Employee {EmployeeId}", employeeId);
|
||||
return new OperationResult
|
||||
{
|
||||
IsSuccedded = false,
|
||||
Message = "خطا در ارتباط با سرور Embedding"
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error while calling embeddings API for Employee {EmployeeId}", employeeId);
|
||||
return new OperationResult
|
||||
{
|
||||
IsSuccedded = false,
|
||||
Message = "خطای غیرمنتظره در تولید Embedding"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<OperationResult> GenerateEmbeddingsFromStreamAsync(long employeeId, long workshopId,
|
||||
string employeeFullName, Stream picture1Stream, Stream picture2Stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
var httpClient = _httpClientFactory.CreateClient();
|
||||
httpClient.BaseAddress = new Uri(_apiBaseUrl);
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(30);
|
||||
|
||||
using var content = new MultipartFormDataContent();
|
||||
|
||||
// Add form fields
|
||||
content.Add(new StringContent(employeeId.ToString()), "employee_id");
|
||||
content.Add(new StringContent(workshopId.ToString()), "workshop_id");
|
||||
content.Add(new StringContent(employeeFullName ?? ""), "employee_full_name");
|
||||
|
||||
// Add picture streams
|
||||
var picture1Content = new StreamContent(picture1Stream);
|
||||
picture1Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
|
||||
content.Add(picture1Content, "picture1", "1.jpg");
|
||||
|
||||
var picture2Content = new StreamContent(picture2Stream);
|
||||
picture2Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
|
||||
content.Add(picture2Content, "picture2", "2.jpg");
|
||||
|
||||
// Send request to Python API
|
||||
var response = await httpClient.PostAsync("embeddings", content);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
_logger.LogInformation("Embeddings generated successfully from streams for Employee {EmployeeId}", employeeId);
|
||||
|
||||
// ارسال اطلاعرسانی به سایر سیستمها
|
||||
if (_notificationService != null)
|
||||
{
|
||||
await _notificationService.NotifyEmbeddingCreatedAsync(workshopId, employeeId, employeeFullName);
|
||||
}
|
||||
|
||||
return new OperationResult { IsSuccedded = true, Message = "Embedding با موفقیت ایجاد شد" };
|
||||
}
|
||||
else
|
||||
{
|
||||
var errorContent = await response.Content.ReadAsStringAsync();
|
||||
_logger.LogError("Failed to generate embeddings from streams. Status: {StatusCode}, Error: {Error}",
|
||||
response.StatusCode, errorContent);
|
||||
|
||||
return new OperationResult
|
||||
{
|
||||
IsSuccedded = false,
|
||||
Message = $"خطا در تولید Embedding: {response.StatusCode}"
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error while generating embeddings from streams for Employee {EmployeeId}", employeeId);
|
||||
return new OperationResult
|
||||
{
|
||||
IsSuccedded = false,
|
||||
Message = "خطا در تولید Embedding"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<OperationResult> RefineEmbeddingAsync(long employeeId, long workshopId, float[] embedding,
|
||||
float confidence, Dictionary<string, object> metadata = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var httpClient = _httpClientFactory.CreateClient();
|
||||
httpClient.BaseAddress = new Uri(_apiBaseUrl);
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(30);
|
||||
|
||||
var requestBody = new
|
||||
{
|
||||
employeeId,
|
||||
workshopId,
|
||||
embedding,
|
||||
confidence,
|
||||
metadata = metadata ?? new Dictionary<string, object>()
|
||||
};
|
||||
|
||||
var response = await httpClient.PostAsJsonAsync("embeddings/refine", requestBody);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
_logger.LogInformation("Embedding refined successfully for Employee {EmployeeId}", employeeId);
|
||||
|
||||
// ارسال اطلاعرسانی به سایر سیستمها
|
||||
if (_notificationService != null)
|
||||
{
|
||||
await _notificationService.NotifyEmbeddingRefinedAsync(workshopId, employeeId);
|
||||
}
|
||||
|
||||
return new OperationResult { IsSuccedded = true, Message = "Embedding بهبود یافت" };
|
||||
}
|
||||
else
|
||||
{
|
||||
var errorContent = await response.Content.ReadAsStringAsync();
|
||||
_logger.LogError("Failed to refine embedding. Status: {StatusCode}, Error: {Error}",
|
||||
response.StatusCode, errorContent);
|
||||
|
||||
return new OperationResult
|
||||
{
|
||||
IsSuccedded = false,
|
||||
Message = $"خطا در بهبود Embedding: {response.StatusCode}"
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error while refining embedding for Employee {EmployeeId}", employeeId);
|
||||
return new OperationResult
|
||||
{
|
||||
IsSuccedded = false,
|
||||
Message = "خطا در بهبود Embedding"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<OperationResult> DeleteEmbeddingAsync(long employeeId, long workshopId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var httpClient = _httpClientFactory.CreateClient();
|
||||
httpClient.BaseAddress = new Uri(_apiBaseUrl);
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(30);
|
||||
|
||||
var response = await httpClient.DeleteAsync($"embeddings/{workshopId}/{employeeId}");
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
_logger.LogInformation("Embedding deleted successfully for Employee {EmployeeId}", employeeId);
|
||||
|
||||
// ارسال اطلاعرسانی به سایر سیستمها
|
||||
if (_notificationService != null)
|
||||
{
|
||||
await _notificationService.NotifyEmbeddingDeletedAsync(workshopId, employeeId);
|
||||
}
|
||||
|
||||
return new OperationResult { IsSuccedded = true, Message = "Embedding حذف شد" };
|
||||
}
|
||||
else
|
||||
{
|
||||
var errorContent = await response.Content.ReadAsStringAsync();
|
||||
_logger.LogError("Failed to delete embedding. Status: {StatusCode}, Error: {Error}",
|
||||
response.StatusCode, errorContent);
|
||||
|
||||
return new OperationResult
|
||||
{
|
||||
IsSuccedded = false,
|
||||
Message = $"خطا در حذف Embedding: {response.StatusCode}"
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error while deleting embedding for Employee {EmployeeId}", employeeId);
|
||||
return new OperationResult
|
||||
{
|
||||
IsSuccedded = false,
|
||||
Message = "خطا در حذف Embedding"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<OperationResult<FaceEmbeddingResponse>> GetEmbeddingAsync(long employeeId, long workshopId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var httpClient = _httpClientFactory.CreateClient();
|
||||
httpClient.BaseAddress = new Uri(_apiBaseUrl);
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(30);
|
||||
|
||||
var response = await httpClient.GetAsync($"embeddings/{workshopId}/{employeeId}");
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
var embeddingData = JsonSerializer.Deserialize<FaceEmbeddingResponse>(content,
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
|
||||
_logger.LogInformation("Embedding retrieved successfully for Employee {EmployeeId}", employeeId);
|
||||
|
||||
return new OperationResult<FaceEmbeddingResponse>
|
||||
{
|
||||
IsSuccedded = true,
|
||||
Message = "Embedding دریافت شد",
|
||||
Data = embeddingData
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
var errorContent = await response.Content.ReadAsStringAsync();
|
||||
_logger.LogError("Failed to get embedding. Status: {StatusCode}, Error: {Error}",
|
||||
response.StatusCode, errorContent);
|
||||
|
||||
return new OperationResult<FaceEmbeddingResponse>
|
||||
{
|
||||
IsSuccedded = false,
|
||||
Message = $"خطا در دریافت Embedding: {response.StatusCode}"
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error while getting embedding for Employee {EmployeeId}", employeeId);
|
||||
return new OperationResult<FaceEmbeddingResponse>
|
||||
{
|
||||
IsSuccedded = false,
|
||||
Message = "خطا در دریافت Embedding"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.Threading.Tasks;
|
||||
using _0_Framework.Application.FaceEmbedding;
|
||||
|
||||
namespace _0_Framework.InfraStructure;
|
||||
|
||||
/// <summary>
|
||||
/// پیادهسازی پیشفرض (بدون عملیات) برای IFaceEmbeddingNotificationService
|
||||
/// این کلاس زمانی استفاده میشود که SignalR در دسترس نباشد
|
||||
/// </summary>
|
||||
public class NullFaceEmbeddingNotificationService : IFaceEmbeddingNotificationService
|
||||
{
|
||||
public Task NotifyEmbeddingCreatedAsync(long workshopId, long employeeId, string employeeFullName)
|
||||
{
|
||||
// هیچ عملیاتی انجام نمیدهد
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task NotifyEmbeddingDeletedAsync(long workshopId, long employeeId)
|
||||
{
|
||||
// هیچ عملیاتی انجام نمیدهد
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task NotifyEmbeddingRefinedAsync(long workshopId, long employeeId)
|
||||
{
|
||||
// هیچ عملیاتی انجام نمیدهد
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
624
ANDROID_SIGNALR_GUIDE.md
Normal file
624
ANDROID_SIGNALR_GUIDE.md
Normal file
@@ -0,0 +1,624 @@
|
||||
# راهنمای اتصال اپلیکیشن Android به SignalR برای Face Embedding
|
||||
|
||||
## 1. افزودن کتابخانه SignalR به پروژه Android
|
||||
|
||||
در فایل `build.gradle` (Module: app) خود، dependency زیر را اضافه کنید:
|
||||
|
||||
```gradle
|
||||
dependencies {
|
||||
// SignalR for Android
|
||||
implementation 'com.microsoft.signalr:signalr:7.0.0'
|
||||
|
||||
// اگر از Kotlin استفاده میکنید:
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1'
|
||||
|
||||
// برای JSON پردازش:
|
||||
implementation 'com.google.code.gson:gson:2.10.1'
|
||||
}
|
||||
```
|
||||
|
||||
## 2. اضافه کردن Permission در AndroidManifest.xml
|
||||
|
||||
```xml
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
```
|
||||
|
||||
## 3. کد Java/Kotlin برای اتصال به SignalR
|
||||
|
||||
### نسخه Java:
|
||||
|
||||
```java
|
||||
import com.microsoft.signalr.HubConnection;
|
||||
import com.microsoft.signalr.HubConnectionBuilder;
|
||||
import com.microsoft.signalr.HubConnectionState;
|
||||
import com.google.gson.JsonObject;
|
||||
import android.util.Log;
|
||||
|
||||
public class FaceEmbeddingSignalRClient {
|
||||
private static final String TAG = "FaceEmbeddingHub";
|
||||
private HubConnection hubConnection;
|
||||
private String serverUrl = "http://YOUR_SERVER_IP:PORT/trackingFaceEmbeddingHub"; // آدرس سرور خود را وارد کنید
|
||||
private long workshopId;
|
||||
|
||||
public FaceEmbeddingSignalRClient(long workshopId) {
|
||||
this.workshopId = workshopId;
|
||||
initializeSignalR();
|
||||
}
|
||||
|
||||
private void initializeSignalR() {
|
||||
// ایجاد اتصال SignalR
|
||||
hubConnection = HubConnectionBuilder
|
||||
.create(serverUrl)
|
||||
.build();
|
||||
|
||||
// دریافت رویداد ایجاد Embedding
|
||||
hubConnection.on("EmbeddingCreated", (data) -> {
|
||||
JsonObject jsonData = (JsonObject) data;
|
||||
long employeeId = jsonData.get("employeeId").getAsLong();
|
||||
String employeeFullName = jsonData.get("employeeFullName").getAsString();
|
||||
String timestamp = jsonData.get("timestamp").getAsString();
|
||||
|
||||
Log.d(TAG, "Embedding Created - Employee: " + employeeFullName + " (ID: " + employeeId + ")");
|
||||
|
||||
// اینجا میتوانید دادههای جدید را از سرور بگیرید یا UI را بروزرسانی کنید
|
||||
onEmbeddingCreated(employeeId, employeeFullName, timestamp);
|
||||
|
||||
}, JsonObject.class);
|
||||
|
||||
// دریافت رویداد حذف Embedding
|
||||
hubConnection.on("EmbeddingDeleted", (data) -> {
|
||||
JsonObject jsonData = (JsonObject) data;
|
||||
long employeeId = jsonData.get("employeeId").getAsLong();
|
||||
String timestamp = jsonData.get("timestamp").getAsString();
|
||||
|
||||
Log.d(TAG, "Embedding Deleted - Employee ID: " + employeeId);
|
||||
onEmbeddingDeleted(employeeId, timestamp);
|
||||
|
||||
}, JsonObject.class);
|
||||
|
||||
// دریافت رویداد بهبود Embedding
|
||||
hubConnection.on("EmbeddingRefined", (data) -> {
|
||||
JsonObject jsonData = (JsonObject) data;
|
||||
long employeeId = jsonData.get("employeeId").getAsLong();
|
||||
String timestamp = jsonData.get("timestamp").getAsString();
|
||||
|
||||
Log.d(TAG, "Embedding Refined - Employee ID: " + employeeId);
|
||||
onEmbeddingRefined(employeeId, timestamp);
|
||||
|
||||
}, JsonObject.class);
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
if (hubConnection.getConnectionState() == HubConnectionState.DISCONNECTED) {
|
||||
hubConnection.start()
|
||||
.doOnComplete(() -> {
|
||||
Log.d(TAG, "Connected to SignalR Hub");
|
||||
joinWorkshopGroup();
|
||||
})
|
||||
.doOnError(error -> {
|
||||
Log.e(TAG, "Error connecting to SignalR: " + error.getMessage());
|
||||
})
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
|
||||
private void joinWorkshopGroup() {
|
||||
// عضویت در گروه مخصوص این کارگاه
|
||||
hubConnection.send("JoinWorkshopGroup", workshopId);
|
||||
Log.d(TAG, "Joined workshop group: " + workshopId);
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
if (hubConnection.getConnectionState() == HubConnectionState.CONNECTED) {
|
||||
// خروج از گروه
|
||||
hubConnection.send("LeaveWorkshopGroup", workshopId);
|
||||
|
||||
hubConnection.stop();
|
||||
Log.d(TAG, "Disconnected from SignalR Hub");
|
||||
}
|
||||
}
|
||||
|
||||
// این متدها را در Activity/Fragment خود override کنید
|
||||
protected void onEmbeddingCreated(long employeeId, String employeeFullName, String timestamp) {
|
||||
// اینجا UI را بروزرسانی کنید یا داده جدید را بگیرید
|
||||
}
|
||||
|
||||
protected void onEmbeddingDeleted(long employeeId, String timestamp) {
|
||||
// اینجا UI را بروزرسانی کنید
|
||||
}
|
||||
|
||||
protected void onEmbeddingRefined(long employeeId, String timestamp) {
|
||||
// اینجا UI را بروزرسانی کنید
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### نسخه Kotlin:
|
||||
|
||||
```kotlin
|
||||
import com.microsoft.signalr.HubConnection
|
||||
import com.microsoft.signalr.HubConnectionBuilder
|
||||
import com.microsoft.signalr.HubConnectionState
|
||||
import com.google.gson.JsonObject
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class FaceEmbeddingSignalRClient(private val workshopId: Long) {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "FaceEmbeddingHub"
|
||||
}
|
||||
|
||||
private lateinit var hubConnection: HubConnection
|
||||
private val serverUrl = "http://YOUR_SERVER_IP:PORT/trackingFaceEmbeddingHub" // آدرس سرور خود را وارد کنید
|
||||
|
||||
init {
|
||||
initializeSignalR()
|
||||
}
|
||||
|
||||
private fun initializeSignalR() {
|
||||
hubConnection = HubConnectionBuilder
|
||||
.create(serverUrl)
|
||||
.build()
|
||||
|
||||
// دریافت رویداد ایجاد Embedding
|
||||
hubConnection.on("EmbeddingCreated", { data: JsonObject ->
|
||||
val employeeId = data.get("employeeId").asLong
|
||||
val employeeFullName = data.get("employeeFullName").asString
|
||||
val timestamp = data.get("timestamp").asString
|
||||
|
||||
Log.d(TAG, "Embedding Created - Employee: $employeeFullName (ID: $employeeId)")
|
||||
onEmbeddingCreated(employeeId, employeeFullName, timestamp)
|
||||
}, JsonObject::class.java)
|
||||
|
||||
// دریافت رویداد حذف Embedding
|
||||
hubConnection.on("EmbeddingDeleted", { data: JsonObject ->
|
||||
val employeeId = data.get("employeeId").asLong
|
||||
val timestamp = data.get("timestamp").asString
|
||||
|
||||
Log.d(TAG, "Embedding Deleted - Employee ID: $employeeId")
|
||||
onEmbeddingDeleted(employeeId, timestamp)
|
||||
}, JsonObject::class.java)
|
||||
|
||||
// دریافت رویداد بهبود Embedding
|
||||
hubConnection.on("EmbeddingRefined", { data: JsonObject ->
|
||||
val employeeId = data.get("employeeId").asLong
|
||||
val timestamp = data.get("timestamp").asString
|
||||
|
||||
Log.d(TAG, "Embedding Refined - Employee ID: $employeeId")
|
||||
onEmbeddingRefined(employeeId, timestamp)
|
||||
}, JsonObject::class.java)
|
||||
}
|
||||
|
||||
fun connect() {
|
||||
if (hubConnection.connectionState == HubConnectionState.DISCONNECTED) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
hubConnection.start().blockingAwait()
|
||||
Log.d(TAG, "Connected to SignalR Hub")
|
||||
joinWorkshopGroup()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error connecting to SignalR: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun joinWorkshopGroup() {
|
||||
hubConnection.send("JoinWorkshopGroup", workshopId)
|
||||
Log.d(TAG, "Joined workshop group: $workshopId")
|
||||
}
|
||||
|
||||
fun disconnect() {
|
||||
if (hubConnection.connectionState == HubConnectionState.CONNECTED) {
|
||||
hubConnection.send("LeaveWorkshopGroup", workshopId)
|
||||
hubConnection.stop()
|
||||
Log.d(TAG, "Disconnected from SignalR Hub")
|
||||
}
|
||||
}
|
||||
|
||||
// این متدها را override کنید
|
||||
open fun onEmbeddingCreated(employeeId: Long, employeeFullName: String, timestamp: String) {
|
||||
// اینجا UI را بروزرسانی کنید یا داده جدید را بگیرید
|
||||
}
|
||||
|
||||
open fun onEmbeddingDeleted(employeeId: Long, timestamp: String) {
|
||||
// اینجا UI را بروزرسانی کنید
|
||||
}
|
||||
|
||||
open fun onEmbeddingRefined(employeeId: Long, timestamp: String) {
|
||||
// اینجا UI را بروزرسانی کنید
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 4. استفاده در Activity یا Fragment
|
||||
|
||||
### مثال با Login و دریافت WorkshopId
|
||||
|
||||
#### Java:
|
||||
```java
|
||||
public class LoginActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_login);
|
||||
|
||||
Button btnLogin = findViewById(R.id.btnLogin);
|
||||
btnLogin.setOnClickListener(v -> performLogin());
|
||||
}
|
||||
|
||||
private void performLogin() {
|
||||
// فراخوانی API لاگین
|
||||
// فرض کنید response شامل workshopId است
|
||||
|
||||
// مثال ساده (باید از Retrofit یا کتابخانه مشابه استفاده کنید):
|
||||
// LoginResponse response = apiService.login(username, password);
|
||||
// long workshopId = response.getWorkshopId();
|
||||
|
||||
long workshopId = 123; // این را از response دریافت کنید
|
||||
|
||||
// ذخیره workshopId
|
||||
SharedPreferences prefs = getSharedPreferences("AppPrefs", MODE_PRIVATE);
|
||||
prefs.edit().putLong("workshopId", workshopId).apply();
|
||||
|
||||
// رفتن به صفحه اصلی
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
private FaceEmbeddingSignalRClient signalRClient;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
// دریافت workshopId از SharedPreferences
|
||||
SharedPreferences prefs = getSharedPreferences("AppPrefs", MODE_PRIVATE);
|
||||
long workshopId = prefs.getLong("workshopId", 0);
|
||||
|
||||
if (workshopId == 0) {
|
||||
// اگر workshopId وجود نداره، برگرد به صفحه لاگین
|
||||
Intent intent = new Intent(this, LoginActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// ایجاد و اتصال SignalR
|
||||
signalRClient = new FaceEmbeddingSignalRClient(workshopId) {
|
||||
@Override
|
||||
protected void onEmbeddingCreated(long employeeId, String employeeFullName, String timestamp) {
|
||||
runOnUiThread(() -> {
|
||||
// بروزرسانی UI
|
||||
Toast.makeText(MainActivity.this,
|
||||
"Embedding ایجاد شد برای: " + employeeFullName,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
|
||||
// دریافت دادههای جدید از API
|
||||
refreshEmployeeList();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEmbeddingDeleted(long employeeId, String timestamp) {
|
||||
runOnUiThread(() -> {
|
||||
// بروزرسانی UI
|
||||
refreshEmployeeList();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEmbeddingRefined(long employeeId, String timestamp) {
|
||||
runOnUiThread(() -> {
|
||||
// بروزرسانی UI
|
||||
refreshEmployeeList();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
signalRClient.connect();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (signalRClient != null) {
|
||||
signalRClient.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshEmployeeList() {
|
||||
// دریافت لیست جدید کارمندان از API
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Kotlin:
|
||||
```kotlin
|
||||
class LoginActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_login)
|
||||
|
||||
val btnLogin = findViewById<Button>(R.id.btnLogin)
|
||||
btnLogin.setOnClickListener { performLogin() }
|
||||
}
|
||||
|
||||
private fun performLogin() {
|
||||
// فراخوانی API لاگین
|
||||
// فرض کنید response شامل workshopId است
|
||||
|
||||
// مثال ساده (باید از Retrofit یا کتابخانه مشابه استفاده کنید):
|
||||
// val response = apiService.login(username, password)
|
||||
// val workshopId = response.workshopId
|
||||
|
||||
val workshopId = 123L // این را از response دریافت کنید
|
||||
|
||||
// ذخیره workshopId
|
||||
val prefs = getSharedPreferences("AppPrefs", Context.MODE_PRIVATE)
|
||||
prefs.edit().putLong("workshopId", workshopId).apply()
|
||||
|
||||
// رفتن به صفحه اصلی
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private lateinit var signalRClient: FaceEmbeddingSignalRClient
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
// دریافت workshopId از SharedPreferences
|
||||
val prefs = getSharedPreferences("AppPrefs", Context.MODE_PRIVATE)
|
||||
val workshopId = prefs.getLong("workshopId", 0L)
|
||||
|
||||
if (workshopId == 0L) {
|
||||
// اگر workshopId وجود نداره، برگرد به صفحه لاگین
|
||||
val intent = Intent(this, LoginActivity::class.java)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
// ایجاد و اتصال SignalR
|
||||
signalRClient = object : FaceEmbeddingSignalRClient(workshopId) {
|
||||
override fun onEmbeddingCreated(employeeId: Long, employeeFullName: String, timestamp: String) {
|
||||
runOnUiThread {
|
||||
// بروزرسانی UI
|
||||
Toast.makeText(this@MainActivity,
|
||||
"Embedding ایجاد شد برای: $employeeFullName",
|
||||
Toast.LENGTH_SHORT).show()
|
||||
|
||||
// دریافت دادههای جدید از API
|
||||
refreshEmployeeList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEmbeddingDeleted(employeeId: Long, timestamp: String) {
|
||||
runOnUiThread {
|
||||
// بروزرسانی UI
|
||||
refreshEmployeeList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEmbeddingRefined(employeeId: Long, timestamp: String) {
|
||||
runOnUiThread {
|
||||
// بروزرسانی UI
|
||||
refreshEmployeeList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signalRClient.connect()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
signalRClient.disconnect()
|
||||
}
|
||||
|
||||
private fun refreshEmployeeList() {
|
||||
// دریافت لیست جدید کارمندان از API
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### مثال ساده بدون Login:
|
||||
اگر workshopId را از قبل میدانید:
|
||||
|
||||
#### Java:
|
||||
```java
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
private FaceEmbeddingSignalRClient signalRClient;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
long workshopId = 123; // شناسه کارگاه خود را وارد کنید
|
||||
|
||||
signalRClient = new FaceEmbeddingSignalRClient(workshopId) {
|
||||
@Override
|
||||
protected void onEmbeddingCreated(long employeeId, String employeeFullName, String timestamp) {
|
||||
runOnUiThread(() -> {
|
||||
// بروزرسانی UI
|
||||
Toast.makeText(MainActivity.this,
|
||||
"Embedding ایجاد شد برای: " + employeeFullName,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
|
||||
// دریافت دادههای جدید از API
|
||||
refreshEmployeeList();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEmbeddingDeleted(long employeeId, String timestamp) {
|
||||
runOnUiThread(() -> {
|
||||
// بروزرسانی UI
|
||||
refreshEmployeeList();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
signalRClient.connect();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (signalRClient != null) {
|
||||
signalRClient.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshEmployeeList() {
|
||||
// دریافت لیست جدید کارمندان از API
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Kotlin:
|
||||
```kotlin
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private lateinit var signalRClient: FaceEmbeddingSignalRClient
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
val workshopId = 123L // شناسه کارگاه خود را وارد کنید
|
||||
|
||||
signalRClient = object : FaceEmbeddingSignalRClient(workshopId) {
|
||||
override fun onEmbeddingCreated(employeeId: Long, employeeFullName: String, timestamp: String) {
|
||||
runOnUiThread {
|
||||
// بروزرسانی UI
|
||||
Toast.makeText(this@MainActivity,
|
||||
"Embedding ایجاد شد برای: $employeeFullName",
|
||||
Toast.LENGTH_SHORT).show()
|
||||
|
||||
// دریافت دادههای جدید از API
|
||||
refreshEmployeeList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEmbeddingDeleted(employeeId: Long, timestamp: String) {
|
||||
runOnUiThread {
|
||||
// بروزرسانی UI
|
||||
refreshEmployeeList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signalRClient.connect()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
signalRClient.disconnect()
|
||||
}
|
||||
|
||||
private fun refreshEmployeeList() {
|
||||
// دریافت لیست جدید کارمندان از API
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 5. نکات مهم
|
||||
|
||||
### آدرس سرور
|
||||
- اگر روی شبیهساز اندروید تست میکنید و سرور روی localhost اجرا میشود، از آدرس `http://10.0.2.2:PORT` استفاده کنید
|
||||
- اگر روی دستگاه فیزیکی تست میکنید، از آدرس IP شبکه محلی سرور استفاده کنید (مثل `http://192.168.1.100:PORT`)
|
||||
- PORT پیشفرض معمولاً 5000 یا 5001 است (بسته به کانفیگ پروژه شما)
|
||||
|
||||
### دریافت WorkshopId از Login
|
||||
بعد از login موفق، workshopId را از سرور دریافت کنید و در SharedPreferences یا یک Singleton ذخیره کنید:
|
||||
|
||||
```java
|
||||
// بعد از login موفق
|
||||
SharedPreferences prefs = getSharedPreferences("AppPrefs", MODE_PRIVATE);
|
||||
prefs.edit().putLong("workshopId", workshopId).apply();
|
||||
|
||||
// استفاده در Activity
|
||||
long workshopId = prefs.getLong("workshopId", 0);
|
||||
```
|
||||
|
||||
یا در Kotlin:
|
||||
|
||||
```kotlin
|
||||
// بعد از login موفق
|
||||
val prefs = getSharedPreferences("AppPrefs", Context.MODE_PRIVATE)
|
||||
prefs.edit().putLong("workshopId", workshopId).apply()
|
||||
|
||||
// استفاده در Activity
|
||||
val workshopId = prefs.getLong("workshopId", 0L)
|
||||
```
|
||||
|
||||
### مدیریت اتصال
|
||||
برای reconnection خودکار:
|
||||
|
||||
```java
|
||||
hubConnection.onClosed(exception -> {
|
||||
Log.e(TAG, "Connection closed. Attempting to reconnect...");
|
||||
new Handler().postDelayed(() -> connect(), 5000); // تلاش مجدد بعد از 5 ثانیه
|
||||
});
|
||||
```
|
||||
|
||||
### Thread Safety
|
||||
همیشه UI updates را در main thread انجام دهید:
|
||||
|
||||
```java
|
||||
runOnUiThread(() -> {
|
||||
// UI updates here
|
||||
});
|
||||
```
|
||||
|
||||
## 6. تست اتصال
|
||||
|
||||
برای تست میتوانید:
|
||||
1. اپلیکیشن را اجرا کنید
|
||||
2. از طریق Postman یا Swagger یک Embedding ایجاد کنید
|
||||
3. باید در Logcat پیام "Embedding Created" را ببینید
|
||||
|
||||
## 7. خطایابی (Debugging)
|
||||
|
||||
برای دیدن جزئیات بیشتر:
|
||||
|
||||
```java
|
||||
hubConnection = HubConnectionBuilder
|
||||
.create(serverUrl)
|
||||
.withHttpConnectionOptions(options -> {
|
||||
options.setLogging(LogLevel.TRACE);
|
||||
})
|
||||
.build();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## خلاصه Endpoints
|
||||
|
||||
| نوع رویداد | متد SignalR | پارامترهای دریافتی |
|
||||
|-----------|-------------|---------------------|
|
||||
| ایجاد Embedding | `EmbeddingCreated` | workshopId, employeeId, employeeFullName, timestamp |
|
||||
| حذف Embedding | `EmbeddingDeleted` | workshopId, employeeId, timestamp |
|
||||
| بهبود Embedding | `EmbeddingRefined` | workshopId, employeeId, timestamp |
|
||||
|
||||
| متد ارسالی | پارامتر | توضیحات |
|
||||
|-----------|---------|---------|
|
||||
| `JoinWorkshopGroup` | workshopId | عضویت در گروه کارگاه |
|
||||
| `LeaveWorkshopGroup` | workshopId | خروج از گروه کارگاه |
|
||||
|
||||
@@ -66,4 +66,11 @@ public interface IAccountApplication
|
||||
public bool CheckExistClientAccount(string userName);
|
||||
List<AccountViewModel> GetAdminAccountsNew();
|
||||
|
||||
void CameraLogin(CameraLoginRequest request);
|
||||
}
|
||||
|
||||
public class CameraLoginRequest
|
||||
{
|
||||
public string UserName { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
@@ -18,6 +18,7 @@ using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database;
|
||||
using Company.Domain.WorkshopAgg;
|
||||
using System.Security.Claims;
|
||||
using _0_Framework.Exceptions;
|
||||
using AccountManagement.Domain.PositionAgg;
|
||||
using AccountManagement.Domain.SubAccountAgg;
|
||||
using AccountManagement.Domain.SubAccountPermissionSubtitle1Agg;
|
||||
@@ -803,4 +804,29 @@ public class AccountApplication : IAccountApplication
|
||||
{
|
||||
return _accountRepository.GetAdminAccountsNew();
|
||||
}
|
||||
|
||||
public void CameraLogin(CameraLoginRequest request)
|
||||
{
|
||||
var cameraAccount = _cameraAccountRepository.GetBy(request.UserName);
|
||||
|
||||
if (cameraAccount == null)
|
||||
{
|
||||
throw new BadRequestException(ApplicationMessages.WrongUserPass);
|
||||
}
|
||||
|
||||
(bool Verified, bool NeedUpgrade) result = _passwordHasher.Check(cameraAccount.Password, request.Password);
|
||||
|
||||
if (!result.Verified)
|
||||
throw new BadRequestException(ApplicationMessages.WrongUserPass);
|
||||
|
||||
var mobile = string.IsNullOrWhiteSpace(cameraAccount.Mobile) ? " " : cameraAccount.Mobile;
|
||||
|
||||
var authViewModel = new CameraAuthViewModel(cameraAccount.id, cameraAccount.WorkshopId,
|
||||
cameraAccount.Username, mobile, cameraAccount.WorkshopName, cameraAccount.AccountId,
|
||||
cameraAccount.IsActiveSting);
|
||||
if (cameraAccount.IsActiveSting != "true")
|
||||
throw new BadRequestException(ApplicationMessages.WrongUserPass);
|
||||
|
||||
_authHelper.CameraSignIn(authViewModel);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using _0_Framework.Application;
|
||||
using _0_Framework.Domain;
|
||||
using CompanyManagment.App.Contracts.AndroidApkVersion;
|
||||
|
||||
namespace Company.Domain.AndroidApkVersionAgg;
|
||||
|
||||
@@ -8,14 +9,17 @@ public class AndroidApkVersion:EntityBase
|
||||
{
|
||||
private AndroidApkVersion () { }
|
||||
|
||||
public AndroidApkVersion( string versionName,string versionCode, IsActive isActive, string path)
|
||||
public AndroidApkVersion( string versionName,string versionCode, IsActive isActive, string path, ApkType apkType, bool isForce = false)
|
||||
{
|
||||
|
||||
VersionName = versionName;
|
||||
VersionCode = versionCode;
|
||||
IsActive = isActive;
|
||||
Path = path;
|
||||
Title = $"Gozareshgir-{versionName}-{CreationDate:g}";
|
||||
ApkType = apkType;
|
||||
IsForce = isForce;
|
||||
var appName = apkType == ApkType.WebView ? "Gozareshgir-WebView" : "Gozareshgir-FaceDetection";
|
||||
Title = $"{appName}-{versionName}-{CreationDate:g}";
|
||||
}
|
||||
|
||||
public string Title { get; private set; }
|
||||
@@ -23,6 +27,9 @@ public class AndroidApkVersion:EntityBase
|
||||
public string VersionCode{ get; private set; }
|
||||
public IsActive IsActive { get; private set; }
|
||||
public string Path { get; set; }
|
||||
public ApkType ApkType { get; private set; }
|
||||
public bool IsForce { get; private set; }
|
||||
|
||||
|
||||
public void Active()
|
||||
{
|
||||
@@ -33,4 +40,4 @@ public class AndroidApkVersion:EntityBase
|
||||
{
|
||||
IsActive = IsActive.False;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using _0_Framework_b.Domain;
|
||||
using CompanyManagment.App.Contracts.AndroidApkVersion;
|
||||
|
||||
namespace Company.Domain.AndroidApkVersionAgg;
|
||||
|
||||
public interface IAndroidApkVersionRepository:IRepository<long,AndroidApkVersion>
|
||||
{
|
||||
IQueryable<AndroidApkVersion> GetActives();
|
||||
IQueryable<AndroidApkVersion> GetActives(ApkType apkType);
|
||||
AndroidApkVersion GetLatestActive(ApkType apkType);
|
||||
void Remove(AndroidApkVersion entity);
|
||||
System.Threading.Tasks.Task<string> GetLatestActiveVersionPath();
|
||||
System.Threading.Tasks.Task<string> GetLatestActiveVersionPath(ApkType apkType);
|
||||
}
|
||||
189
Company.Domain/EmployeeFaceEmbeddingAgg/EmployeeFaceEmbedding.cs
Normal file
189
Company.Domain/EmployeeFaceEmbeddingAgg/EmployeeFaceEmbedding.cs
Normal file
@@ -0,0 +1,189 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace Company.Domain.EmployeeFaceEmbeddingAgg;
|
||||
|
||||
public class EmployeeFaceEmbedding
|
||||
{
|
||||
public EmployeeFaceEmbedding()
|
||||
{
|
||||
EmbeddingHistory = new List<EmbeddingHistoryItem>();
|
||||
MetadataHistory = new List<MetadataHistoryItem>();
|
||||
}
|
||||
|
||||
public EmployeeFaceEmbedding(string employeeFullName, long employeeId, long workshopId,
|
||||
List<double> embeddings, EmployeeFaceEmbeddingMetadata metadata)
|
||||
{
|
||||
Id = Guid.NewGuid().ToString();
|
||||
EmployeeFullName = employeeFullName;
|
||||
EmployeeId = employeeId;
|
||||
WorkshopId = workshopId;
|
||||
Embeddings = embeddings;
|
||||
Metadata = metadata;
|
||||
EmbeddingHistory = new List<EmbeddingHistoryItem>();
|
||||
MetadataHistory = new List<MetadataHistoryItem>();
|
||||
CreatedAt = DateTime.UtcNow;
|
||||
UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
[BsonId]
|
||||
[BsonRepresentation(BsonType.String)]
|
||||
public string Id { get; set; }
|
||||
|
||||
[BsonElement("employeeFullName")]
|
||||
public string EmployeeFullName { get; set; }
|
||||
|
||||
[BsonElement("employeeId")]
|
||||
public long EmployeeId { get; set; }
|
||||
|
||||
[BsonElement("workshopId")]
|
||||
public long WorkshopId { get; set; }
|
||||
|
||||
[BsonElement("embeddings")]
|
||||
public List<double> Embeddings { get; set; }
|
||||
|
||||
[BsonElement("metadata")]
|
||||
public EmployeeFaceEmbeddingMetadata Metadata { get; set; }
|
||||
|
||||
[BsonElement("embeddingHistory")]
|
||||
public List<EmbeddingHistoryItem> EmbeddingHistory { get; set; }
|
||||
|
||||
[BsonElement("metadataHistory")]
|
||||
public List<MetadataHistoryItem> MetadataHistory { get; set; }
|
||||
|
||||
[BsonElement("createdAt")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[BsonElement("updatedAt")]
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
public void UpdateEmbedding(List<double> newEmbedding, double confidence, double refinementPercentage)
|
||||
{
|
||||
if (Embeddings != null)
|
||||
{
|
||||
EmbeddingHistory.Add(new EmbeddingHistoryItem
|
||||
{
|
||||
Embedding = new List<double>(Embeddings),
|
||||
Timestamp = DateTime.UtcNow,
|
||||
Confidence = confidence,
|
||||
RefinementPercentage = refinementPercentage
|
||||
});
|
||||
}
|
||||
|
||||
Embeddings = newEmbedding;
|
||||
UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public void UpdateMetadata(EmployeeFaceEmbeddingMetadata newMetadata, double confidence, double refinementPercentage)
|
||||
{
|
||||
if (Metadata != null)
|
||||
{
|
||||
MetadataHistory.Add(new MetadataHistoryItem
|
||||
{
|
||||
Metadata = Metadata,
|
||||
Timestamp = DateTime.UtcNow,
|
||||
Confidence = confidence,
|
||||
RefinementPercentage = refinementPercentage
|
||||
});
|
||||
}
|
||||
|
||||
Metadata = newMetadata;
|
||||
UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public void UpdateEmployeeInfo(string employeeFullName, long workshopId)
|
||||
{
|
||||
EmployeeFullName = employeeFullName;
|
||||
WorkshopId = workshopId;
|
||||
UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
public class EmployeeFaceEmbeddingMetadata
|
||||
{
|
||||
[BsonElement("avg_eye_distance_normalized")]
|
||||
public double AvgEyeDistanceNormalized { get; set; }
|
||||
|
||||
[BsonElement("avg_eye_to_face_ratio")]
|
||||
public double AvgEyeToFaceRatio { get; set; }
|
||||
|
||||
[BsonElement("avg_face_aspect_ratio")]
|
||||
public double AvgFaceAspectRatio { get; set; }
|
||||
|
||||
[BsonElement("avg_detection_confidence")]
|
||||
public double AvgDetectionConfidence { get; set; }
|
||||
|
||||
[BsonElement("avg_keypoints_normalized")]
|
||||
public EmployeeFaceEmbeddingKeypoints AvgKeypointsNormalized { get; set; }
|
||||
|
||||
[BsonElement("per_image_metadata")]
|
||||
public List<ImageMetadata> PerImageMetadata { get; set; }
|
||||
}
|
||||
|
||||
public class EmployeeFaceEmbeddingKeypoints
|
||||
{
|
||||
[BsonElement("left_eye")]
|
||||
public double[] LeftEye { get; set; }
|
||||
|
||||
[BsonElement("right_eye")]
|
||||
public double[] RightEye { get; set; }
|
||||
|
||||
[BsonElement("nose")]
|
||||
public double[] Nose { get; set; }
|
||||
|
||||
[BsonElement("mouth_left")]
|
||||
public double[] MouthLeft { get; set; }
|
||||
|
||||
[BsonElement("mouth_right")]
|
||||
public double[] MouthRight { get; set; }
|
||||
}
|
||||
|
||||
public class ImageMetadata
|
||||
{
|
||||
[BsonElement("face_aspect_ratio")]
|
||||
public double FaceAspectRatio { get; set; }
|
||||
|
||||
[BsonElement("eye_distance_normalized")]
|
||||
public double EyeDistanceNormalized { get; set; }
|
||||
|
||||
[BsonElement("eye_to_face_ratio")]
|
||||
public double EyeToFaceRatio { get; set; }
|
||||
|
||||
[BsonElement("detection_confidence")]
|
||||
public double DetectionConfidence { get; set; }
|
||||
|
||||
[BsonElement("keypoints_normalized")]
|
||||
public EmployeeFaceEmbeddingKeypoints KeypointsNormalized { get; set; }
|
||||
}
|
||||
|
||||
public class EmbeddingHistoryItem
|
||||
{
|
||||
[BsonElement("embedding")]
|
||||
public List<double> Embedding { get; set; }
|
||||
|
||||
[BsonElement("timestamp")]
|
||||
public DateTime Timestamp { get; set; }
|
||||
|
||||
[BsonElement("confidence")]
|
||||
public double Confidence { get; set; }
|
||||
|
||||
[BsonElement("refinementPercentage")]
|
||||
public double RefinementPercentage { get; set; }
|
||||
}
|
||||
|
||||
public class MetadataHistoryItem
|
||||
{
|
||||
[BsonElement("metadata")]
|
||||
public EmployeeFaceEmbeddingMetadata Metadata { get; set; }
|
||||
|
||||
[BsonElement("timestamp")]
|
||||
public DateTime Timestamp { get; set; }
|
||||
|
||||
[BsonElement("confidence")]
|
||||
public double Confidence { get; set; }
|
||||
|
||||
[BsonElement("refinementPercentage")]
|
||||
public double RefinementPercentage { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Company.Domain.EmployeeFaceEmbeddingAgg;
|
||||
|
||||
public interface IEmployeeFaceEmbeddingRepository
|
||||
{
|
||||
Task CreateAsync(EmployeeFaceEmbedding employeeFaceEmbedding);
|
||||
Task UpdateAsync(EmployeeFaceEmbedding employeeFaceEmbedding);
|
||||
Task<EmployeeFaceEmbedding> GetByIdAsync(string id);
|
||||
Task<EmployeeFaceEmbedding> GetByEmployeeIdAsync(long employeeId);
|
||||
Task<List<EmployeeFaceEmbedding>> GetByWorkshopIdAsync(long workshopId);
|
||||
Task<List<EmployeeFaceEmbedding>> GetByWorkshopIdsAsync(List<long> workshopIds);
|
||||
Task DeleteAsync(string id);
|
||||
}
|
||||
@@ -77,4 +77,5 @@ public interface IInstitutionContractRepository : IRepository<long, InstitutionC
|
||||
|
||||
Task<List<InstitutionContractSelectListViewModel>> GetInstitutionContractSelectList(string search, string selected);
|
||||
Task<List<InstitutionContractPrintViewModel>> PrintAllAsync(List<long> ids);
|
||||
Task<GetInstitutionContractWorkshopsDetails> GetContractWorkshopsDetails(long id);
|
||||
}
|
||||
@@ -18,13 +18,15 @@ public class PaymentTransaction:EntityBase
|
||||
/// <param name="callBackUrl"></param>
|
||||
public PaymentTransaction(long contractingPartyId,
|
||||
double amount,
|
||||
string contractingPartyName,string callBackUrl)
|
||||
string contractingPartyName,string callBackUrl,
|
||||
PaymentTransactionGateWay gateway)
|
||||
{
|
||||
ContractingPartyId = contractingPartyId;
|
||||
Status = PaymentTransactionStatus.Pending;
|
||||
Amount = amount;
|
||||
ContractingPartyName = contractingPartyName;
|
||||
CallBackUrl = callBackUrl;
|
||||
Gateway = gateway;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -68,13 +70,20 @@ public class PaymentTransaction:EntityBase
|
||||
public string TransactionId { get; private set; }
|
||||
|
||||
public string CallBackUrl { get; private set; }
|
||||
public PaymentTransactionGateWay Gateway { get; private set; }
|
||||
public string Rrn { get; private set; }
|
||||
public string DigitalReceipt { get; private set; }
|
||||
|
||||
public void SetPaid(string cardNumber,string bankName)
|
||||
|
||||
public void SetPaid(string cardNumber,string bankName,string rrn,string digitalReceipt)
|
||||
{
|
||||
Status = PaymentTransactionStatus.Success;
|
||||
TransactionDate = DateTime.Now;
|
||||
CardNumber = cardNumber;
|
||||
BankName = bankName;
|
||||
Rrn = rrn;
|
||||
DigitalReceipt = digitalReceipt;
|
||||
|
||||
}
|
||||
public void SetFailed()
|
||||
{
|
||||
@@ -85,4 +94,5 @@ public class PaymentTransaction:EntityBase
|
||||
{
|
||||
TransactionId = transactionId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,21 +11,23 @@ namespace Company.Domain.ReportAgg
|
||||
{
|
||||
Task<AllReport> GetAllActiveWorkshopsNew(string year, string month);
|
||||
AllReport GetAllActiveWorkshops(string year, string month);
|
||||
WorkshopResult GetWorkshopContractDone(string year, string month, long accountId, List<long> workshopList);
|
||||
WorkshopResult GetWorkshopContractSignDone(string year, string month, long accountId, List<long> workshopList);
|
||||
WorkshopResult GetWorkshopCheckoutDone(string year, string month, long accountId, List<long> workshopList);
|
||||
WorkshopResult GetWorkshopCheckoutSignDone(string year, string month, long accountId, List<long> workshopList);
|
||||
List<EmployeeNotDone> GetEmployeeContract(string year, string month, long workshopId);
|
||||
List<EmployeeNotDone> GetEmployeeContractSign(string year, string month, long workshopId);
|
||||
List<EmployeeNotDone> GetEmployeeCheckout(string year, string month, long workshopId);
|
||||
List<EmployeeNotDone> GetEmployeeCheckoutSign(string year, string month, long workshopId);
|
||||
PrintAllContractCheckout GetPrintAllContractDone(string year, string month, long accountId,
|
||||
Task<WorkshopResult> GetWorkshopContractDone(string year, string month, long accountId, List<long> workshopList);
|
||||
Task<WorkshopResult> GetWorkshopContractSignDone(string year, string month, long accountId,
|
||||
List<long> workshopList);
|
||||
PrintAllContractCheckout GetPrintAllContractSignDone(string year, string month, long accountId,
|
||||
Task<WorkshopResult> GetWorkshopCheckoutDone(string year, string month, long accountId, List<long> workshopList);
|
||||
Task<WorkshopResult> GetWorkshopCheckoutSignDone(string year, string month, long accountId,
|
||||
List<long> workshopList);
|
||||
PrintAllContractCheckout GetPrintAllCheckoutDone(string year, string month, long accountId,
|
||||
Task<List<EmployeeNotDone>> GetEmployeeContract(string year, string month, long workshopId);
|
||||
Task<List<EmployeeNotDone>> GetEmployeeContractSign(string year, string month, long workshopId);
|
||||
Task<List<EmployeeNotDone>> GetEmployeeCheckout(string year, string month, long workshopId);
|
||||
Task<List<EmployeeNotDone>> GetEmployeeCheckoutSign(string year, string month, long workshopId);
|
||||
Task<PrintAllContractCheckout> GetPrintAllContractDone(string year, string month, long accountId,
|
||||
List<long> workshopList);
|
||||
PrintAllContractCheckout GetPrintAllCheckoutSignDone(string year, string month, long accountId,
|
||||
Task<PrintAllContractCheckout> GetPrintAllContractSignDone(string year, string month, long accountId,
|
||||
List<long> workshopList);
|
||||
Task<PrintAllContractCheckout> GetPrintAllCheckoutDone(string year, string month, long accountId,
|
||||
List<long> workshopList);
|
||||
Task<PrintAllContractCheckout> GetPrintAllCheckoutSignDone(string year, string month, long accountId,
|
||||
List<long> workshopList);
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
using Company.Domain.EmployeeFaceEmbeddingAgg;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace CompanyManagement.Infrastructure.Mongo.EmployeeFaceEmbeddingRepo;
|
||||
|
||||
public class EmployeeFaceEmbeddingRepository : IEmployeeFaceEmbeddingRepository
|
||||
{
|
||||
private readonly IMongoCollection<EmployeeFaceEmbedding> _employeeFaceEmbeddings;
|
||||
|
||||
public EmployeeFaceEmbeddingRepository(IMongoDatabase database)
|
||||
{
|
||||
_employeeFaceEmbeddings = database.GetCollection<EmployeeFaceEmbedding>("EmployeeFaces");
|
||||
}
|
||||
|
||||
public async Task CreateAsync(EmployeeFaceEmbedding employeeFaceEmbedding)
|
||||
{
|
||||
await _employeeFaceEmbeddings.InsertOneAsync(employeeFaceEmbedding);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(EmployeeFaceEmbedding employeeFaceEmbedding)
|
||||
{
|
||||
await _employeeFaceEmbeddings.ReplaceOneAsync(
|
||||
x => x.Id == employeeFaceEmbedding.Id,
|
||||
employeeFaceEmbedding);
|
||||
}
|
||||
|
||||
public async Task<EmployeeFaceEmbedding> GetByIdAsync(string id)
|
||||
{
|
||||
return await _employeeFaceEmbeddings
|
||||
.Find(x => x.Id == id)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<EmployeeFaceEmbedding> GetByEmployeeIdAsync(long employeeId)
|
||||
{
|
||||
return await _employeeFaceEmbeddings
|
||||
.Find(x => x.EmployeeId == employeeId)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<List<EmployeeFaceEmbedding>> GetByWorkshopIdAsync(long workshopId)
|
||||
{
|
||||
return await _employeeFaceEmbeddings
|
||||
.Find(x => x.WorkshopId == workshopId)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<EmployeeFaceEmbedding>> GetByWorkshopIdsAsync(List<long> workshopIds)
|
||||
{
|
||||
return await _employeeFaceEmbeddings
|
||||
.Find(x => workshopIds.First()==x.WorkshopId)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(string id)
|
||||
{
|
||||
await _employeeFaceEmbeddings.DeleteOneAsync(x => x.Id == id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace CompanyManagment.App.Contracts.AndroidApkVersion;
|
||||
|
||||
public enum ApkType
|
||||
{
|
||||
WebView,
|
||||
FaceDetection
|
||||
}
|
||||
|
||||
@@ -4,12 +4,15 @@ using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace CompanyManagment.App.Contracts.AndroidApkVersion;
|
||||
|
||||
public record AndroidApkVersionInfo(int LatestVersionCode, string LatestVersionName, bool ShouldUpdate, bool IsForceUpdate, string DownloadUrl, string ReleaseNotes);
|
||||
|
||||
public interface IAndroidApkVersionApplication
|
||||
{
|
||||
Task<OperationResult> CreateAndActive(IFormFile file);
|
||||
Task<OperationResult> CreateAndDeActive(IFormFile file);
|
||||
Task<string> GetLatestActiveVersionPath();
|
||||
Task<OperationResult> CreateAndActive(IFormFile file, ApkType apkType, string versionName, string versionCode, bool isForce = false);
|
||||
Task<OperationResult> CreateAndDeActive(IFormFile file, ApkType apkType, string versionName, string versionCode, bool isForce = false);
|
||||
Task<string> GetLatestActiveVersionPath(ApkType apkType);
|
||||
Task<AndroidApkVersionInfo> GetLatestActiveInfo(ApkType apkType, int currentVersionCode);
|
||||
OperationResult Remove(long id);
|
||||
|
||||
bool HasAndroidApkToDownload();
|
||||
bool HasAndroidApkToDownload(ApkType apkType);
|
||||
}
|
||||
@@ -25,4 +25,5 @@ public class CustomizeWorkshopEmployeeSettingsViewModel
|
||||
public int LeavePermittedDays { get; set; }
|
||||
public ICollection<CustomizeRotatingShiftsViewModel> CustomizeRotatingShiftsViewModels { get; set; }
|
||||
public List<DayOfWeek> WeeklyOffDays { get; set; }
|
||||
public bool HasLeft { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace CompanyManagment.App.Contracts.EmployeeFaceEmbedding;
|
||||
|
||||
public class EmployeeFaceEmbeddingDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string EmployeeFullName { get; set; }
|
||||
public long EmployeeId { get; set; }
|
||||
public long WorkshopId { get; set; }
|
||||
public List<double> Embeddings { get; set; }
|
||||
public EmployeeFaceEmbeddingMetadataDto Metadata { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace CompanyManagment.App.Contracts.EmployeeFaceEmbedding;
|
||||
|
||||
public class EmployeeFaceEmbeddingMetadataDto
|
||||
{
|
||||
public double AvgEyeDistanceNormalized { get; set; }
|
||||
public double AvgEyeToFaceRatio { get; set; }
|
||||
public double AvgFaceAspectRatio { get; set; }
|
||||
public double AvgDetectionConfidence { get; set; }
|
||||
public EmployeeFaceEmbeddingKeypointsDto AvgKeypointsNormalized { get; set; }
|
||||
public List<ImageMetadataDto> PerImageMetadata { get; set; }
|
||||
}
|
||||
|
||||
public class EmployeeFaceEmbeddingKeypointsDto
|
||||
{
|
||||
public double[] LeftEye { get; set; }
|
||||
public double[] RightEye { get; set; }
|
||||
public double[] Nose { get; set; }
|
||||
public double[] MouthLeft { get; set; }
|
||||
public double[] MouthRight { get; set; }
|
||||
}
|
||||
|
||||
public class ImageMetadataDto
|
||||
{
|
||||
public double FaceAspectRatio { get; set; }
|
||||
public double EyeDistanceNormalized { get; set; }
|
||||
public double EyeToFaceRatio { get; set; }
|
||||
public double DetectionConfidence { get; set; }
|
||||
public EmployeeFaceEmbeddingKeypointsDto KeypointsNormalized { get; set; }
|
||||
}
|
||||
|
||||
public class EmbeddingHistoryItemDto
|
||||
{
|
||||
public List<double> Embedding { get; set; }
|
||||
public DateTime Timestamp { get; set; }
|
||||
public double Confidence { get; set; }
|
||||
public double RefinementPercentage { get; set; }
|
||||
}
|
||||
|
||||
public class MetadataHistoryItemDto
|
||||
{
|
||||
public EmployeeFaceEmbeddingMetadataDto Metadata { get; set; }
|
||||
public DateTime Timestamp { get; set; }
|
||||
public double Confidence { get; set; }
|
||||
public double RefinementPercentage { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CompanyManagment.App.Contracts.EmployeeFaceEmbedding;
|
||||
|
||||
/// <summary>
|
||||
/// سرویس مدیریت Embedding چهره کارکنان
|
||||
/// این سرویس فقط برای دریافت و ارسال داده است و هیچ command-driven نیست
|
||||
/// </summary>
|
||||
public interface IEmployeeFaceEmbeddingApplication
|
||||
{
|
||||
/// <summary>
|
||||
/// دریافت embedding بر اساس شناسه
|
||||
/// </summary>
|
||||
Task<EmployeeFaceEmbeddingDto> GetByIdAsync(string id);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت embedding بر اساس شناسه کارمند
|
||||
/// </summary>
|
||||
Task<EmployeeFaceEmbeddingDto> GetByEmployeeIdAsync(long employeeId);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت لیست embeddings بر اساس شناسه کارگاه
|
||||
/// </summary>
|
||||
Task<List<EmployeeFaceEmbeddingDto>> GetByWorkshopIdAsync(long workshopId);
|
||||
|
||||
/// <summary>
|
||||
/// دریافت لیست embeddings بر اساس لیست شناسه کارگاهها
|
||||
/// </summary>
|
||||
Task<List<EmployeeFaceEmbeddingDto>> GetByWorkshopIdsAsync(List<long> workshopIds);
|
||||
|
||||
/// <summary>
|
||||
/// ذخیره یا بهروزرسانی embedding
|
||||
/// اگر Id وجود داشته باشد، بهروزرسانی میشود، در غیر این صورت ایجاد میشود
|
||||
/// </summary>
|
||||
Task<string> SaveAsync(EmployeeFaceEmbeddingDto dto);
|
||||
|
||||
/// <summary>
|
||||
/// حذف embedding
|
||||
/// </summary>
|
||||
Task DeleteAsync(string id);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Security.AccessControl;
|
||||
using CompanyManagment.App.Contracts.Workshop;
|
||||
using Microsoft.AspNetCore.Server.HttpSys;
|
||||
|
||||
namespace CompanyManagment.App.Contracts.InstitutionContract;
|
||||
|
||||
@@ -97,6 +100,7 @@ public class InstitutionContractListWorkshop
|
||||
{
|
||||
public string WorkshopName { get; set; }
|
||||
public int EmployeeCount { get; set; }
|
||||
public string Price { get; set; }
|
||||
public WorkshopServicesViewModel WorkshopServices { get; set; }
|
||||
}
|
||||
|
||||
@@ -104,22 +108,31 @@ public class WorkshopServicesViewModel
|
||||
{
|
||||
public bool Insurance { get; set; }
|
||||
public string InsuranceLabel => "ارسال لیست بیمه";
|
||||
public string InsurancePrice { get; set; }
|
||||
|
||||
public bool InsuranceInPerson { get; set; }
|
||||
public string InsuranceInPersonLabel => "خدمات مستقیم";
|
||||
|
||||
public string InsuranceInPersonPrice { get; set; }
|
||||
|
||||
|
||||
public bool Contract { get; set; }
|
||||
public string ContractLabel => "قرارداد و تصفیه حساب";
|
||||
|
||||
public string ContractPrice { get; set; }
|
||||
|
||||
|
||||
public bool ContractInPerson { get; set; }
|
||||
public string ContractInPersonLabel => "خدمات مستقیم";
|
||||
|
||||
public string ContractInPersonPrice { get; set; }
|
||||
|
||||
public bool RollCall { get; set; }
|
||||
public string RollCallLabel => "ساعت حضور و غیاب";
|
||||
|
||||
public string RollCallPrice { get; set; }
|
||||
|
||||
public bool RollCallInPerson { get; set; }
|
||||
public string RollCallInPersonLabel => "خدمات مستقیم";
|
||||
|
||||
public string RollCallInPersonPrice { get; set; }
|
||||
|
||||
public bool CustomizeCheckout { get; set; }
|
||||
public string CustomizeCheckoutLabel => "فیش غیر رسمی";
|
||||
public string CustomizeCheckoutPrice { get; set; }
|
||||
}
|
||||
@@ -251,8 +251,17 @@ public interface IInstitutionContractApplication
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
Task<InstitutionContractPrintViewModel> PrintOneAsync(long id);
|
||||
|
||||
Task<GetInstitutionContractWorkshopsDetails> GetContractWorkshopsDetails(long id);
|
||||
}
|
||||
|
||||
public class GetInstitutionContractWorkshopsDetails
|
||||
{
|
||||
public List<InstitutionContractListWorkshop> Workshops { get; set; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class InstitutionContractPrintViewModel
|
||||
{
|
||||
public InstitutionContratVerificationParty FirstParty { get; set; }
|
||||
|
||||
@@ -17,4 +17,14 @@ public class CreatePaymentTransaction
|
||||
/// مسیر برگشت پس از پرداخت
|
||||
/// </summary>
|
||||
public string CallBackUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// نوع درگاه
|
||||
/// </summary>
|
||||
public PaymentTransactionGateWay Gateway { get; set; }
|
||||
}
|
||||
public enum PaymentTransactionGateWay
|
||||
{
|
||||
AqayePardakht = 1,
|
||||
SepehrPay = 2
|
||||
}
|
||||
@@ -49,7 +49,7 @@ public interface IPaymentTransactionApplication
|
||||
/// <param name="cardNumber"></param>
|
||||
/// <param name="bankName"></param>
|
||||
/// <returns></returns>
|
||||
OperationResult SetSuccess(long paymentTransactionId, string cardNumber, string bankName);
|
||||
OperationResult SetSuccess(long paymentTransactionId, string cardNumber, string bankName, string rrn, string digitalReceipt);
|
||||
|
||||
Task<OperationResult> SetTransactionId(long id, string transactionId);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ namespace CompanyManagment.App.Contracts.Report
|
||||
public int ContractSignNotDone { get; set; }
|
||||
public int ContractSignDone { get; set; }
|
||||
|
||||
public int ContractSignToBe { get; set; }
|
||||
|
||||
//تصفیه
|
||||
public int AllCheckout { get; set; }
|
||||
public int CheckoutNotDone { get; set; }
|
||||
@@ -34,6 +36,8 @@ namespace CompanyManagment.App.Contracts.Report
|
||||
public int CheckoutSignNotDone { get; set; }
|
||||
public int CheckoutSignDone { get; set; }
|
||||
|
||||
public int CheckoutSignToBe { get; set; }
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -10,21 +10,23 @@ namespace CompanyManagment.App.Contracts.Report
|
||||
{
|
||||
Task<AllReport> GetAllActiveWorkshops(string year, string month);
|
||||
Task<AllReport> GetAllReports(string year, string month);
|
||||
WorkshopResult GetWorkshopContractDone(string year, string month, long accountId, List<long> workshopList);
|
||||
WorkshopResult GetWorkshopContractSignDone(string year, string month, long accountId, List<long> workshopList);
|
||||
WorkshopResult GetWorkshopCheckoutDone(string year, string month, long accountId, List<long> workshopList);
|
||||
WorkshopResult GetWorkshopCheckoutSignDone(string year, string month, long accountId, List<long> workshopList);
|
||||
List<EmployeeNotDone> GetEmployeeContract(string year, string month, long workshopId);
|
||||
List<EmployeeNotDone> GetEmployeeContractSign(string year, string month, long workshopId);
|
||||
List<EmployeeNotDone> GetEmployeeCheckout(string year, string month, long workshopId);
|
||||
List<EmployeeNotDone> GetEmployeeCheckoutSign(string year, string month, long workshopId);
|
||||
PrintAllContractCheckout GetPrintAllContractDone(string year, string month, long accountId,
|
||||
Task<WorkshopResult> GetWorkshopContractDone(string year, string month, long accountId, List<long> workshopList);
|
||||
Task<WorkshopResult> GetWorkshopContractSignDone(string year, string month, long accountId,
|
||||
List<long> workshopList);
|
||||
PrintAllContractCheckout GetPrintAllContractSignDone(string year, string month, long accountId,
|
||||
Task<WorkshopResult> GetWorkshopCheckoutDone(string year, string month, long accountId, List<long> workshopList);
|
||||
Task<WorkshopResult> GetWorkshopCheckoutSignDone(string year, string month, long accountId,
|
||||
List<long> workshopList);
|
||||
PrintAllContractCheckout GetPrintAllCheckoutDone(string year, string month, long accountId,
|
||||
Task<List<EmployeeNotDone>> GetEmployeeContract(string year, string month, long workshopId);
|
||||
Task<List<EmployeeNotDone>> GetEmployeeContractSign(string year, string month, long workshopId);
|
||||
Task<List<EmployeeNotDone>> GetEmployeeCheckout(string year, string month, long workshopId);
|
||||
Task<List<EmployeeNotDone>> GetEmployeeCheckoutSign(string year, string month, long workshopId);
|
||||
Task<PrintAllContractCheckout> GetPrintAllContractDone(string year, string month, long accountId,
|
||||
List<long> workshopList);
|
||||
PrintAllContractCheckout GetPrintAllCheckoutSignDone(string year, string month, long accountId,
|
||||
Task<PrintAllContractCheckout> GetPrintAllContractSignDone(string year, string month, long accountId,
|
||||
List<long> workshopList);
|
||||
Task<PrintAllContractCheckout> GetPrintAllCheckoutDone(string year, string month, long accountId,
|
||||
List<long> workshopList);
|
||||
Task<PrintAllContractCheckout> GetPrintAllCheckoutSignDone(string year, string month, long accountId,
|
||||
List<long> workshopList);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using AccountManagement.Domain.TaskAgg;
|
||||
using ApkReader;
|
||||
using Company.Domain.AndroidApkVersionAgg;
|
||||
using CompanyManagment.App.Contracts.AndroidApkVersion;
|
||||
using CompanyManagment.Application.Helpers;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using _0_Framework.Application;
|
||||
|
||||
@@ -24,7 +22,7 @@ public class AndroidApkVersionApplication : IAndroidApkVersionApplication
|
||||
_taskRepository = taskRepository;
|
||||
}
|
||||
|
||||
public async Task<OperationResult> CreateAndActive(IFormFile file)
|
||||
public async Task<OperationResult> CreateAndActive(IFormFile file, ApkType apkType, string versionName, string versionCode, bool isForce = false)
|
||||
{
|
||||
OperationResult op = new OperationResult();
|
||||
|
||||
@@ -36,22 +34,26 @@ public class AndroidApkVersionApplication : IAndroidApkVersionApplication
|
||||
if (Path.GetExtension(file.FileName).ToLower() != ".apk")
|
||||
return op.Failed("لطفا فایلی با پسوند .apk وارد کنید");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(versionName))
|
||||
return op.Failed("لطفا نام ورژن را وارد کنید");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(versionCode))
|
||||
return op.Failed("لطفا کد ورژن را وارد کنید");
|
||||
|
||||
#endregion
|
||||
|
||||
var activeApks = _androidApkVersionRepository.GetActives();
|
||||
var activeApks = _androidApkVersionRepository.GetActives(apkType);
|
||||
|
||||
await activeApks.ExecuteUpdateAsync(setter => setter.SetProperty(e => e.IsActive, IsActive.False));
|
||||
_androidApkVersionRepository.SaveChanges();
|
||||
|
||||
var folderName = apkType == ApkType.WebView ? "GozreshgirWebView" : "GozreshgirFaceDetection";
|
||||
var path = Path.Combine($"{_taskRepository.GetWebEnvironmentPath()}", "Storage",
|
||||
"Apk", "Android", "GozreshgirWebView");
|
||||
"Apk", "Android", folderName);
|
||||
|
||||
Directory.CreateDirectory(path);
|
||||
|
||||
//var apk = new ApkReader.ApkReader().Read(file.OpenReadStream());
|
||||
|
||||
string uniqueFileName = $"{Path.GetFileNameWithoutExtension(file.FileName)}{Path.GetExtension(file.FileName)}";
|
||||
|
||||
string uniqueFileName = $"{Path.GetFileNameWithoutExtension(file.FileName)}.v{versionName}{Path.GetExtension(file.FileName)}";
|
||||
|
||||
string filepath = Path.Combine(path, uniqueFileName);
|
||||
|
||||
@@ -60,13 +62,13 @@ public class AndroidApkVersionApplication : IAndroidApkVersionApplication
|
||||
await file.CopyToAsync(stream);
|
||||
}
|
||||
|
||||
var entity = new AndroidApkVersion("0", "0", IsActive.True, filepath);
|
||||
var entity = new AndroidApkVersion(versionName, versionCode, IsActive.True, filepath,apkType,isForce);
|
||||
_androidApkVersionRepository.Create(entity);
|
||||
_androidApkVersionRepository.SaveChanges();
|
||||
return op.Succcedded();
|
||||
}
|
||||
|
||||
public async Task<OperationResult> CreateAndDeActive(IFormFile file)
|
||||
public async Task<OperationResult> CreateAndDeActive(IFormFile file, ApkType apkType, string versionName, string versionCode, bool isForce = false)
|
||||
{
|
||||
OperationResult op = new OperationResult();
|
||||
|
||||
@@ -78,18 +80,22 @@ public class AndroidApkVersionApplication : IAndroidApkVersionApplication
|
||||
if (Path.GetExtension(file.FileName).ToLower() != ".apk")
|
||||
return op.Failed("لطفا فایلی با پسوند .apk وارد کنید");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(versionName))
|
||||
return op.Failed("لطفا نام ورژن را وارد کنید");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(versionCode))
|
||||
return op.Failed("لطفا کد ورژن را وارد کنید");
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
var folderName = apkType == ApkType.WebView ? "GozreshgirWebView" : "GozreshgirFaceDetection";
|
||||
var path = Path.Combine($"{_taskRepository.GetWebEnvironmentPath()}", "Storage",
|
||||
"Apk", "Android", "GozreshgirWebView");
|
||||
"Apk", "Android", folderName);
|
||||
|
||||
Directory.CreateDirectory(path);
|
||||
|
||||
var apk = new ApkReader.ApkReader().Read(file.OpenReadStream());
|
||||
|
||||
string uniqueFileName = $"{Path.GetFileNameWithoutExtension(file.FileName)}.v{apk.VersionName}{Path.GetExtension(file.FileName)}";
|
||||
|
||||
string uniqueFileName = $"{Path.GetFileNameWithoutExtension(file.FileName)}.v{versionName}{Path.GetExtension(file.FileName)}";
|
||||
|
||||
string filepath = Path.Combine(path, uniqueFileName);
|
||||
|
||||
@@ -98,14 +104,15 @@ public class AndroidApkVersionApplication : IAndroidApkVersionApplication
|
||||
await file.CopyToAsync(stream);
|
||||
}
|
||||
|
||||
var entity = new AndroidApkVersion(apk.VersionName, apk.VersionCode, IsActive.False, filepath);
|
||||
var entity = new AndroidApkVersion(versionName, versionCode, IsActive.False, filepath,apkType);
|
||||
_androidApkVersionRepository.Create(entity);
|
||||
_androidApkVersionRepository.SaveChanges();
|
||||
return op.Succcedded();
|
||||
}
|
||||
|
||||
public async Task<string> GetLatestActiveVersionPath()
|
||||
public async Task<string> GetLatestActiveVersionPath(ApkType apkType)
|
||||
{
|
||||
return await _androidApkVersionRepository.GetLatestActiveVersionPath();
|
||||
return await _androidApkVersionRepository.GetLatestActiveVersionPath(apkType);
|
||||
}
|
||||
|
||||
public OperationResult Remove(long id)
|
||||
@@ -127,8 +134,22 @@ public class AndroidApkVersionApplication : IAndroidApkVersionApplication
|
||||
return op.Succcedded();
|
||||
}
|
||||
|
||||
public bool HasAndroidApkToDownload()
|
||||
public bool HasAndroidApkToDownload(ApkType apkType)
|
||||
{
|
||||
return _androidApkVersionRepository.Exists(x => x.IsActive == IsActive.True);
|
||||
return _androidApkVersionRepository.Exists(x => x.IsActive == IsActive.True && x.ApkType == apkType);
|
||||
}
|
||||
|
||||
public Task<AndroidApkVersionInfo> GetLatestActiveInfo(ApkType apkType, int currentVersionCode)
|
||||
{
|
||||
var latest = _androidApkVersionRepository.GetLatestActive(apkType);
|
||||
if (latest == null)
|
||||
return Task.FromResult(new AndroidApkVersionInfo(0, "0", false, false, string.Empty, string.Empty));
|
||||
|
||||
// Return API endpoint for downloading APK file
|
||||
var fileUrl = $"/api/android-apk/download?type={apkType}";
|
||||
int latestCode = 0;
|
||||
int.TryParse(latest.VersionCode, out latestCode);
|
||||
var shouldUpdate = latestCode > currentVersionCode;
|
||||
return Task.FromResult(new AndroidApkVersionInfo(latestCode, latest.VersionName, shouldUpdate, latest.IsForce, fileUrl, "Bug fixes and improvements"));
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ApkReader" Version="2.0.1.1" />
|
||||
<PackageReference Include="AndroidXml" Version="1.1.24" />
|
||||
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -31,6 +31,7 @@ using Company.Domain.LeftWorkAgg;
|
||||
using CompanyManagment.App.Contracts.Employee.DTO;
|
||||
using Company.Domain.EmployeeAuthorizeTempAgg;
|
||||
using Company.Domain.LeftWorkInsuranceAgg;
|
||||
using _0_Framework.Application.FaceEmbedding;
|
||||
|
||||
namespace CompanyManagment.Application;
|
||||
|
||||
@@ -61,29 +62,31 @@ public class EmployeeAplication : RepositoryBase<long, Employee>, IEmployeeAppli
|
||||
private readonly ICustomizeWorkshopGroupSettingsRepository _customizeWorkshopGroupSettingsRepository;
|
||||
private readonly IEmployeeAuthorizeTempRepository _employeeAuthorizeTempRepository;
|
||||
private readonly ILeftWorkInsuranceRepository _leftWorkInsuranceRepository;
|
||||
private readonly IFaceEmbeddingService _faceEmbeddingService;
|
||||
|
||||
public EmployeeAplication(IEmployeeRepository employeeRepository, CompanyContext context, IWorkshopRepository workShopRepository, IWebHostEnvironment webHostEnvironment, IRollCallEmployeeStatusApplication rollCallEmployeeStatusApplication, IRollCallEmployeeRepository rollCallEmployeeRepository, ICustomizeWorkshopSettingsApplication customizeWorkshopSettingsApplication, IEmployeeDocumentsApplication employeeDocumentsApplication, IEmployeeDocumentsRepository employeeDocumentsRepository, IEmployeeBankInformationApplication employeeBankInformationApplication, ILeftWorkTempRepository leftWorkTempRepository, IUidService uidService, ICustomizeWorkshopEmployeeSettingsRepository customizeWorkshopEmployeeSettingsRepository, IPersonnelCodeRepository personnelCodeRepository, IEmployeeClientTempRepository employeeClientTempRepository, ICustomizeWorkshopGroupSettingsRepository customizeWorkshopGroupSettingsRepository, ILeftWorkRepository leftWorkRepository, IEmployeeAuthorizeTempRepository employeeAuthorizeTempRepository, ILeftWorkInsuranceRepository leftWorkInsuranceRepository) : base(context)
|
||||
{
|
||||
_context = context;
|
||||
_WorkShopRepository = workShopRepository;
|
||||
_webHostEnvironment = webHostEnvironment;
|
||||
_rollCallEmployeeStatusApplication = rollCallEmployeeStatusApplication;
|
||||
_rollCallEmployeeRepository = rollCallEmployeeRepository;
|
||||
_customizeWorkshopSettingsApplication = customizeWorkshopSettingsApplication;
|
||||
_employeeDocumentsApplication = employeeDocumentsApplication;
|
||||
_employeeBankInformationApplication = employeeBankInformationApplication;
|
||||
_leftWorkTempRepository = leftWorkTempRepository;
|
||||
_uidService = uidService;
|
||||
_customizeWorkshopEmployeeSettingsRepository = customizeWorkshopEmployeeSettingsRepository;
|
||||
_personnelCodeRepository = personnelCodeRepository;
|
||||
_employeeClientTempRepository = employeeClientTempRepository;
|
||||
_leftWorkRepository = leftWorkRepository;
|
||||
_employeeAuthorizeTempRepository = employeeAuthorizeTempRepository;
|
||||
_leftWorkInsuranceRepository = leftWorkInsuranceRepository;
|
||||
_EmployeeRepository = employeeRepository;
|
||||
}
|
||||
public EmployeeAplication(IEmployeeRepository employeeRepository, CompanyContext context, IWorkshopRepository workShopRepository, IWebHostEnvironment webHostEnvironment, IRollCallEmployeeStatusApplication rollCallEmployeeStatusApplication, IRollCallEmployeeRepository rollCallEmployeeRepository, ICustomizeWorkshopSettingsApplication customizeWorkshopSettingsApplication, IEmployeeDocumentsApplication employeeDocumentsApplication, IEmployeeDocumentsRepository employeeDocumentsRepository, IEmployeeBankInformationApplication employeeBankInformationApplication, ILeftWorkTempRepository leftWorkTempRepository, IUidService uidService, ICustomizeWorkshopEmployeeSettingsRepository customizeWorkshopEmployeeSettingsRepository, IPersonnelCodeRepository personnelCodeRepository, IEmployeeClientTempRepository employeeClientTempRepository, ICustomizeWorkshopGroupSettingsRepository customizeWorkshopGroupSettingsRepository, ILeftWorkRepository leftWorkRepository, IEmployeeAuthorizeTempRepository employeeAuthorizeTempRepository, ILeftWorkInsuranceRepository leftWorkInsuranceRepository, IFaceEmbeddingService faceEmbeddingService) : base(context)
|
||||
{
|
||||
_context = context;
|
||||
_WorkShopRepository = workShopRepository;
|
||||
_webHostEnvironment = webHostEnvironment;
|
||||
_rollCallEmployeeStatusApplication = rollCallEmployeeStatusApplication;
|
||||
_rollCallEmployeeRepository = rollCallEmployeeRepository;
|
||||
_customizeWorkshopSettingsApplication = customizeWorkshopSettingsApplication;
|
||||
_employeeDocumentsApplication = employeeDocumentsApplication;
|
||||
_employeeBankInformationApplication = employeeBankInformationApplication;
|
||||
_leftWorkTempRepository = leftWorkTempRepository;
|
||||
_uidService = uidService;
|
||||
_customizeWorkshopEmployeeSettingsRepository = customizeWorkshopEmployeeSettingsRepository;
|
||||
_personnelCodeRepository = personnelCodeRepository;
|
||||
_employeeClientTempRepository = employeeClientTempRepository;
|
||||
_leftWorkRepository = leftWorkRepository;
|
||||
_employeeAuthorizeTempRepository = employeeAuthorizeTempRepository;
|
||||
_leftWorkInsuranceRepository = leftWorkInsuranceRepository;
|
||||
_EmployeeRepository = employeeRepository;
|
||||
_faceEmbeddingService = faceEmbeddingService;
|
||||
}
|
||||
|
||||
public OperationResult Create(CreateEmployee command)
|
||||
public OperationResult Create(CreateEmployee command)
|
||||
{
|
||||
var opration = new OperationResult();
|
||||
if (_EmployeeRepository.Exists(x =>
|
||||
@@ -1122,6 +1125,12 @@ public class EmployeeAplication : RepositoryBase<long, Employee>, IEmployeeAppli
|
||||
rollCallEmployee.HasImage();
|
||||
_rollCallEmployeeRepository.Create(rollCallEmployee);
|
||||
_rollCallEmployeeRepository.SaveChanges();
|
||||
string employeeFullName = employee.FName + " " + employee.LName;
|
||||
var res = _faceEmbeddingService.GenerateEmbeddingsAsync(employee.id,command.WorkshopId,employeeFullName, filePath1,filePath2).GetAwaiter().GetResult();
|
||||
if (!res.IsSuccedded)
|
||||
{
|
||||
return op.Failed(res.Message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
170
CompanyManagment.Application/EmployeeFaceEmbeddingApplication.cs
Normal file
170
CompanyManagment.Application/EmployeeFaceEmbeddingApplication.cs
Normal file
@@ -0,0 +1,170 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Company.Domain.EmployeeFaceEmbeddingAgg;
|
||||
using CompanyManagment.App.Contracts.EmployeeFaceEmbedding;
|
||||
|
||||
namespace CompanyManagment.Application;
|
||||
|
||||
public class EmployeeFaceEmbeddingApplication : IEmployeeFaceEmbeddingApplication
|
||||
{
|
||||
private readonly IEmployeeFaceEmbeddingRepository _repository;
|
||||
|
||||
public EmployeeFaceEmbeddingApplication(IEmployeeFaceEmbeddingRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<EmployeeFaceEmbeddingDto> GetByIdAsync(string id)
|
||||
{
|
||||
var entity = await _repository.GetByIdAsync(id);
|
||||
return entity == null ? null : MapToDto(entity);
|
||||
}
|
||||
|
||||
public async Task<EmployeeFaceEmbeddingDto> GetByEmployeeIdAsync(long employeeId)
|
||||
{
|
||||
var entity = await _repository.GetByEmployeeIdAsync(employeeId);
|
||||
return entity == null ? null : MapToDto(entity);
|
||||
}
|
||||
|
||||
public async Task<List<EmployeeFaceEmbeddingDto>> GetByWorkshopIdAsync(long workshopId)
|
||||
{
|
||||
var entities = await _repository.GetByWorkshopIdAsync(workshopId);
|
||||
return entities.Select(MapToDto).ToList();
|
||||
}
|
||||
|
||||
public async Task<List<EmployeeFaceEmbeddingDto>> GetByWorkshopIdsAsync(List<long> workshopIds)
|
||||
{
|
||||
var entities = await _repository.GetByWorkshopIdsAsync(workshopIds);
|
||||
return entities.Select(MapToDto).ToList();
|
||||
}
|
||||
|
||||
public async Task<string> SaveAsync(EmployeeFaceEmbeddingDto dto)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(dto.Id))
|
||||
{
|
||||
// ایجاد جدید
|
||||
var metadata = MapMetadataFromDto(dto.Metadata);
|
||||
var entity = new EmployeeFaceEmbedding(
|
||||
dto.EmployeeFullName,
|
||||
dto.EmployeeId,
|
||||
dto.WorkshopId,
|
||||
dto.Embeddings,
|
||||
metadata
|
||||
);
|
||||
|
||||
await _repository.CreateAsync(entity);
|
||||
return entity.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
// بهروزرسانی
|
||||
var entity = await _repository.GetByIdAsync(dto.Id);
|
||||
if (entity == null)
|
||||
throw new Exception($"EmployeeFaceEmbedding با شناسه {dto.Id} یافت نشد");
|
||||
|
||||
entity.UpdateEmployeeInfo(dto.EmployeeFullName, dto.WorkshopId);
|
||||
entity.UpdateEmbedding(dto.Embeddings, 0, 0);
|
||||
entity.UpdateMetadata(MapMetadataFromDto(dto.Metadata), 0, 0);
|
||||
|
||||
await _repository.UpdateAsync(entity);
|
||||
return entity.Id;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(string id)
|
||||
{
|
||||
await _repository.DeleteAsync(id);
|
||||
}
|
||||
|
||||
#region Mapping Methods
|
||||
|
||||
private EmployeeFaceEmbeddingDto MapToDto(EmployeeFaceEmbedding entity)
|
||||
{
|
||||
return new EmployeeFaceEmbeddingDto
|
||||
{
|
||||
Id = entity.Id,
|
||||
EmployeeFullName = entity.EmployeeFullName,
|
||||
EmployeeId = entity.EmployeeId,
|
||||
WorkshopId = entity.WorkshopId,
|
||||
Embeddings = entity.Embeddings,
|
||||
Metadata = MapMetadataToDto(entity.Metadata)
|
||||
};
|
||||
}
|
||||
|
||||
private EmployeeFaceEmbeddingMetadataDto MapMetadataToDto(EmployeeFaceEmbeddingMetadata metadata)
|
||||
{
|
||||
if (metadata == null) return null;
|
||||
|
||||
return new EmployeeFaceEmbeddingMetadataDto
|
||||
{
|
||||
AvgEyeDistanceNormalized = metadata.AvgEyeDistanceNormalized,
|
||||
AvgEyeToFaceRatio = metadata.AvgEyeToFaceRatio,
|
||||
AvgFaceAspectRatio = metadata.AvgFaceAspectRatio,
|
||||
AvgDetectionConfidence = metadata.AvgDetectionConfidence,
|
||||
AvgKeypointsNormalized = MapKeypointsToDto(metadata.AvgKeypointsNormalized),
|
||||
PerImageMetadata = metadata.PerImageMetadata?.Select(x => new ImageMetadataDto
|
||||
{
|
||||
FaceAspectRatio = x.FaceAspectRatio,
|
||||
EyeDistanceNormalized = x.EyeDistanceNormalized,
|
||||
EyeToFaceRatio = x.EyeToFaceRatio,
|
||||
DetectionConfidence = x.DetectionConfidence,
|
||||
KeypointsNormalized = MapKeypointsToDto(x.KeypointsNormalized)
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
private EmployeeFaceEmbeddingKeypointsDto MapKeypointsToDto(EmployeeFaceEmbeddingKeypoints keypoints)
|
||||
{
|
||||
if (keypoints == null) return null;
|
||||
|
||||
return new EmployeeFaceEmbeddingKeypointsDto
|
||||
{
|
||||
LeftEye = keypoints.LeftEye,
|
||||
RightEye = keypoints.RightEye,
|
||||
Nose = keypoints.Nose,
|
||||
MouthLeft = keypoints.MouthLeft,
|
||||
MouthRight = keypoints.MouthRight
|
||||
};
|
||||
}
|
||||
|
||||
private EmployeeFaceEmbeddingMetadata MapMetadataFromDto(EmployeeFaceEmbeddingMetadataDto dto)
|
||||
{
|
||||
if (dto == null) return null;
|
||||
|
||||
return new EmployeeFaceEmbeddingMetadata
|
||||
{
|
||||
AvgEyeDistanceNormalized = dto.AvgEyeDistanceNormalized,
|
||||
AvgEyeToFaceRatio = dto.AvgEyeToFaceRatio,
|
||||
AvgFaceAspectRatio = dto.AvgFaceAspectRatio,
|
||||
AvgDetectionConfidence = dto.AvgDetectionConfidence,
|
||||
AvgKeypointsNormalized = MapKeypointsFromDto(dto.AvgKeypointsNormalized),
|
||||
PerImageMetadata = dto.PerImageMetadata?.Select(x => new ImageMetadata
|
||||
{
|
||||
FaceAspectRatio = x.FaceAspectRatio,
|
||||
EyeDistanceNormalized = x.EyeDistanceNormalized,
|
||||
EyeToFaceRatio = x.EyeToFaceRatio,
|
||||
DetectionConfidence = x.DetectionConfidence,
|
||||
KeypointsNormalized = MapKeypointsFromDto(x.KeypointsNormalized)
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
private EmployeeFaceEmbeddingKeypoints MapKeypointsFromDto(EmployeeFaceEmbeddingKeypointsDto dto)
|
||||
{
|
||||
if (dto == null) return null;
|
||||
|
||||
return new EmployeeFaceEmbeddingKeypoints
|
||||
{
|
||||
LeftEye = dto.LeftEye,
|
||||
RightEye = dto.RightEye,
|
||||
Nose = dto.Nose,
|
||||
MouthLeft = dto.MouthLeft,
|
||||
MouthRight = dto.MouthRight
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
132
CompanyManagment.Application/Helpers/ApkHelper.cs
Normal file
132
CompanyManagment.Application/Helpers/ApkHelper.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace CompanyManagment.Application.Helpers
|
||||
{
|
||||
public class ApkInfo
|
||||
{
|
||||
public string VersionName { get; set; } = string.Empty;
|
||||
public string VersionCode { get; set; } = string.Empty;
|
||||
public string PackageName { get; set; } = string.Empty;
|
||||
public string ApplicationLabel { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public static class ApkHelper
|
||||
{
|
||||
public static ApkInfo ReadApkInfo(Stream apkStream)
|
||||
{
|
||||
var apkInfo = new ApkInfo();
|
||||
|
||||
try
|
||||
{
|
||||
using var archive = new ZipArchive(apkStream, ZipArchiveMode.Read, true);
|
||||
var manifestEntry = archive.GetEntry("AndroidManifest.xml");
|
||||
|
||||
if (manifestEntry == null)
|
||||
{
|
||||
throw new InvalidOperationException("AndroidManifest.xml not found in APK file");
|
||||
}
|
||||
|
||||
using var manifestStream = manifestEntry.Open();
|
||||
using var memoryStream = new MemoryStream();
|
||||
manifestStream.CopyTo(memoryStream);
|
||||
var manifestBytes = memoryStream.ToArray();
|
||||
|
||||
// Parse the binary AndroidManifest.xml
|
||||
apkInfo = ParseBinaryManifest(manifestBytes);
|
||||
|
||||
// Validate that we found at least version information
|
||||
if (string.IsNullOrEmpty(apkInfo.VersionCode) && string.IsNullOrEmpty(apkInfo.VersionName))
|
||||
{
|
||||
throw new InvalidOperationException("Could not extract version information from APK");
|
||||
}
|
||||
|
||||
return apkInfo;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException($"Error reading APK file: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static ApkInfo ParseBinaryManifest(byte[] manifestBytes)
|
||||
{
|
||||
var apkInfo = new ApkInfo();
|
||||
|
||||
try
|
||||
{
|
||||
// Convert bytes to string for regex parsing
|
||||
var text = System.Text.Encoding.UTF8.GetString(manifestBytes);
|
||||
|
||||
// Extract package name
|
||||
var packageMatch = Regex.Match(text, @"package[^a-zA-Z]+([a-zA-Z][a-zA-Z0-9_]*(?:\.[a-zA-Z][a-zA-Z0-9_]*)+)", RegexOptions.IgnoreCase);
|
||||
if (packageMatch.Success)
|
||||
{
|
||||
apkInfo.PackageName = packageMatch.Groups[1].Value;
|
||||
}
|
||||
|
||||
// Extract version code - look for numeric values that could be version codes
|
||||
var versionCodeMatch = Regex.Match(text, @"versionCode[^\d]*(\d+)", RegexOptions.IgnoreCase);
|
||||
if (versionCodeMatch.Success)
|
||||
{
|
||||
apkInfo.VersionCode = versionCodeMatch.Groups[1].Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback: try to find reasonable numeric values
|
||||
var numbers = Regex.Matches(text, @"\b(\d{1,8})\b");
|
||||
foreach (Match numMatch in numbers)
|
||||
{
|
||||
if (int.TryParse(numMatch.Groups[1].Value, out int num) && num > 0 && num < 99999999)
|
||||
{
|
||||
apkInfo.VersionCode = num.ToString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract version name
|
||||
var versionNameMatch = Regex.Match(text, @"versionName[^\d]*(\d+(?:\.\d+)*(?:\.\d+)*)", RegexOptions.IgnoreCase);
|
||||
if (versionNameMatch.Success)
|
||||
{
|
||||
apkInfo.VersionName = versionNameMatch.Groups[1].Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback: look for version-like patterns
|
||||
var versionMatch = Regex.Match(text, @"\b(\d+\.\d+(?:\.\d+)*)\b");
|
||||
if (versionMatch.Success)
|
||||
{
|
||||
apkInfo.VersionName = versionMatch.Groups[1].Value;
|
||||
}
|
||||
}
|
||||
|
||||
// Set defaults if nothing found
|
||||
if (string.IsNullOrEmpty(apkInfo.VersionCode))
|
||||
apkInfo.VersionCode = "1";
|
||||
|
||||
if (string.IsNullOrEmpty(apkInfo.VersionName))
|
||||
apkInfo.VersionName = "1.0";
|
||||
|
||||
if (string.IsNullOrEmpty(apkInfo.PackageName))
|
||||
apkInfo.PackageName = "unknown.package";
|
||||
|
||||
return apkInfo;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Return default values if parsing fails
|
||||
return new ApkInfo
|
||||
{
|
||||
VersionCode = "1",
|
||||
VersionName = "1.0",
|
||||
PackageName = "unknown.package"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1140,7 +1140,7 @@ public class InstitutionContractApplication : IInstitutionContractApplication
|
||||
CreateContractingPartyAccount(contractingParty.id, res.SendId);
|
||||
|
||||
|
||||
await _smsService.SendInstitutionVerificationLink(contractingParty.Phone, contractingPartyFullName,
|
||||
await _smsService.SendInstitutionCreationVerificationLink(contractingParty.Phone, contractingPartyFullName,
|
||||
entity.PublicId, contractingParty.id,entity.id );
|
||||
|
||||
await _institutionContractRepository.SaveChangesAsync();
|
||||
@@ -1377,7 +1377,7 @@ public class InstitutionContractApplication : IInstitutionContractApplication
|
||||
if (contractingParty == null)
|
||||
throw new NotFoundException("طرف قرارداد یافت نشد");
|
||||
var contractingPartyFullName = contractingParty.FName + " " + contractingParty.LName;
|
||||
await _smsService.SendInstitutionVerificationLink(contractingParty.Phone, contractingPartyFullName,
|
||||
await _smsService.SendInstitutionCreationVerificationLink(contractingParty.Phone, contractingPartyFullName,
|
||||
institutionContract.PublicId, contractingParty.id, institutionContract.id);
|
||||
return new OperationResult().Succcedded();
|
||||
}
|
||||
@@ -1387,6 +1387,11 @@ public class InstitutionContractApplication : IInstitutionContractApplication
|
||||
return (await _institutionContractRepository.PrintAllAsync([id])).FirstOrDefault();
|
||||
}
|
||||
|
||||
public Task<GetInstitutionContractWorkshopsDetails> GetContractWorkshopsDetails(long id)
|
||||
{
|
||||
return _institutionContractRepository.GetContractWorkshopsDetails(id);
|
||||
}
|
||||
|
||||
|
||||
private async Task<OperationResult<PersonalContractingParty>> CreateLegalContractingPartyEntity(
|
||||
CreateInstitutionContractLegalPartyRequest request, long representativeId, string address, string city,
|
||||
|
||||
@@ -50,7 +50,7 @@ public class PaymentTransactionApplication : IPaymentTransactionApplication
|
||||
command.ContractingPartyId,
|
||||
command.Amount,
|
||||
contractingPartyName,
|
||||
command.CallBackUrl);
|
||||
command.CallBackUrl,command.Gateway);
|
||||
|
||||
await _paymentTransactionRepository.CreateAsync(entity);
|
||||
await _paymentTransactionRepository.SaveChangesAsync();
|
||||
@@ -87,7 +87,7 @@ public class PaymentTransactionApplication : IPaymentTransactionApplication
|
||||
return op.Succcedded();
|
||||
}
|
||||
|
||||
public OperationResult SetSuccess(long paymentTransactionId,string cardNumber, string bankName)
|
||||
public OperationResult SetSuccess(long paymentTransactionId,string cardNumber, string bankName, string rrn, string digitalReceipt)
|
||||
{
|
||||
var op = new OperationResult();
|
||||
|
||||
@@ -97,7 +97,7 @@ public class PaymentTransactionApplication : IPaymentTransactionApplication
|
||||
{
|
||||
return op.Failed("تراکنش مورد نظر یافت نشد");
|
||||
}
|
||||
paymentTransaction.SetPaid(cardNumber, bankName);
|
||||
paymentTransaction.SetPaid(cardNumber, bankName,rrn, digitalReceipt);
|
||||
_paymentTransactionRepository.SaveChanges();
|
||||
|
||||
return op.Succcedded();
|
||||
|
||||
@@ -29,67 +29,67 @@ public class ReportApplication : IReportApplication
|
||||
return await _reportRepository.GetAllActiveWorkshopsNew(year, month);
|
||||
}
|
||||
|
||||
public WorkshopResult GetWorkshopContractDone(string year, string month, long accountId, List<long> workshopList)
|
||||
public async Task<WorkshopResult> GetWorkshopContractDone(string year, string month, long accountId, List<long> workshopList)
|
||||
{
|
||||
return _reportRepository.GetWorkshopContractDone(year, month, accountId, workshopList);
|
||||
return await _reportRepository.GetWorkshopContractDone(year, month, accountId, workshopList);
|
||||
}
|
||||
|
||||
public WorkshopResult GetWorkshopContractSignDone(string year, string month, long accountId, List<long> workshopList)
|
||||
public async Task<WorkshopResult> GetWorkshopContractSignDone(string year, string month, long accountId, List<long> workshopList)
|
||||
{
|
||||
return _reportRepository.GetWorkshopContractSignDone(year, month, accountId, workshopList);
|
||||
return await _reportRepository.GetWorkshopContractSignDone(year, month, accountId, workshopList);
|
||||
}
|
||||
|
||||
public WorkshopResult GetWorkshopCheckoutDone(string year, string month, long accountId, List<long> workshopList)
|
||||
public async Task<WorkshopResult> GetWorkshopCheckoutDone(string year, string month, long accountId, List<long> workshopList)
|
||||
{
|
||||
return _reportRepository.GetWorkshopCheckoutDone(year, month, accountId, workshopList);
|
||||
return await _reportRepository.GetWorkshopCheckoutDone(year, month, accountId, workshopList);
|
||||
}
|
||||
|
||||
public WorkshopResult GetWorkshopCheckoutSignDone(string year, string month, long accountId, List<long> workshopList)
|
||||
public async Task<WorkshopResult> GetWorkshopCheckoutSignDone(string year, string month, long accountId, List<long> workshopList)
|
||||
{
|
||||
return _reportRepository.GetWorkshopCheckoutSignDone(year, month, accountId, workshopList);
|
||||
return await _reportRepository.GetWorkshopCheckoutSignDone(year, month, accountId, workshopList);
|
||||
}
|
||||
|
||||
public List<EmployeeNotDone> GetEmployeeContract(string year, string month, long workshopId)
|
||||
public async Task<List<EmployeeNotDone>> GetEmployeeContract(string year, string month, long workshopId)
|
||||
{
|
||||
return _reportRepository.GetEmployeeContract(year, month, workshopId);
|
||||
return await _reportRepository.GetEmployeeContract(year, month, workshopId);
|
||||
}
|
||||
|
||||
public List<EmployeeNotDone> GetEmployeeContractSign(string year, string month, long workshopId)
|
||||
public async Task<List<EmployeeNotDone>> GetEmployeeContractSign(string year, string month, long workshopId)
|
||||
{
|
||||
return _reportRepository.GetEmployeeContractSign(year, month, workshopId);
|
||||
return await _reportRepository.GetEmployeeContractSign(year, month, workshopId);
|
||||
}
|
||||
|
||||
public List<EmployeeNotDone> GetEmployeeCheckout(string year, string month, long workshopId)
|
||||
public async Task<List<EmployeeNotDone>> GetEmployeeCheckout(string year, string month, long workshopId)
|
||||
{
|
||||
return _reportRepository.GetEmployeeCheckout(year, month, workshopId);
|
||||
return await _reportRepository.GetEmployeeCheckout(year, month, workshopId);
|
||||
}
|
||||
|
||||
public List<EmployeeNotDone> GetEmployeeCheckoutSign(string year, string month, long workshopId)
|
||||
public async Task<List<EmployeeNotDone>> GetEmployeeCheckoutSign(string year, string month, long workshopId)
|
||||
{
|
||||
return _reportRepository.GetEmployeeCheckoutSign(year, month, workshopId);
|
||||
return await _reportRepository.GetEmployeeCheckoutSign(year, month, workshopId);
|
||||
}
|
||||
|
||||
#region Print
|
||||
|
||||
public PrintAllContractCheckout GetPrintAllContractDone(string year, string month, long accountId,
|
||||
public async Task<PrintAllContractCheckout> GetPrintAllContractDone(string year, string month, long accountId,
|
||||
List<long> workshopList)
|
||||
{
|
||||
return _reportRepository.GetPrintAllContractDone(year, month, accountId, workshopList);
|
||||
return await _reportRepository.GetPrintAllContractDone(year, month, accountId, workshopList);
|
||||
}
|
||||
public PrintAllContractCheckout GetPrintAllContractSignDone(string year, string month, long accountId,
|
||||
public async Task<PrintAllContractCheckout> GetPrintAllContractSignDone(string year, string month, long accountId,
|
||||
List<long> workshopList)
|
||||
{
|
||||
return _reportRepository.GetPrintAllContractSignDone(year, month, accountId, workshopList);
|
||||
return await _reportRepository.GetPrintAllContractSignDone(year, month, accountId, workshopList);
|
||||
}
|
||||
public PrintAllContractCheckout GetPrintAllCheckoutDone(string year, string month, long accountId,
|
||||
public async Task<PrintAllContractCheckout> GetPrintAllCheckoutDone(string year, string month, long accountId,
|
||||
List<long> workshopList)
|
||||
{
|
||||
return _reportRepository.GetPrintAllCheckoutDone(year, month, accountId, workshopList);
|
||||
return await _reportRepository.GetPrintAllCheckoutDone(year, month, accountId, workshopList);
|
||||
}
|
||||
public PrintAllContractCheckout GetPrintAllCheckoutSignDone(string year, string month, long accountId,
|
||||
public async Task<PrintAllContractCheckout> GetPrintAllCheckoutSignDone(string year, string month, long accountId,
|
||||
List<long> workshopList)
|
||||
{
|
||||
return _reportRepository.GetPrintAllCheckoutSignDone(year, month, accountId, workshopList);
|
||||
return await _reportRepository.GetPrintAllCheckoutSignDone(year, month, accountId, workshopList);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.QualityTools.Testing.Fakes" Version="16.11.230815" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="3.5.0" />
|
||||
<PackageReference Include="Parbad.Gateway.Sepehr" Version="1.7.0" />
|
||||
<PackageReference Include="PersianTools.Core" Version="2.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
|
||||
using Company.Domain.AndroidApkVersionAgg;
|
||||
using Company.Domain.AndroidApkVersionAgg;
|
||||
using CompanyManagment.App.Contracts.AndroidApkVersion;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Hosting.Builder;
|
||||
using _0_Framework.Application;
|
||||
|
||||
namespace CompanyManagment.EFCore.Mapping;
|
||||
@@ -19,10 +18,15 @@ public class AndroidApkVersionMapping:IEntityTypeConfiguration<AndroidApkVersion
|
||||
v => v.ToString(),
|
||||
v => (IsActive)Enum.Parse(typeof(IsActive), v)).HasMaxLength(5);
|
||||
|
||||
builder.Property(x => x.Title).HasMaxLength(50);
|
||||
builder.Property(x => x.ApkType).HasConversion(
|
||||
v => v.ToString(),
|
||||
v => (ApkType)Enum.Parse(typeof(ApkType), v)).HasMaxLength(20);
|
||||
|
||||
builder.Property(x => x.Title).HasMaxLength(200);
|
||||
builder.Property(x => x.VersionCode).HasMaxLength(20);
|
||||
builder.Property(x => x.VersionName).HasMaxLength(35);
|
||||
builder.Property(x => x.Path).HasMaxLength(255);
|
||||
builder.Property(x => x.IsForce);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,12 @@ public class PaymentTransactionMapping:IEntityTypeConfiguration<PaymentTransacti
|
||||
builder.Property(x => x.CardNumber).HasMaxLength(25);
|
||||
builder.Property(x => x.BankName).HasMaxLength(50);
|
||||
builder.Property(x => x.Status).HasConversion<string>().HasMaxLength(35);
|
||||
builder.Property(x => x.Gateway).HasConversion<string>().HasMaxLength(35);
|
||||
builder.Property(x => x.ContractingPartyName).HasMaxLength(255);
|
||||
builder.Property(x => x.CallBackUrl).HasMaxLength(500);
|
||||
builder.Property(x => x.Rrn).HasMaxLength(50);
|
||||
builder.Property(x => x.DigitalReceipt).HasMaxLength(50);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
11121
CompanyManagment.EFCore/Migrations/20251112143907_addGateways in transaction .Designer.cs
generated
Normal file
11121
CompanyManagment.EFCore/Migrations/20251112143907_addGateways in transaction .Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CompanyManagment.EFCore.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class addGatewaysintransaction : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Gateway",
|
||||
table: "PaymentTransactions",
|
||||
type: "nvarchar(35)",
|
||||
maxLength: 35,
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Gateway",
|
||||
table: "PaymentTransactions");
|
||||
}
|
||||
}
|
||||
}
|
||||
11129
CompanyManagment.EFCore/Migrations/20251112151518_add rrn and digital receipt.Designer.cs
generated
Normal file
11129
CompanyManagment.EFCore/Migrations/20251112151518_add rrn and digital receipt.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CompanyManagment.EFCore.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class addrrnanddigitalreceipt : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "DigitalReceipt",
|
||||
table: "PaymentTransactions",
|
||||
type: "nvarchar(50)",
|
||||
maxLength: 50,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Rrn",
|
||||
table: "PaymentTransactions",
|
||||
type: "nvarchar(50)",
|
||||
maxLength: 50,
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DigitalReceipt",
|
||||
table: "PaymentTransactions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Rrn",
|
||||
table: "PaymentTransactions");
|
||||
}
|
||||
}
|
||||
}
|
||||
11134
CompanyManagment.EFCore/Migrations/20251115161128_AddApkTypeToAndroidApkVersion.Designer.cs
generated
Normal file
11134
CompanyManagment.EFCore/Migrations/20251115161128_AddApkTypeToAndroidApkVersion.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CompanyManagment.EFCore.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddApkTypeToAndroidApkVersion : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ApkType",
|
||||
table: "AndroidApkVersions",
|
||||
type: "nvarchar(20)",
|
||||
maxLength: 20,
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ApkType",
|
||||
table: "AndroidApkVersions");
|
||||
}
|
||||
}
|
||||
}
|
||||
11137
CompanyManagment.EFCore/Migrations/20251116081057_AddIsForceFieldToAndroidApkVersions.Designer.cs
generated
Normal file
11137
CompanyManagment.EFCore/Migrations/20251116081057_AddIsForceFieldToAndroidApkVersions.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CompanyManagment.EFCore.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddIsForceFieldToAndroidApkVersions : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "IsForce",
|
||||
table: "AndroidApkVersions",
|
||||
type: "bit",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "IsForce",
|
||||
table: "AndroidApkVersions");
|
||||
}
|
||||
}
|
||||
}
|
||||
11137
CompanyManagment.EFCore/Migrations/20251116194223_add title max length in android apk.Designer.cs
generated
Normal file
11137
CompanyManagment.EFCore/Migrations/20251116194223_add title max length in android apk.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CompanyManagment.EFCore.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class addtitlemaxlengthinandroidapk : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Title",
|
||||
table: "AndroidApkVersions",
|
||||
type: "nvarchar(200)",
|
||||
maxLength: 200,
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(50)",
|
||||
oldMaxLength: 50,
|
||||
oldNullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Title",
|
||||
table: "AndroidApkVersions",
|
||||
type: "nvarchar(50)",
|
||||
maxLength: 50,
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(200)",
|
||||
oldMaxLength: 200,
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,11 @@ namespace CompanyManagment.EFCore.Migrations
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("id"));
|
||||
|
||||
b.Property<string>("ApkType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<DateTime>("CreationDate")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
@@ -69,13 +74,16 @@ namespace CompanyManagment.EFCore.Migrations
|
||||
.HasMaxLength(5)
|
||||
.HasColumnType("nvarchar(5)");
|
||||
|
||||
b.Property<bool>("IsForce")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Path")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)");
|
||||
|
||||
b.Property<string>("VersionCode")
|
||||
.HasMaxLength(20)
|
||||
@@ -4990,6 +4998,19 @@ namespace CompanyManagment.EFCore.Migrations
|
||||
b.Property<DateTime>("CreationDate")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("DigitalReceipt")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Gateway")
|
||||
.IsRequired()
|
||||
.HasMaxLength(35)
|
||||
.HasColumnType("nvarchar(35)");
|
||||
|
||||
b.Property<string>("Rrn")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(35)
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Threading.Tasks;
|
||||
using _0_Framework.Application;
|
||||
using _0_Framework.InfraStructure;
|
||||
using Company.Domain.AndroidApkVersionAgg;
|
||||
using CompanyManagment.App.Contracts.AndroidApkVersion;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CompanyManagment.EFCore.Repository;
|
||||
@@ -15,13 +16,21 @@ public class AndroidApkVersionRepository:RepositoryBase<long, AndroidApkVersion>
|
||||
_companyContext = companyContext;
|
||||
}
|
||||
|
||||
public IQueryable<AndroidApkVersion> GetActives()
|
||||
public IQueryable<AndroidApkVersion> GetActives(ApkType apkType)
|
||||
{
|
||||
return _companyContext.AndroidApkVersions.Where(x => x.IsActive == IsActive.True);
|
||||
return _companyContext.AndroidApkVersions.Where(x => x.IsActive == IsActive.True && x.ApkType == apkType);
|
||||
}
|
||||
|
||||
public async Task<string> GetLatestActiveVersionPath()
|
||||
public async Task<string> GetLatestActiveVersionPath(ApkType apkType)
|
||||
{
|
||||
return (await _companyContext.AndroidApkVersions.OrderByDescending(x=>x.CreationDate).FirstOrDefaultAsync(x => x.IsActive == IsActive.True)).Path;
|
||||
return (await _companyContext.AndroidApkVersions.OrderByDescending(x=>x.CreationDate).FirstOrDefaultAsync(x => x.IsActive == IsActive.True && x.ApkType == apkType)).Path;
|
||||
}
|
||||
|
||||
public AndroidApkVersion GetLatestActive(ApkType apkType)
|
||||
{
|
||||
return _companyContext.AndroidApkVersions
|
||||
.Where(x => x.IsActive == IsActive.True && x.ApkType == apkType)
|
||||
.OrderByDescending(x => x.CreationDate)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
@@ -19,39 +19,41 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CompanyManagment.EFCore.Repository;
|
||||
|
||||
public class CustomizeWorkshopGroupSettingsRepository(CompanyContext companyContext, IRollCallEmployeeRepository _rollCallEmployeeRepository)
|
||||
public class CustomizeWorkshopGroupSettingsRepository(
|
||||
CompanyContext companyContext,
|
||||
IRollCallEmployeeRepository _rollCallEmployeeRepository)
|
||||
: RepositoryBase<long, CustomizeWorkshopGroupSettings>(companyContext),
|
||||
ICustomizeWorkshopGroupSettingsRepository
|
||||
{
|
||||
private readonly CompanyContext _companyContext = companyContext;
|
||||
private readonly IRollCallEmployeeRepository _rollCallEmployeeRepository = _rollCallEmployeeRepository;
|
||||
|
||||
public CustomizeWorkshopGroupSettings GetIncludeWorkshopSettings(long id)
|
||||
{
|
||||
|
||||
return _companyContext.CustomizeWorkshopGroupSettings.AsSplitQuery().Include(x => x.CustomizeWorkshopSettings)
|
||||
.FirstOrDefault(x => x.id == id);
|
||||
.FirstOrDefault(x => x.id == id);
|
||||
}
|
||||
|
||||
public CustomizeWorkshopGroupSettings GetWorkshopMainGroup(long workshopId)
|
||||
{
|
||||
return _companyContext.CustomizeWorkshopGroupSettings
|
||||
.Include(x=>x.CustomizeWorkshopSettings)
|
||||
.Include(x=> x.CustomizeWorkshopEmployeeSettingsCollection)
|
||||
.AsSplitQuery().FirstOrDefault(x => x.MainGroup && x.CustomizeWorkshopSettings.WorkshopId == workshopId);
|
||||
return _companyContext.CustomizeWorkshopGroupSettings
|
||||
.Include(x => x.CustomizeWorkshopSettings)
|
||||
.Include(x => x.CustomizeWorkshopEmployeeSettingsCollection)
|
||||
.AsSplitQuery().FirstOrDefault(x => x.MainGroup && x.CustomizeWorkshopSettings.WorkshopId == workshopId);
|
||||
}
|
||||
|
||||
public List<EmployeeViewModel> GetEmployeesWithoutGroup(long customizeWorkshopSettingId)
|
||||
{
|
||||
var workshopSettings = _companyContext.CustomizeWorkshopSettings.Find(customizeWorkshopSettingId);
|
||||
var workshopSettings = _companyContext.CustomizeWorkshopSettings.Find(customizeWorkshopSettingId);
|
||||
|
||||
if (workshopSettings == null)
|
||||
return new();
|
||||
|
||||
var existsEmployees = _companyContext.CustomizeWorkshopEmployeeSettings
|
||||
.AsSplitQuery().Include(x=>x.CustomizeWorkshopGroupSettings)
|
||||
.Where(x => x.WorkshopId == workshopSettings.WorkshopId && !x.CustomizeWorkshopGroupSettings.MainGroup)
|
||||
.Select(x=>x.EmployeeId).ToList();
|
||||
|
||||
.AsSplitQuery().Include(x => x.CustomizeWorkshopGroupSettings)
|
||||
.Where(x => x.WorkshopId == workshopSettings.WorkshopId && !x.CustomizeWorkshopGroupSettings.MainGroup)
|
||||
.Select(x => x.EmployeeId).ToList();
|
||||
|
||||
var workshopId = workshopSettings.WorkshopId;
|
||||
var rollCallEmployees = _rollCallEmployeeRepository.GetActivePersonnelByWorkshopId(workshopId);
|
||||
|
||||
@@ -81,79 +83,81 @@ public class CustomizeWorkshopGroupSettingsRepository(CompanyContext companyCont
|
||||
|
||||
var dateNow = DateTime.Now.Date;
|
||||
|
||||
//var existsEmployees = _companyContext.CustomizeWorkshopEmployeeSettings
|
||||
// .AsSplitQuery().Include(x => x.CustomizeWorkshopGroupSettings)
|
||||
// .Where(x => x.WorkshopId == workshopId && !x.CustomizeWorkshopGroupSettings.MainGroup)
|
||||
// .Select(x => x.EmployeeId).ToList();
|
||||
//var existsEmployees = _companyContext.CustomizeWorkshopEmployeeSettings
|
||||
// .AsSplitQuery().Include(x => x.CustomizeWorkshopGroupSettings)
|
||||
// .Where(x => x.WorkshopId == workshopId && !x.CustomizeWorkshopGroupSettings.MainGroup)
|
||||
// .Select(x => x.EmployeeId).ToList();
|
||||
|
||||
//var rollCallEmployees = _rollCallEmployeeRepository.GetActivePersonnelByWorkshopId(workshopId);
|
||||
//var rollCallEmployees = _rollCallEmployeeRepository.GetActivePersonnelByWorkshopId(workshopId);
|
||||
|
||||
//var employees = rollCallEmployees
|
||||
// .Where(x => !existsEmployees.Contains(x.EmployeeId))
|
||||
// .Select(x => new EmployeeViewModel()
|
||||
// {
|
||||
// EmployeeFullName = x.EmployeeFullName,
|
||||
// Id = x.EmployeeId
|
||||
// }).ToList();
|
||||
//var employees = rollCallEmployees
|
||||
// .Where(x => !existsEmployees.Contains(x.EmployeeId))
|
||||
// .Select(x => new EmployeeViewModel()
|
||||
// {
|
||||
// EmployeeFullName = x.EmployeeFullName,
|
||||
// Id = x.EmployeeId
|
||||
// }).ToList();
|
||||
|
||||
var employees = _companyContext.RollCallEmployees
|
||||
.Include(x =>
|
||||
x.EmployeesStatus)
|
||||
.Where(
|
||||
x =>
|
||||
x.WorkshopId == workshopId &&
|
||||
x.EmployeesStatus.Any(y => y.StartDate.Date <= dateNow && y.EndDate.Date > dateNow) &&
|
||||
x.HasUploadedImage == "true")
|
||||
.GroupJoin(_companyContext.CustomizeWorkshopEmployeeSettings
|
||||
.AsSplitQuery()
|
||||
.Include(x => x.CustomizeWorkshopGroupSettings)
|
||||
.Where(x => !x.CustomizeWorkshopGroupSettings.MainGroup && x.WorkshopId == workshopId), rollCallEmployee => rollCallEmployee.EmployeeId,
|
||||
cws => cws.EmployeeId,
|
||||
(rollCallEmployee, cws) => new { rollCallEmployee, cws })
|
||||
.SelectMany(x => x.cws.DefaultIfEmpty(), (x, cws) => new { x.rollCallEmployee, cws })
|
||||
.Where(x => x.cws == null).Select(x=> new EmployeeViewModel()
|
||||
{
|
||||
var employees = _companyContext.RollCallEmployees
|
||||
.Include(x =>
|
||||
x.EmployeesStatus)
|
||||
.Where(x =>
|
||||
x.WorkshopId == workshopId &&
|
||||
x.EmployeesStatus.Any(y => y.StartDate.Date <= dateNow && y.EndDate.Date > dateNow) &&
|
||||
x.HasUploadedImage == "true")
|
||||
.GroupJoin(_companyContext.CustomizeWorkshopEmployeeSettings
|
||||
.AsSplitQuery()
|
||||
.Include(x => x.CustomizeWorkshopGroupSettings)
|
||||
.Where(x => !x.CustomizeWorkshopGroupSettings.MainGroup && x.WorkshopId == workshopId),
|
||||
rollCallEmployee => rollCallEmployee.EmployeeId,
|
||||
cws => cws.EmployeeId,
|
||||
(rollCallEmployee, cws) => new { rollCallEmployee, cws })
|
||||
.SelectMany(x => x.cws.DefaultIfEmpty(), (x, cws) => new { x.rollCallEmployee, cws })
|
||||
.Where(x => x.cws == null).Select(x => new EmployeeViewModel()
|
||||
{
|
||||
EmployeeFullName = x.rollCallEmployee.EmployeeFullName,
|
||||
Id = x.rollCallEmployee.EmployeeId
|
||||
});
|
||||
});
|
||||
|
||||
return employees.ToList();
|
||||
return employees.ToList();
|
||||
}
|
||||
|
||||
public bool HasAnyEmployeeWithoutGroup(long workshopId)
|
||||
{
|
||||
var dateNow = DateTime.Now.Date;
|
||||
var dateNow = DateTime.Now.Date;
|
||||
var leftWork = _companyContext.LeftWorkList.Where(x =>
|
||||
x.WorkshopId == workshopId && x.StartWorkDate <= dateNow && x.LeftWorkDate >= dateNow);
|
||||
|
||||
var rollCallEmployeesWithoutCWS = _companyContext.RollCallEmployees
|
||||
.Include(x =>
|
||||
x.EmployeesStatus)
|
||||
.Where(
|
||||
x =>
|
||||
x.WorkshopId == workshopId &&
|
||||
x.EmployeesStatus.Any(y => y.StartDate.Date <= dateNow && y.EndDate.Date > dateNow) &&
|
||||
x.HasUploadedImage == "true"&&
|
||||
leftWork.Any(l => l.EmployeeId == x.EmployeeId && l.WorkshopId == x.WorkshopId))
|
||||
.GroupJoin(_companyContext.CustomizeWorkshopEmployeeSettings
|
||||
.AsSplitQuery()
|
||||
.Include(x => x.CustomizeWorkshopGroupSettings)
|
||||
.Where(x => !x.CustomizeWorkshopGroupSettings.MainGroup && x.WorkshopId == workshopId), rollCallEmployee => rollCallEmployee.EmployeeId,
|
||||
cws => cws.EmployeeId,
|
||||
(rollCallEmployee, cws) => new { rollCallEmployee, cws })
|
||||
.SelectMany(x => x.cws.DefaultIfEmpty(), (x, cws) => new { x.rollCallEmployee, cws })
|
||||
.Any(x => x.cws == null);
|
||||
|
||||
return rollCallEmployeesWithoutCWS;
|
||||
.Include(x =>
|
||||
x.EmployeesStatus)
|
||||
.Where(x =>
|
||||
x.WorkshopId == workshopId &&
|
||||
x.EmployeesStatus.Any(y => y.StartDate.Date <= dateNow && y.EndDate.Date > dateNow) &&
|
||||
x.HasUploadedImage == "true" &&
|
||||
leftWork.Any(l => l.EmployeeId == x.EmployeeId && l.WorkshopId == x.WorkshopId))
|
||||
.GroupJoin(_companyContext.CustomizeWorkshopEmployeeSettings
|
||||
.AsSplitQuery()
|
||||
.Include(x => x.CustomizeWorkshopGroupSettings)
|
||||
.Where(x => !x.CustomizeWorkshopGroupSettings.MainGroup && x.WorkshopId == workshopId),
|
||||
rollCallEmployee => rollCallEmployee.EmployeeId,
|
||||
cws => cws.EmployeeId,
|
||||
(rollCallEmployee, cws) => new { rollCallEmployee, cws })
|
||||
.SelectMany(x => x.cws.DefaultIfEmpty(), (x, cws) => new { x.rollCallEmployee, cws })
|
||||
.Any(x => x.cws == null);
|
||||
|
||||
return rollCallEmployeesWithoutCWS;
|
||||
}
|
||||
|
||||
public CustomizeWorkshopGroupSettings GetWithEmployees(long groupId)
|
||||
{
|
||||
return _companyContext.CustomizeWorkshopGroupSettings.AsSplitQuery().Include(x => x.CustomizeWorkshopEmployeeSettingsCollection)
|
||||
return _companyContext.CustomizeWorkshopGroupSettings.AsSplitQuery()
|
||||
.Include(x => x.CustomizeWorkshopEmployeeSettingsCollection)
|
||||
.FirstOrDefault(x => x.id == groupId);
|
||||
}
|
||||
|
||||
public List<CustomizeWorkshopEmployeeSettingsViewModel> GetShiftChangedEmployeeSettingsByGroupSettingsId(long groupSettingsId)
|
||||
public List<CustomizeWorkshopEmployeeSettingsViewModel> GetShiftChangedEmployeeSettingsByGroupSettingsId(
|
||||
long groupSettingsId)
|
||||
{
|
||||
var groupEmployeeSettingsList = _companyContext.CustomizeWorkshopGroupSettings
|
||||
.AsSplitQuery().Include(x => x.CustomizeWorkshopEmployeeSettingsCollection)
|
||||
@@ -169,23 +173,26 @@ public class CustomizeWorkshopGroupSettingsRepository(CompanyContext companyCont
|
||||
x.id
|
||||
}).ToList();
|
||||
|
||||
return groupEmployeeSettingsList.Join(employees, x => x.EmployeeId, y => y.id, (x, y) => new CustomizeWorkshopEmployeeSettingsViewModel()
|
||||
{
|
||||
EmployeeId = x.EmployeeId,
|
||||
Id = x.id,
|
||||
IsShiftChanged = x.IsShiftChanged,
|
||||
IsSettingChanged = x.IsSettingChanged,
|
||||
Name = y.FullName,
|
||||
RollCallWorkshopShifts = x.CustomizeWorkshopEmployeeSettingsShifts.Select(z => new CustomizeWorkshopShiftViewModel()
|
||||
return groupEmployeeSettingsList.Join(employees, x => x.EmployeeId, y => y.id, (x, y) =>
|
||||
new CustomizeWorkshopEmployeeSettingsViewModel()
|
||||
{
|
||||
EndTime = z.EndTime.ToString("HH:mm"),
|
||||
Placement = z.Placement,
|
||||
StartTime = z.StartTime.ToString("HH:mm")
|
||||
}).ToList()
|
||||
}).ToList();
|
||||
EmployeeId = x.EmployeeId,
|
||||
Id = x.id,
|
||||
IsShiftChanged = x.IsShiftChanged,
|
||||
IsSettingChanged = x.IsSettingChanged,
|
||||
Name = y.FullName,
|
||||
RollCallWorkshopShifts = x.CustomizeWorkshopEmployeeSettingsShifts.Select(z =>
|
||||
new CustomizeWorkshopShiftViewModel()
|
||||
{
|
||||
EndTime = z.EndTime.ToString("HH:mm"),
|
||||
Placement = z.Placement,
|
||||
StartTime = z.StartTime.ToString("HH:mm")
|
||||
}).ToList()
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
public List<CustomizeWorkshopEmployeeSettingsViewModel> GetSettingChangedEmployeeSettingsByGroupSettingsId(long groupSettingsId)
|
||||
public List<CustomizeWorkshopEmployeeSettingsViewModel> GetSettingChangedEmployeeSettingsByGroupSettingsId(
|
||||
long groupSettingsId)
|
||||
{
|
||||
var groupEmployeeSettingsList = _companyContext.CustomizeWorkshopGroupSettings
|
||||
.AsSplitQuery().Include(x => x.CustomizeWorkshopEmployeeSettingsCollection)
|
||||
@@ -201,38 +208,52 @@ public class CustomizeWorkshopGroupSettingsRepository(CompanyContext companyCont
|
||||
x.id
|
||||
}).ToList();
|
||||
|
||||
return groupEmployeeSettingsList.Join(employees, x => x.EmployeeId, y => y.id, (x, y) => new CustomizeWorkshopEmployeeSettingsViewModel()
|
||||
{
|
||||
EmployeeId = x.EmployeeId,
|
||||
Id = x.id,
|
||||
IsSettingChanged = x.IsSettingChanged,
|
||||
IsShiftChanged = x.IsShiftChanged,
|
||||
Name = y.FullName,
|
||||
RollCallWorkshopShifts = x.CustomizeWorkshopEmployeeSettingsShifts.Select(z => new CustomizeWorkshopShiftViewModel()
|
||||
return groupEmployeeSettingsList.Join(employees, x => x.EmployeeId, y => y.id, (x, y) =>
|
||||
new CustomizeWorkshopEmployeeSettingsViewModel()
|
||||
{
|
||||
EndTime = z.EndTime.ToString("HH:mm"),
|
||||
Placement = z.Placement,
|
||||
StartTime = z.StartTime.ToString("HH:mm")
|
||||
}).ToList()
|
||||
}).ToList();
|
||||
EmployeeId = x.EmployeeId,
|
||||
Id = x.id,
|
||||
IsSettingChanged = x.IsSettingChanged,
|
||||
IsShiftChanged = x.IsShiftChanged,
|
||||
Name = y.FullName,
|
||||
RollCallWorkshopShifts = x.CustomizeWorkshopEmployeeSettingsShifts.Select(z =>
|
||||
new CustomizeWorkshopShiftViewModel()
|
||||
{
|
||||
EndTime = z.EndTime.ToString("HH:mm"),
|
||||
Placement = z.Placement,
|
||||
StartTime = z.StartTime.ToString("HH:mm")
|
||||
}).ToList()
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
public List<CustomizeWorkshopEmployeeSettingsViewModel> GetEmployeeSettingsByGroupSettingsId(long groupSettingsId)
|
||||
{
|
||||
|
||||
var entity = _companyContext.CustomizeWorkshopGroupSettings.AsSplitQuery().Include(x=>x.CustomizeWorkshopEmployeeSettingsCollection)
|
||||
.FirstOrDefault(x => x.id == groupSettingsId);
|
||||
var entity = _companyContext.CustomizeWorkshopGroupSettings.AsSplitQuery()
|
||||
.Include(x => x.CustomizeWorkshopEmployeeSettingsCollection)
|
||||
.FirstOrDefault(x => x.id == groupSettingsId);
|
||||
|
||||
if (entity == null)
|
||||
return new();
|
||||
|
||||
var employees = entity.CustomizeWorkshopEmployeeSettingsCollection;
|
||||
if (employees.Count == 0)
|
||||
return [];
|
||||
|
||||
var workshopId = employees.FirstOrDefault()?.WorkshopId??0;
|
||||
|
||||
if (workshopId == 0)
|
||||
return [];
|
||||
|
||||
|
||||
var employeeIds = employees.Select(x => x.EmployeeId).ToList();
|
||||
|
||||
|
||||
var employeeIds = employees.Select(x => x.EmployeeId);
|
||||
var names = _companyContext.Employees.Where(x => employeeIds.Contains(x.id))
|
||||
.Select(x => new { x.id, x.FullName }).ToList();
|
||||
|
||||
|
||||
var names = _companyContext.Employees.Where(x => employeeIds.Contains(x.id)).Select(x => new { x.id, x.FullName }).ToList();
|
||||
var leftWork = _companyContext.LeftWorkList.Where(x =>
|
||||
x.WorkshopId == workshopId &&
|
||||
employeeIds.Contains(x.EmployeeId)).ToList();
|
||||
|
||||
var joinedList = employees.Join(names, x => x.EmployeeId, y => y.id, (x, y) =>
|
||||
new CustomizeWorkshopEmployeeSettingsViewModel
|
||||
@@ -243,82 +264,86 @@ public class CustomizeWorkshopGroupSettingsRepository(CompanyContext companyCont
|
||||
Salary = x.Salary,
|
||||
IsSettingChanged = x.IsSettingChanged,
|
||||
IsShiftChanged = x.IsShiftChanged,
|
||||
RollCallWorkshopShifts = x.CustomizeWorkshopEmployeeSettingsShifts.Select(z => new CustomizeWorkshopShiftViewModel()
|
||||
{
|
||||
EndTime = z.EndTime.ToString("HH:mm"),
|
||||
StartTime = z.StartTime.ToString("HH:mm"),
|
||||
Placement = z.Placement
|
||||
}).ToList(),
|
||||
RollCallWorkshopShifts = x.CustomizeWorkshopEmployeeSettingsShifts
|
||||
.Select(z => new CustomizeWorkshopShiftViewModel()
|
||||
{
|
||||
EndTime = z.EndTime.ToString("HH:mm"),
|
||||
StartTime = z.StartTime.ToString("HH:mm"),
|
||||
Placement = z.Placement
|
||||
}).ToList(),
|
||||
IrregularShift = x.IrregularShift,
|
||||
WorkshopShiftStatus = x.WorkshopShiftStatus,
|
||||
LeavePermittedDays = x.LeavePermittedDays,
|
||||
CustomizeRotatingShiftsViewModels = x.CustomizeRotatingShifts
|
||||
.Select(r=> new CustomizeRotatingShiftsViewModel(){StartTime = r.StartTime.ToString("HH:mm") , EndTime =r.EndTime.ToString("HH:mm") }).ToList()
|
||||
|
||||
.Select(r => new CustomizeRotatingShiftsViewModel
|
||||
{
|
||||
StartTime = r.StartTime.ToString("HH:mm"),
|
||||
EndTime = r.EndTime.ToString("HH:mm")
|
||||
}).ToList(),
|
||||
HasLeft = leftWork.OrderByDescending(l=>l.StartWorkDate).FirstOrDefault(l => x.EmployeeId == l.EmployeeId)?.HasLeft ?? false
|
||||
});
|
||||
return joinedList.ToList();
|
||||
return joinedList.OrderBy(x=>x.HasLeft).ToList();
|
||||
}
|
||||
|
||||
public EditCustomizeWorkshopGroupSettings GetCustomizeWorkshopGroupSettingsDetails(long groupId)
|
||||
{
|
||||
|
||||
var entity = _companyContext.CustomizeWorkshopGroupSettings.AsSplitQuery().FirstOrDefault(x => x.id == groupId);
|
||||
return new EditCustomizeWorkshopGroupSettings()
|
||||
{
|
||||
//FridayWork = entity.FridayWork,
|
||||
FridayPay = new (){ FridayPayType = entity.FridayPay.FridayPayType, Value = entity.FridayPay.Value },
|
||||
FridayPay = new() { FridayPayType = entity.FridayPay.FridayPayType, Value = entity.FridayPay.Value },
|
||||
LateToWork = new()
|
||||
{
|
||||
{
|
||||
LateToWorkTimeFinesVewModels = entity.LateToWork.LateToWorkTimeFines.Select(x =>
|
||||
new LateToWorkTimeFineVewModel() { FineMoney = x.FineMoney, Minute = x.Minute }).ToList(),
|
||||
Value = entity.LateToWork.Value, LateToWorkType = entity.LateToWork.LateToWorkType
|
||||
},
|
||||
HolidayWork = entity.HolidayWork,
|
||||
FineAbsenceDeduction = new()
|
||||
{
|
||||
{
|
||||
Value = entity.FineAbsenceDeduction.Value,
|
||||
FineAbsenceDayOfWeekViewModels = entity.FineAbsenceDeduction.FineAbsenceDayOfWeekCollection
|
||||
.Select(x => new FineAbsenceDayOfWeekViewModel() { DayOfWeek = x.DayOfWeek }).ToList(),
|
||||
FineAbsenceDeductionType = entity.FineAbsenceDeduction.FineAbsenceDeductionType
|
||||
},
|
||||
EarlyExit = new()
|
||||
{
|
||||
{
|
||||
EarlyExitTimeFinesViewModels = entity.EarlyExit.EarlyExitTimeFines.Select(x =>
|
||||
new EarlyExitTimeFineViewModel() { FineMoney = x.FineMoney, Minute = x.Minute }).ToList(),
|
||||
Value = entity.EarlyExit.Value, EarlyExitType = entity.EarlyExit.EarlyExitType
|
||||
},
|
||||
BonusesPay = new()
|
||||
{
|
||||
{
|
||||
Value = entity.BonusesPay.Value, BonusesPayType = entity.BonusesPay.BonusesPayType,
|
||||
PaymentType = entity.BonusesPay.PaymentType
|
||||
},
|
||||
ShiftPay = new()
|
||||
{
|
||||
{
|
||||
Value = entity.ShiftPay.Value, ShiftPayType = entity.ShiftPay.ShiftPayType,
|
||||
ShiftType = entity.ShiftPay.ShiftType
|
||||
},
|
||||
InsuranceDeduction = new()
|
||||
{
|
||||
{
|
||||
Value = entity.InsuranceDeduction.Value,
|
||||
InsuranceDeductionType = entity.InsuranceDeduction.InsuranceDeductionType
|
||||
},
|
||||
OverTimePay = new()
|
||||
{ OverTimePayType = entity.OverTimePay.OverTimePayType, Value = entity.OverTimePay.Value },
|
||||
{ OverTimePayType = entity.OverTimePay.OverTimePayType, Value = entity.OverTimePay.Value },
|
||||
BaseYearsPay = new()
|
||||
{
|
||||
{
|
||||
BaseYearsPayType = entity.BaseYearsPay.BaseYearsPayType,
|
||||
Value = entity.BaseYearsPay.Value
|
||||
},
|
||||
NightWorkPay = new()
|
||||
{ NightWorkingType = entity.NightWorkPay.NightWorkingType, Value = entity.NightWorkPay.Value },
|
||||
LeavePay =new() { Value = entity.LeavePay.Value, LeavePayType = entity.LeavePay.LeavePayType },
|
||||
{ NightWorkingType = entity.NightWorkPay.NightWorkingType, Value = entity.NightWorkPay.Value },
|
||||
LeavePay = new() { Value = entity.LeavePay.Value, LeavePayType = entity.LeavePay.LeavePayType },
|
||||
MarriedAllowance = new()
|
||||
{
|
||||
{
|
||||
Value = entity.MarriedAllowance.Value,
|
||||
MarriedAllowanceType = entity.MarriedAllowance.MarriedAllowanceType
|
||||
},
|
||||
FamilyAllowance = new()
|
||||
{
|
||||
{
|
||||
FamilyAllowanceType = entity.FamilyAllowance.FamilyAllowanceType,
|
||||
Value = entity.FamilyAllowance.Value
|
||||
},
|
||||
@@ -332,23 +357,27 @@ public class CustomizeWorkshopGroupSettingsRepository(CompanyContext companyCont
|
||||
IsShiftChanged = entity.IsShiftChange,
|
||||
ShiftViewModel = entity.CustomizeWorkshopGroupSettingsShifts.Select(x =>
|
||||
new CustomizeWorkshopShiftViewModel()
|
||||
{ EndTime = x.EndTime.ToString("HH:mm"), Placement = x.Placement, StartTime = x.StartTime.ToString("HH:mm") }).ToList(),
|
||||
{
|
||||
EndTime = x.EndTime.ToString("HH:mm"), Placement = x.Placement,
|
||||
StartTime = x.StartTime.ToString("HH:mm")
|
||||
}).ToList(),
|
||||
LeavePermittedDays = entity.LeavePermittedDays,
|
||||
CustomizeRotatingShiftsViewModels = entity.CustomizeRotatingShifts.Select(x=> new CustomizeRotatingShiftsViewModel()
|
||||
{
|
||||
EndTime = x.EndTime.ToString("HH:mm"),
|
||||
StartTime = x.StartTime.ToString("HH:mm")
|
||||
}).ToList(),
|
||||
OffDayOfWeeks = entity.WeeklyOffDays.Select(x=>x.DayOfWeek).ToList()
|
||||
CustomizeRotatingShiftsViewModels = entity.CustomizeRotatingShifts.Select(x =>
|
||||
new CustomizeRotatingShiftsViewModel()
|
||||
{
|
||||
EndTime = x.EndTime.ToString("HH:mm"),
|
||||
StartTime = x.StartTime.ToString("HH:mm")
|
||||
}).ToList(),
|
||||
OffDayOfWeeks = entity.WeeklyOffDays.Select(x => x.DayOfWeek).ToList()
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public List<CustomizeWorkshopGroupSettings> GetAllGroupsIncludeEmployeeSettingsByWorkshopSettingsId(long workshopSettingsId)
|
||||
public List<CustomizeWorkshopGroupSettings> GetAllGroupsIncludeEmployeeSettingsByWorkshopSettingsId(
|
||||
long workshopSettingsId)
|
||||
{
|
||||
return _companyContext.CustomizeWorkshopGroupSettings
|
||||
.AsSplitQuery().Include(x => x.CustomizeWorkshopEmployeeSettingsCollection)
|
||||
.Where(x => x.CustomizeWorkshopSettingId == workshopSettingsId).ToList();
|
||||
return _companyContext.CustomizeWorkshopGroupSettings
|
||||
.AsSplitQuery().Include(x => x.CustomizeWorkshopEmployeeSettingsCollection)
|
||||
.Where(x => x.CustomizeWorkshopSettingId == workshopSettingsId).ToList();
|
||||
}
|
||||
|
||||
public void Remove(long groupId)
|
||||
@@ -360,7 +389,8 @@ public class CustomizeWorkshopGroupSettingsRepository(CompanyContext companyCont
|
||||
_companyContext.SaveChanges();
|
||||
}
|
||||
|
||||
public CustomizeWorkshopGroupSettingsViewModel GetEmployeesGroupSettingsByEmployeeId(long employeeId, long workshopId)
|
||||
public CustomizeWorkshopGroupSettingsViewModel GetEmployeesGroupSettingsByEmployeeId(long employeeId,
|
||||
long workshopId)
|
||||
{
|
||||
return _companyContext.CustomizeWorkshopGroupSettings
|
||||
.Include(x => x.CustomizeWorkshopEmployeeSettingsCollection)
|
||||
|
||||
@@ -1291,7 +1291,9 @@ public class InstitutionContractRepository : RepositoryBase<long, InstitutionCon
|
||||
return new InstitutionContractListWorkshop()
|
||||
{
|
||||
EmployeeCount = w.PersonnelCount,
|
||||
Price = w.Price.ToMoney(),
|
||||
WorkshopName = workshopSelected?.WorkshopName ?? w.WorkshopName,
|
||||
|
||||
WorkshopServices = new WorkshopServicesViewModel()
|
||||
{
|
||||
Contract = w.Services.Contract,
|
||||
@@ -1300,7 +1302,8 @@ public class InstitutionContractRepository : RepositoryBase<long, InstitutionCon
|
||||
Insurance = w.Services.Insurance,
|
||||
InsuranceInPerson = w.Services.InsuranceInPerson,
|
||||
RollCall = w.Services.RollCall,
|
||||
RollCallInPerson = w.Services.RollCallInPerson
|
||||
RollCallInPerson = w.Services.RollCallInPerson,
|
||||
|
||||
}
|
||||
};
|
||||
}).ToList() ?? [];
|
||||
@@ -2342,7 +2345,7 @@ public class InstitutionContractRepository : RepositoryBase<long, InstitutionCon
|
||||
|
||||
await SaveChangesAsync();
|
||||
|
||||
await _smsService.SendInstitutionVerificationLink(contractingParty.Phone, contractingPartyFullName,
|
||||
await _smsService.SendInstitutionCreationVerificationLink(contractingParty.Phone, contractingPartyFullName,
|
||||
entity.PublicId, contractingParty.id, entity.id);
|
||||
|
||||
|
||||
@@ -2768,6 +2771,61 @@ public class InstitutionContractRepository : RepositoryBase<long, InstitutionCon
|
||||
return res;
|
||||
}
|
||||
|
||||
public async Task<GetInstitutionContractWorkshopsDetails> GetContractWorkshopsDetails(long id)
|
||||
{
|
||||
var institutionContract = await _context.InstitutionContractSet
|
||||
.Include(x => x.WorkshopGroup)
|
||||
.ThenInclude(x => x.InitialWorkshops)
|
||||
.FirstOrDefaultAsync(x => x.id == id);
|
||||
if (institutionContract == null)
|
||||
throw new NotFoundException("قرارداد مؤسسه یافت نشد");
|
||||
|
||||
var workshops = institutionContract.WorkshopGroup.InitialWorkshops
|
||||
.Select(x =>
|
||||
{
|
||||
var plan = _planPercentageRepository.GetInstitutionPlanForWorkshop(new WorkshopTempViewModel()
|
||||
{
|
||||
CountPerson = x.PersonnelCount,
|
||||
ContractAndCheckout = x.Services.Contract,
|
||||
ContractAndCheckoutInPerson = x.Services.ContractInPerson,
|
||||
Insurance = x.Services.Insurance,
|
||||
InsuranceInPerson = x.Services.InsuranceInPerson,
|
||||
RollCall = x.Services.RollCall,
|
||||
RollCallInPerson = x.Services.RollCallInPerson,
|
||||
CustomizeCheckout = x.Services.CustomizeCheckout,
|
||||
});
|
||||
|
||||
return new InstitutionContractListWorkshop()
|
||||
{
|
||||
EmployeeCount = x.PersonnelCount,
|
||||
Price = x.Price.ToMoney(),
|
||||
WorkshopName = x.WorkshopName,
|
||||
WorkshopServices = new WorkshopServicesViewModel()
|
||||
{
|
||||
Contract = x.Services.Contract,
|
||||
ContractPrice =plan.ContractAndCheckout ,
|
||||
ContractInPerson = x.Services.ContractInPerson,
|
||||
ContractInPersonPrice = plan.ContractAndCheckoutInPerson,
|
||||
CustomizeCheckout = x.Services.CustomizeCheckout,
|
||||
CustomizeCheckoutPrice = plan.CustomizeCheckout,
|
||||
Insurance = x.Services.Insurance,
|
||||
InsurancePrice =plan.Insurance ,
|
||||
InsuranceInPerson = x.Services.InsuranceInPerson,
|
||||
InsuranceInPersonPrice = plan.InsuranceInPerson,
|
||||
RollCall = x.Services.RollCall,
|
||||
RollCallPrice =plan.RollCall ,
|
||||
RollCallInPerson = x.Services.RollCallInPerson,
|
||||
RollCallInPersonPrice = "0",
|
||||
}
|
||||
};
|
||||
}).ToList();
|
||||
return new GetInstitutionContractWorkshopsDetails()
|
||||
{
|
||||
Workshops = workshops
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
private InstitutionContractExtensionPaymentResponse CalculateInPersonPayment(
|
||||
InstitutionContractExtensionPlanDetail selectedPlan, double baseAmount, double tenPercent,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,8 @@ public class SmsService : ISmsService
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ISmsResultRepository _smsResultRepository;
|
||||
private readonly bool _isDevEnvironment;
|
||||
private readonly List<string> _testNumbers;
|
||||
public SmsIr SmsIr { get; set; }
|
||||
|
||||
|
||||
@@ -27,6 +29,33 @@ public class SmsService : ISmsService
|
||||
_smsResultRepository = smsResultRepository;
|
||||
SmsIr = new SmsIr("Og5M562igmzJRhQPnq0GdtieYdLgtfikjzxOmeQBPxJjZtyge5Klc046Lfw1mxSa");
|
||||
|
||||
// خواندن تنظیمات SMS از appsettings
|
||||
var smsSettings = _configuration.GetSection("SmsSettings");
|
||||
_isDevEnvironment = smsSettings.GetValue<bool>("IsTestMode");
|
||||
_testNumbers = smsSettings.GetSection("TestNumbers").Get<List<string>>() ?? new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// متد مرکزی برای ارسال پیامک که محیط dev را چک میکند
|
||||
/// </summary>
|
||||
private async Task<SmsIrResult<VerifySendResult>> VerifySendSmsAsync(string number, int templateId, VerifySendParameter[] parameters)
|
||||
{
|
||||
// اگر محیط dev است و شمارههای تست وجود دارد، به شمارههای تست ارسال میشود
|
||||
if (_isDevEnvironment && _testNumbers is { Count: > 0 })
|
||||
{
|
||||
// ارسال به همه شمارههای تست
|
||||
SmsIrResult<VerifySendResult> lastResult = null;
|
||||
foreach (var testNumber in _testNumbers)
|
||||
{
|
||||
lastResult = await SmsIr.VerifySendAsync(testNumber, templateId, parameters);
|
||||
}
|
||||
return lastResult; // برگرداندن نتیجه آخرین ارسال
|
||||
}
|
||||
else
|
||||
{
|
||||
// ارسال به شماره واقعی
|
||||
return await SmsIr.VerifySendAsync(number, templateId, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
public void Send(string number, string message)
|
||||
@@ -56,12 +85,7 @@ public class SmsService : ISmsService
|
||||
}
|
||||
public bool VerifySend(string number, string message)
|
||||
{
|
||||
|
||||
SmsIr smsIr = new SmsIr("Og5M562igmzJRhQPnq0GdtieYdLgtfikjzxOmeQBPxJjZtyge5Klc046Lfw1mxSa");
|
||||
|
||||
//var bulkSendResult = smsIr.BulkSendAsync(95007079000006, "your text message", new string[] { "9120000000" });
|
||||
|
||||
var verificationSendResult = smsIr.VerifySendAsync(number, 768382, new VerifySendParameter[] { new VerifySendParameter("VerificationCode", message) });
|
||||
var verificationSendResult = VerifySendSmsAsync(number, 768382, new VerifySendParameter[] { new VerifySendParameter("VerificationCode", message) });
|
||||
Thread.Sleep(2000);
|
||||
if (verificationSendResult.IsCompletedSuccessfully)
|
||||
{
|
||||
@@ -90,11 +114,7 @@ public class SmsService : ISmsService
|
||||
|
||||
public bool LoginSend(string number, string message)
|
||||
{
|
||||
SmsIr smsIr = new SmsIr("Og5M562igmzJRhQPnq0GdtieYdLgtfikjzxOmeQBPxJjZtyge5Klc046Lfw1mxSa");
|
||||
|
||||
//var bulkSendResult = smsIr.BulkSendAsync(95007079000006, "your text message", new string[] { "9120000000" });
|
||||
|
||||
var verificationSendResult = smsIr.VerifySendAsync(number, 635330, new VerifySendParameter[] { new VerifySendParameter("LOGINCODE", message) });
|
||||
var verificationSendResult = VerifySendSmsAsync(number, 635330, new VerifySendParameter[] { new VerifySendParameter("LOGINCODE", message) });
|
||||
Thread.Sleep(2000);
|
||||
if (verificationSendResult.IsCompletedSuccessfully)
|
||||
{
|
||||
@@ -119,11 +139,8 @@ public class SmsService : ISmsService
|
||||
|
||||
public async Task<SentSmsViewModel> SendVerifyCodeToClient(string number, string code)
|
||||
{
|
||||
SmsIr smsIr = new SmsIr("Og5M562igmzJRhQPnq0GdtieYdLgtfikjzxOmeQBPxJjZtyge5Klc046Lfw1mxSa");
|
||||
var result = new SentSmsViewModel();
|
||||
//var bulkSendResult = smsIr.BulkSendAsync(95007079000006, "your text message", new string[] { "9120000000" });
|
||||
|
||||
var sendResult = await smsIr.VerifySendAsync(number, 768382, new VerifySendParameter[] { new VerifySendParameter("VerificationCode", code) });
|
||||
var sendResult = await VerifySendSmsAsync(number, 768382, new VerifySendParameter[] { new VerifySendParameter("VerificationCode", code) });
|
||||
Thread.Sleep(2000);
|
||||
|
||||
if (sendResult.Message == "موفق")
|
||||
@@ -148,9 +165,8 @@ public class SmsService : ISmsService
|
||||
var checkLength = fullName.Length;
|
||||
if (checkLength > 25)
|
||||
fullName = fullName.Substring(0, 24);
|
||||
SmsIr smsIr = new SmsIr("Og5M562igmzJRhQPnq0GdtieYdLgtfikjzxOmeQBPxJjZtyge5Klc046Lfw1mxSa");
|
||||
|
||||
var sendResult = smsIr.VerifySendAsync(number, 725814, new VerifySendParameter[] { new VerifySendParameter("FULLNAME", fullName), new VerifySendParameter("USERNAME", userName), new VerifySendParameter("PASSWORD", userName) });
|
||||
var sendResult = VerifySendSmsAsync(number, 725814, new VerifySendParameter[] { new VerifySendParameter("FULLNAME", fullName), new VerifySendParameter("USERNAME", userName), new VerifySendParameter("PASSWORD", userName) });
|
||||
|
||||
|
||||
Console.WriteLine(userName + " - " + sendResult.Result.Status);
|
||||
@@ -332,18 +348,39 @@ public class SmsService : ISmsService
|
||||
}
|
||||
|
||||
}
|
||||
public async Task<bool> SendInstitutionVerificationLink(string number, string fullName, Guid institutionId, long contractingPartyId, long institutionContractId)
|
||||
|
||||
public async Task<bool> SendInstitutionCreationVerificationLink(string number, string fullName, Guid institutionId, long contractingPartyId, long institutionContractId)
|
||||
{
|
||||
var guidStr=institutionId.ToString();
|
||||
var firstPart = guidStr.Substring(0, 15);
|
||||
var secondPart = guidStr.Substring(15);
|
||||
var verificationSendResult =await SmsIr.VerifySendAsync(number, 527519, new VerifySendParameter[]
|
||||
var verificationSendResult =await VerifySendSmsAsync(number, 527519, new VerifySendParameter[]
|
||||
{
|
||||
new("FULLNAME", fullName),
|
||||
new("CODE1",firstPart),
|
||||
new("CODE2",secondPart)
|
||||
});
|
||||
var smsResult = new SmsResult(verificationSendResult.Data.MessageId, verificationSendResult.Message, "لینک تاییدیه قرارداد مالی",
|
||||
|
||||
var smsResult = new SmsResult(verificationSendResult.Data.MessageId, verificationSendResult.Message, "لینک تاییدیه ایجاد قرارداد مالی",
|
||||
fullName, number, contractingPartyId, institutionContractId);
|
||||
await _smsResultRepository.CreateAsync(smsResult);
|
||||
await _smsResultRepository.SaveChangesAsync();
|
||||
return verificationSendResult.Status == 0;
|
||||
}
|
||||
|
||||
public async Task<bool> SendInstitutionAmendmentVerificationLink(string number, string fullName, Guid institutionId,
|
||||
long contractingPartyId, long institutionContractId)
|
||||
{
|
||||
var guidStr=institutionId.ToString();
|
||||
var firstPart = guidStr.Substring(0, 15);
|
||||
var secondPart = guidStr.Substring(15);
|
||||
var verificationSendResult =await VerifySendSmsAsync(number, 527519, new VerifySendParameter[]
|
||||
{
|
||||
new("FULLNAME", fullName),
|
||||
new("CODE1",firstPart),
|
||||
new("CODE2",secondPart)
|
||||
});
|
||||
var smsResult = new SmsResult(verificationSendResult.Data.MessageId, verificationSendResult.Message, "لینک تاییدیه ارتقا قرارداد مالی",
|
||||
fullName, number, contractingPartyId, institutionContractId);
|
||||
await _smsResultRepository.CreateAsync(smsResult);
|
||||
await _smsResultRepository.SaveChangesAsync();
|
||||
@@ -353,7 +390,7 @@ public class SmsService : ISmsService
|
||||
public async Task<bool> SendInstitutionVerificationCode(string number, string code, string contractingPartyFullName,
|
||||
long contractingPartyId, long institutionContractId)
|
||||
{
|
||||
var verificationSendResult =await SmsIr.VerifySendAsync(number, 965348, new VerifySendParameter[]
|
||||
var verificationSendResult =await VerifySendSmsAsync(number, 965348, new VerifySendParameter[]
|
||||
{
|
||||
new("VERIFYCODE", code)
|
||||
});
|
||||
|
||||
@@ -220,11 +220,17 @@ using CompanyManagment.App.Contracts.PaymentInstrument;
|
||||
using CompanyManagment.App.Contracts.PaymentTransaction;
|
||||
using CompanyManagment.App.Contracts.AuthorizedPerson;
|
||||
using Company.Domain.AuthorizedPersonAgg;
|
||||
using Company.Domain.EmployeeFaceEmbeddingAgg;
|
||||
using Company.Domain.InstitutionContractExtensionTempAgg;
|
||||
using Company.Domain.LawAgg;
|
||||
using CompanyManagement.Infrastructure.Mongo.EmployeeFaceEmbeddingRepo;
|
||||
using CompanyManagement.Infrastructure.Mongo.InstitutionContractInsertTempRepo;
|
||||
using CompanyManagment.App.Contracts.EmployeeFaceEmbedding;
|
||||
using CompanyManagment.App.Contracts.Law;
|
||||
using CompanyManagment.EFCore.Repository;
|
||||
using _0_Framework.Application.FaceEmbedding;
|
||||
using _0_Framework.Infrastructure;
|
||||
using _0_Framework.InfraStructure;
|
||||
|
||||
namespace PersonalContractingParty.Config;
|
||||
|
||||
@@ -479,6 +485,10 @@ public class PersonalBootstrapper
|
||||
|
||||
services
|
||||
.AddTransient<IInstitutionContractExtenstionTempRepository, InstitutionContractExtenstionTempRepository>();
|
||||
|
||||
services.AddTransient<IEmployeeFaceEmbeddingRepository, EmployeeFaceEmbeddingRepository>();
|
||||
services.AddTransient<IEmployeeFaceEmbeddingApplication, EmployeeFaceEmbeddingApplication>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Pooya
|
||||
@@ -604,6 +614,10 @@ public class PersonalBootstrapper
|
||||
services.AddTransient<IInsuranceApplication, InsuranceApplication>();
|
||||
services.AddTransient<IInsuranceRepository, InsuranceRepository>();
|
||||
|
||||
// Face Embedding Services
|
||||
services.AddTransient<IFaceEmbeddingService, FaceEmbeddingService>();
|
||||
services.AddTransient<IFaceEmbeddingNotificationService, NullFaceEmbeddingNotificationService>();
|
||||
|
||||
services.AddDbContext<CompanyContext>(x => x.UseSqlServer(connectionString));
|
||||
}
|
||||
}
|
||||
61
ServiceHost/Areas/Admin/Controllers/AndroidApkController.cs
Normal file
61
ServiceHost/Areas/Admin/Controllers/AndroidApkController.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
|
||||
using CompanyManagment.App.Contracts.AndroidApkVersion;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ServiceHost.BaseControllers;
|
||||
|
||||
namespace ServiceHost.Areas.Admin.Controllers;
|
||||
|
||||
[AllowAnonymous]
|
||||
[ApiController]
|
||||
[Route("api/android-apk")]
|
||||
public class AndroidApkController:AdminBaseController
|
||||
{
|
||||
private readonly IAndroidApkVersionApplication _apkApp;
|
||||
public AndroidApkController(IAndroidApkVersionApplication apkApp)
|
||||
{
|
||||
_apkApp = apkApp;
|
||||
}
|
||||
|
||||
[HttpGet("check-update")]
|
||||
public async Task<IActionResult> CheckUpdate([FromQuery] ApkType type, [FromQuery] int currentVersionCode = 0)
|
||||
{
|
||||
|
||||
var info = await _apkApp.GetLatestActiveInfo(type, currentVersionCode);
|
||||
return Ok(new
|
||||
{
|
||||
latestVersionCode = info.LatestVersionCode,
|
||||
latestVersionName = info.LatestVersionName,
|
||||
shouldUpdate = info.ShouldUpdate,
|
||||
isForceUpdate = info.IsForceUpdate,
|
||||
downloadUrl = info.DownloadUrl,
|
||||
releaseNotes = info.ReleaseNotes
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("download")]
|
||||
public async Task<IActionResult> Download([FromQuery] ApkType type)
|
||||
{
|
||||
// Check if APK exists
|
||||
if (!_apkApp.HasAndroidApkToDownload(type))
|
||||
{
|
||||
return NotFound(new { message = $"هیچ فایل APK فعالی برای {type} یافت نشد" });
|
||||
}
|
||||
|
||||
// Get the path to the latest active APK
|
||||
var path = await _apkApp.GetLatestActiveVersionPath(type);
|
||||
|
||||
if (string.IsNullOrEmpty(path) || !System.IO.File.Exists(path))
|
||||
{
|
||||
return NotFound(new { message = "فایل APK یافت نشد" });
|
||||
}
|
||||
|
||||
// Set appropriate file name for download
|
||||
var fileName = type == ApkType.WebView
|
||||
? "Gozareshgir.apk"
|
||||
: "Gozareshgir-FaceDetection.apk";
|
||||
|
||||
// Return the file for download
|
||||
return PhysicalFile(path, "application/vnd.android.package-archive", fileName);
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,12 @@ public class institutionContractController : AdminBaseController
|
||||
return await _institutionContractApplication.GetListStats(searchModel);
|
||||
}
|
||||
|
||||
[HttpGet("workshops-details/{id}")]
|
||||
public async Task<ActionResult<GetInstitutionContractWorkshopsDetails>> GetWorkshopsDetials(long id)
|
||||
{
|
||||
var result = await _institutionContractApplication.GetContractWorkshopsDetails(id);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ویرایش
|
||||
@@ -218,6 +224,7 @@ public class institutionContractController : AdminBaseController
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<ActionResult<OperationResult>> Remove(long id)
|
||||
{
|
||||
return BadRequest("امکان حذف قرارداد وجود ندارد");
|
||||
_institutionContractApplication.RemoveContract(id);
|
||||
return new OperationResult().Succcedded();
|
||||
|
||||
|
||||
@@ -923,6 +923,8 @@ public class IndexModel : PageModel
|
||||
public IActionResult OnGetDownloadExcel()
|
||||
{
|
||||
var institutionContractViewModels = _institutionContract.NewSearch(new() {IsActiveString = "both", TypeOfContract = "both" });
|
||||
|
||||
institutionContractViewModels= institutionContractViewModels.GroupBy(x=>x.ContractingPartyId).Select(g=>g.MaxBy(x=>x.ContractStartGr)).ToList();
|
||||
var bytes = InstitutionContractExcelGenerator.GenerateExcel(institutionContractViewModels);
|
||||
return File(bytes,
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
|
||||
@@ -287,7 +287,7 @@
|
||||
|
||||
<div class="textsChart">
|
||||
<p class="alltxt">
|
||||
<span style="width: 40px;display: inline-block;">@Model.ContrcatDone</span> : <span style="width: 62px;display: inline-block;">کل</span>
|
||||
<span style="width: 40px;display: inline-block;">@Model.ContractSignToBe</span> : <span style="width: 62px;display: inline-block;">کل</span>
|
||||
</p>
|
||||
<p class="alldonetxt">
|
||||
<span style="width: 40px;display: inline-block;">@Model.ContractSignDone</span> : <span style="width: 62px;display: inline-block;color: #283344;">انجام شده</span>
|
||||
@@ -353,7 +353,7 @@
|
||||
|
||||
<div class="textsChart">
|
||||
<p class="alltxt">
|
||||
<span style="width: 40px;display: inline-block;">@Model.CheckoutDone</span> : <span style="width: 62px;display: inline-block;">کل</span>
|
||||
<span style="width: 40px;display: inline-block;">@Model.CheckoutSignToBe</span> : <span style="width: 62px;display: inline-block;">کل</span>
|
||||
</p>
|
||||
<p class="alldonetxt">
|
||||
<span style="width: 40px;display: inline-block;">@Model.CheckoutSignDone</span> : <span style="width: 62px;display: inline-block;color: #283344;">انجام شده</span>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using _0_Framework.Application;
|
||||
using AccountMangement.Infrastructure.EFCore;
|
||||
using CompanyManagment.App.Contracts.Report;
|
||||
using CompanyManagment.App.Contracts.Workshop;
|
||||
using CompanyManagment.App.Contracts.YearlySalary;
|
||||
@@ -42,6 +43,7 @@ public class IndexModel : PageModel
|
||||
public int ContrcatDone { get; set; }
|
||||
public int ContractSignNotDone { get; set; }
|
||||
public int ContractSignDone { get; set; }
|
||||
public int ContractSignToBe { get; set; }
|
||||
|
||||
//تصفیه
|
||||
public int AllCheckout { get; set; }
|
||||
@@ -49,8 +51,9 @@ public class IndexModel : PageModel
|
||||
public int CheckoutDone { get; set; }
|
||||
public int CheckoutSignNotDone { get; set; }
|
||||
public int CheckoutSignDone { get; set; }
|
||||
public int CheckoutSignToBe { get; set; }
|
||||
|
||||
public async Task OnGet(string year, string month)
|
||||
public async Task OnGet(string year, string month)
|
||||
{
|
||||
YearlyList = _yearlySalaryApplication.GetYears();
|
||||
var allReports = await _reportApplication.GetAllReports(year, month);
|
||||
@@ -73,19 +76,23 @@ public class IndexModel : PageModel
|
||||
ContrcatDone = allReports.ContrcatDone;
|
||||
ContractSignNotDone = allReports.ContractSignNotDone;
|
||||
ContractSignDone = allReports.ContractSignDone;
|
||||
AllCheckout = allReports.AllCheckout;
|
||||
ContractSignToBe = allReports.ContractSignToBe;
|
||||
|
||||
AllCheckout = allReports.AllCheckout;
|
||||
CheckoutNotDone = allReports.CheckoutNotDone;
|
||||
CheckoutDone = allReports.CheckoutDone;
|
||||
CheckoutSignNotDone = allReports.CheckoutSignNotDone;
|
||||
CheckoutSignDone = allReports.CheckoutSignDone;
|
||||
|
||||
CheckoutSignToBe = allReports.CheckoutSignToBe;
|
||||
|
||||
|
||||
}
|
||||
|
||||
#region workshopFirstLoad
|
||||
|
||||
public IActionResult OnGetWorkshopContractDone(string year, string month, long accountId, List<long> workshopList)
|
||||
{
|
||||
var res = _reportApplication.GetWorkshopContractDone(year, month, accountId, workshopList);
|
||||
var res = _reportApplication.GetWorkshopContractDone(year, month, accountId, workshopList).GetAwaiter().GetResult();
|
||||
return new JsonResult(new
|
||||
{
|
||||
success = true,
|
||||
@@ -96,7 +103,7 @@ public class IndexModel : PageModel
|
||||
|
||||
public IActionResult OnGetWorkshopContractSignDone(string year, string month, long accountId, List<long> workshopList)
|
||||
{
|
||||
var res = _reportApplication.GetWorkshopContractSignDone(year, month, accountId, workshopList);
|
||||
var res = _reportApplication.GetWorkshopContractSignDone(year, month, accountId, workshopList).GetAwaiter().GetResult();
|
||||
return new JsonResult(new
|
||||
{
|
||||
success = true,
|
||||
@@ -107,7 +114,7 @@ public class IndexModel : PageModel
|
||||
|
||||
public IActionResult OnGetWorkshopCheckoutDone(string year, string month, long accountId, List<long> workshopList)
|
||||
{
|
||||
var res = _reportApplication.GetWorkshopCheckoutDone(year, month, accountId, workshopList);
|
||||
var res = _reportApplication.GetWorkshopCheckoutDone(year, month, accountId, workshopList).GetAwaiter().GetResult();
|
||||
return new JsonResult(new
|
||||
{
|
||||
success = true,
|
||||
@@ -118,7 +125,7 @@ public class IndexModel : PageModel
|
||||
|
||||
public IActionResult OnGetWorkshopCheckoutSignDone(string year, string month, long accountId, List<long> workshopList)
|
||||
{
|
||||
var res = _reportApplication.GetWorkshopCheckoutSignDone(year, month, accountId, workshopList);
|
||||
var res = _reportApplication.GetWorkshopCheckoutSignDone(year, month, accountId, workshopList).GetAwaiter().GetResult();
|
||||
return new JsonResult(new
|
||||
{
|
||||
success = true,
|
||||
@@ -133,7 +140,7 @@ public class IndexModel : PageModel
|
||||
|
||||
public IActionResult OnGetEmployeeContract(string year, string month, long workshopId)
|
||||
{
|
||||
var res = _reportApplication.GetEmployeeContract(year, month, workshopId);
|
||||
var res = _reportApplication.GetEmployeeContract(year, month, workshopId).GetAwaiter().GetResult();
|
||||
return new JsonResult(new
|
||||
{
|
||||
success = true,
|
||||
@@ -143,7 +150,7 @@ public class IndexModel : PageModel
|
||||
|
||||
public IActionResult OnGetEmployeeContractSign(string year, string month, long workshopId)
|
||||
{
|
||||
var res = _reportApplication.GetEmployeeContractSign(year, month, workshopId);
|
||||
var res = _reportApplication.GetEmployeeContractSign(year, month, workshopId).GetAwaiter().GetResult();
|
||||
return new JsonResult(new
|
||||
{
|
||||
success = true,
|
||||
@@ -153,7 +160,7 @@ public class IndexModel : PageModel
|
||||
|
||||
public IActionResult OnGetEmployeeCheckout(string year, string month, long workshopId)
|
||||
{
|
||||
var res = _reportApplication.GetEmployeeCheckout(year, month, workshopId);
|
||||
var res = _reportApplication.GetEmployeeCheckout(year, month, workshopId).GetAwaiter().GetResult();
|
||||
return new JsonResult(new
|
||||
{
|
||||
success = true,
|
||||
@@ -163,7 +170,7 @@ public class IndexModel : PageModel
|
||||
|
||||
public IActionResult OnGetEmployeeCheckoutSign(string year, string month, long workshopId, long accountId)
|
||||
{
|
||||
var res = _reportApplication.GetEmployeeCheckoutSign(year, month, workshopId);
|
||||
var res = _reportApplication.GetEmployeeCheckoutSign(year, month, workshopId).GetAwaiter().GetResult();
|
||||
return new JsonResult(new
|
||||
{
|
||||
success = true,
|
||||
@@ -180,14 +187,14 @@ public class IndexModel : PageModel
|
||||
{
|
||||
|
||||
var workshopIds = Tools.ExtractNumbers(workshopList);
|
||||
var res = _reportApplication.GetPrintAllContractDone(year, month, accountId, workshopIds);
|
||||
var res = _reportApplication.GetPrintAllContractDone(year, month, accountId, workshopIds).GetAwaiter().GetResult();
|
||||
return Partial("PrintAll", res);
|
||||
}
|
||||
|
||||
public IActionResult OnGetPrintAllContractSignDone(string year, string month, long accountId, string workshopList)
|
||||
{
|
||||
var workshopIds = Tools.ExtractNumbers(workshopList);
|
||||
var res = _reportApplication.GetPrintAllContractSignDone(year, month, accountId, workshopIds);
|
||||
var res = _reportApplication.GetPrintAllContractSignDone(year, month, accountId, workshopIds).GetAwaiter().GetResult();
|
||||
return Partial("PrintAll", res);
|
||||
}
|
||||
|
||||
@@ -195,14 +202,14 @@ public class IndexModel : PageModel
|
||||
{
|
||||
var workshopIds = Tools.ExtractNumbers(workshopList);
|
||||
|
||||
var res = _reportApplication.GetPrintAllCheckoutDone(year, month, accountId, workshopIds);
|
||||
var res = _reportApplication.GetPrintAllCheckoutDone(year, month, accountId, workshopIds).GetAwaiter().GetResult();
|
||||
return Partial("PrintAll", res);
|
||||
}
|
||||
|
||||
public IActionResult OnGetPrintAllCheckoutSignDone(string year, string month, long accountId, string workshopList)
|
||||
{
|
||||
var workshopIds = Tools.ExtractNumbers(workshopList);
|
||||
var res = _reportApplication.GetPrintAllCheckoutSignDone(year, month, accountId, workshopIds);
|
||||
var res = _reportApplication.GetPrintAllCheckoutSignDone(year, month, accountId, workshopIds).GetAwaiter().GetResult();
|
||||
return Partial("PrintAll", res);
|
||||
}
|
||||
//================Employee
|
||||
@@ -211,7 +218,7 @@ public class IndexModel : PageModel
|
||||
public IActionResult OnGetPrintEmployeeContract(string year, string month, long workshopId, string accountFullName)
|
||||
{
|
||||
var workshop = _workshopApplication.GetDetails(workshopId);
|
||||
var res = _reportApplication.GetEmployeeContract(year, month, workshopId);
|
||||
var res = _reportApplication.GetEmployeeContract(year, month, workshopId).GetAwaiter().GetResult();
|
||||
var next = $"{year}/{month}/01";
|
||||
var y = Convert.ToInt32(year);
|
||||
var m = Convert.ToInt32(month);
|
||||
@@ -235,7 +242,7 @@ public class IndexModel : PageModel
|
||||
string accountFullName)
|
||||
{
|
||||
var workshop = _workshopApplication.GetDetails(workshopId);
|
||||
var res = _reportApplication.GetEmployeeContractSign(year, month, workshopId);
|
||||
var res = _reportApplication.GetEmployeeContractSign(year, month, workshopId).GetAwaiter().GetResult();
|
||||
var next = $"{year}/{month}/01";
|
||||
var y = Convert.ToInt32(year);
|
||||
var m = Convert.ToInt32(month);
|
||||
@@ -258,7 +265,7 @@ public class IndexModel : PageModel
|
||||
public IActionResult OnGetPrintEmployeeCheckout(string year, string month, long workshopId, string accountFullName)
|
||||
{
|
||||
var workshop = _workshopApplication.GetDetails(workshopId);
|
||||
var res = _reportApplication.GetEmployeeCheckout(year, month, workshopId);
|
||||
var res = _reportApplication.GetEmployeeCheckout(year, month, workshopId).GetAwaiter().GetResult();
|
||||
|
||||
var final = new PrintEmployeeNotDone
|
||||
{
|
||||
@@ -277,7 +284,7 @@ public class IndexModel : PageModel
|
||||
string accountFullName)
|
||||
{
|
||||
var workshop = _workshopApplication.GetDetails(workshopId);
|
||||
var res = _reportApplication.GetEmployeeCheckoutSign(year, month, workshopId);
|
||||
var res = _reportApplication.GetEmployeeCheckoutSign(year, month, workshopId).GetAwaiter().GetResult();
|
||||
|
||||
var final = new PrintEmployeeNotDone
|
||||
{
|
||||
|
||||
@@ -4,14 +4,46 @@
|
||||
ViewData["Title"] = "File Upload";
|
||||
}
|
||||
|
||||
<h1>Upload File</h1>
|
||||
<form asp-page-handler="Upload" id="1" method="post" enctype="multipart/form-data">
|
||||
<div>
|
||||
<label asp-for="File">Choose a file:</label>
|
||||
<input asp-for="File" type="file" required>
|
||||
<h1>Upload APK File</h1>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form asp-page-handler="Upload" id="1" method="post" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<label asp-for="File" class="form-label">Choose APK file:</label>
|
||||
<input asp-for="File" type="file" class="form-control" required accept=".apk">
|
||||
<small class="form-text text-muted">Please select a valid APK file.</small>
|
||||
<span asp-validation-for="File" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="VersionName" class="form-label">Version Name:</label>
|
||||
<input asp-for="VersionName" type="text" class="form-control" placeholder="e.g. 1.0.0" required>
|
||||
<small class="form-text text-muted">Enter the version name (e.g. 1.0.0, 2.1.3).</small>
|
||||
<span asp-validation-for="VersionName" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="VersionCode" class="form-label">Version Code:</label>
|
||||
<input asp-for="VersionCode" type="text" class="form-control" placeholder="e.g. 100" required>
|
||||
<small class="form-text text-muted">Enter the version code (numeric value).</small>
|
||||
<span asp-validation-for="VersionCode" class="text-danger"></span>
|
||||
</div>
|
||||
<div style="margin-top:12px;">
|
||||
<label asp-for="SelectedApkType">نوع اپلیکیشن:</label>
|
||||
<select asp-for="SelectedApkType" asp-items="Html.GetEnumSelectList<CompanyManagment.App.Contracts.AndroidApkVersion.ApkType>()"></select>
|
||||
</div>
|
||||
<div style="margin-top:12px;">
|
||||
<label asp-for="IsForce">
|
||||
<input asp-for="IsForce" type="checkbox">
|
||||
آپدیت اجباری (Force Update)
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">Upload APK</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<button type="submit">Upload</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<form asp-page-handler="ShiftDate" id="8" method="post">
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using _0_Framework.Domain.CustomizeCheckoutShared.Enums;
|
||||
using AccountManagement.Domain.AccountLeftWorkAgg;
|
||||
using AccountMangement.Infrastructure.EFCore;
|
||||
using Company.Domain.AndroidApkVersionAgg;
|
||||
using Company.Domain.CustomizeCheckoutAgg.ValueObjects;
|
||||
using Company.Domain.CustomizeCheckoutTempAgg.ValueObjects;
|
||||
using Company.Domain.RewardAgg;
|
||||
@@ -18,9 +19,15 @@ using System.Text.Json.Serialization;
|
||||
using _0_Framework.Application.PaymentGateway;
|
||||
using Company.Domain.InstitutionContractAgg;
|
||||
using Company.Domain.InstitutionPlanAgg;
|
||||
using Company.Domain.PaymentTransactionAgg;
|
||||
using CompanyManagment.App.Contracts.InstitutionContract;
|
||||
using CompanyManagment.App.Contracts.PaymentTransaction;
|
||||
using CompanyManagment.App.Contracts.TemporaryClientRegistration;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Parbad;
|
||||
using Parbad.AspNetCore;
|
||||
using Parbad.Gateway.Sepehr;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using static ServiceHost.Areas.AdminNew.Pages.Company.AndroidApk.IndexModel2;
|
||||
|
||||
namespace ServiceHost.Areas.AdminNew.Pages.Company.AndroidApk
|
||||
@@ -34,21 +41,38 @@ namespace ServiceHost.Areas.AdminNew.Pages.Company.AndroidApk
|
||||
private readonly AccountContext _accountContext;
|
||||
private readonly IPaymentGateway _paymentGateway;
|
||||
private readonly ITemporaryClientRegistrationApplication _clientRegistrationApplication;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly IOnlinePayment _onlinePayment;
|
||||
|
||||
|
||||
|
||||
[BindProperty] public IFormFile File { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
[Required(ErrorMessage = "لطفا نام ورژن را وارد کنید")]
|
||||
[Display(Name = "نام ورژن")]
|
||||
public string VersionName { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
[Required(ErrorMessage = "لطفا کد ورژن را وارد کنید")]
|
||||
[Display(Name = "کد ورژن")]
|
||||
public string VersionCode { get; set; }
|
||||
[BindProperty] public ApkType SelectedApkType { get; set; }
|
||||
[BindProperty] public bool IsForce { get; set; }
|
||||
|
||||
public IndexModel(IAndroidApkVersionApplication application, IRollCallDomainService rollCallDomainService,
|
||||
CompanyContext context, AccountContext accountContext, IHttpClientFactory httpClientFactory,
|
||||
IOptions<AppSettingConfiguration> appSetting,
|
||||
ITemporaryClientRegistrationApplication clientRegistrationApplication)
|
||||
ITemporaryClientRegistrationApplication clientRegistrationApplication, IOnlinePayment onlinePayment)
|
||||
{
|
||||
_application = application;
|
||||
_rollCallDomainService = rollCallDomainService;
|
||||
_context = context;
|
||||
_accountContext = accountContext;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_clientRegistrationApplication = clientRegistrationApplication;
|
||||
_paymentGateway = new AqayePardakhtPaymentGateway(httpClientFactory, appSetting);
|
||||
_onlinePayment = onlinePayment;
|
||||
_paymentGateway = new SepehrPaymentGateway(httpClientFactory);
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
@@ -57,13 +81,13 @@ namespace ServiceHost.Areas.AdminNew.Pages.Company.AndroidApk
|
||||
|
||||
public async Task<IActionResult> OnPostUpload()
|
||||
{
|
||||
var result = await _application.CreateAndActive(File);
|
||||
var result = await _application.CreateAndActive(File,SelectedApkType, VersionName, VersionCode, IsForce);
|
||||
ViewData["message"] = result.Message;
|
||||
return Page();
|
||||
}
|
||||
|
||||
|
||||
public IActionResult OnPostShiftDate()
|
||||
public async Task<IActionResult> OnPostShiftDate()
|
||||
{
|
||||
//var startRollCall = new DateTime(2025, 2, 19);
|
||||
//var rollCalls = _context.RollCalls.Where(x => x.ShiftDate >= startRollCall);
|
||||
@@ -73,10 +97,116 @@ namespace ServiceHost.Areas.AdminNew.Pages.Company.AndroidApk
|
||||
//var notEndedRollCalls = rollCalls.Where(x => x.EndDate == null).ToList();
|
||||
//RefactorAllTheRollCallsOnEsfand(endedRollCalls, notEndedRollCalls);
|
||||
//CreateRewardForKebabMahdi().GetAwaiter().GetResult();
|
||||
SetEntityIdForCheckoutValues();
|
||||
SetEntityIdForCheckoutValuesTemp();
|
||||
ViewData["message"] = "ایجاد شد";
|
||||
return Page();
|
||||
|
||||
var callBack = Url.Action("Verify", "General", null, Request.Scheme);
|
||||
|
||||
|
||||
var amount = 10000;
|
||||
var transaction = new PaymentTransaction(30427, amount, "سید حسن مصباح", "https://client.gozareshgir.ir", PaymentTransactionGateWay.SepehrPay);
|
||||
|
||||
_context.PaymentTransactions.Add(transaction);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
var command = new CreatePaymentGatewayRequest()
|
||||
{
|
||||
InvoiceId = transaction.id.ToString(),
|
||||
Amount = amount,
|
||||
CallBackUrl = callBack
|
||||
};
|
||||
|
||||
var createRes = await _paymentGateway.Create(command);
|
||||
|
||||
if (createRes.IsSuccess)
|
||||
{
|
||||
var payUrl = _paymentGateway.GetStartPayUrl(createRes.Token);
|
||||
return Redirect(payUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
return BadRequest(createRes.Status + "خطا در ارسال به درگاه پرداخت");
|
||||
}
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task CreateDadmehrWorkshopFaceEmbedding()
|
||||
{
|
||||
var basePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "faces");
|
||||
|
||||
if (!Directory.Exists(basePath))
|
||||
{
|
||||
ViewData["message"] = "مسیر پوشه یافت نشد";
|
||||
return;
|
||||
}
|
||||
|
||||
var directories = Directory.GetDirectories(basePath);
|
||||
|
||||
var client = new HttpClient();
|
||||
|
||||
foreach (var mainDirectory in directories)
|
||||
{
|
||||
var subDirectories = Directory.GetDirectories(mainDirectory);
|
||||
var workshopName = Path.GetFileName(mainDirectory);
|
||||
foreach (var directory in subDirectories)
|
||||
{
|
||||
var directoryName = Path.GetFileName(directory);
|
||||
|
||||
var employee = await _context.Employees
|
||||
.FirstOrDefaultAsync(x => x.id == Convert.ToInt64(directoryName));
|
||||
var fullname = employee != null ? employee.FullName : "نامشخص";
|
||||
|
||||
var imageFiles = Directory.GetFiles(directory, "*.*")
|
||||
.Take(2)
|
||||
.ToArray();
|
||||
|
||||
if (imageFiles.Length < 2)
|
||||
{
|
||||
Console.WriteLine($"تعداد تصاویر کافی برای {directoryName} وجود ندارد");
|
||||
continue;
|
||||
}
|
||||
|
||||
using var formData = new MultipartFormDataContent();
|
||||
formData.Add(new StringContent(directoryName), "employee_id");
|
||||
formData.Add(new StringContent(workshopName), "workshop_id");
|
||||
formData.Add(new StringContent(fullname), "employee_full_name");
|
||||
|
||||
FileStream file1Stream = null;
|
||||
FileStream file2Stream = null;
|
||||
|
||||
try
|
||||
{
|
||||
// ارسال فایل اول
|
||||
file1Stream = new FileStream(imageFiles[0], FileMode.Open, FileAccess.Read);
|
||||
var file1Content = new StreamContent(file1Stream);
|
||||
file1Content.Headers.ContentType =
|
||||
new System.Net.Http.Headers.MediaTypeHeaderValue("image/jpeg");
|
||||
formData.Add(file1Content, "picture1", Path.GetFileName(imageFiles[0]));
|
||||
|
||||
// ارسال فایل دوم
|
||||
file2Stream = new FileStream(imageFiles[1], FileMode.Open, FileAccess.Read);
|
||||
var file2Content = new StreamContent(file2Stream);
|
||||
file2Content.Headers.ContentType =
|
||||
new System.Net.Http.Headers.MediaTypeHeaderValue("image/jpeg");
|
||||
formData.Add(file2Content, "picture2", Path.GetFileName(imageFiles[1]));
|
||||
|
||||
var response = await client.PostAsync("http://127.0.0.1:8000/embeddings", formData);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
Console.WriteLine($"✓ {directoryName}: {result}");
|
||||
}
|
||||
else
|
||||
{
|
||||
var error = await response.Content.ReadAsStringAsync();
|
||||
Console.WriteLine($"✗ {directoryName}: {response.StatusCode} - {error}");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
file1Stream?.Dispose();
|
||||
file2Stream?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IActionResult OnPostShiftDateNew()
|
||||
@@ -128,7 +258,7 @@ namespace ServiceHost.Areas.AdminNew.Pages.Company.AndroidApk
|
||||
|
||||
if (createResponse.Status == "success")
|
||||
{
|
||||
return Redirect(_paymentGateway.GetStartPayUrl(createResponse.TransactionId));
|
||||
return Redirect(_paymentGateway.GetStartPayUrl(createResponse.Token));
|
||||
}
|
||||
|
||||
//TranslateCode(result?.ErrorCode);
|
||||
@@ -695,7 +825,7 @@ namespace ServiceHost.Areas.AdminNew.Pages.Company.AndroidApk
|
||||
//TODO: set data for institution price
|
||||
var workshops = item.contractingParty.Employers
|
||||
.SelectMany(e => e.WorkshopEmployers.Select(we => we.Workshop)).ToList()
|
||||
.DistinctBy(x=>x.id).ToList();
|
||||
.DistinctBy(x => x.id).ToList();
|
||||
|
||||
var initialWorkshop = workshops
|
||||
.Select(w =>
|
||||
@@ -750,56 +880,56 @@ namespace ServiceHost.Areas.AdminNew.Pages.Company.AndroidApk
|
||||
|
||||
//var oneMonthSum = current.Sum(x => x.Price);
|
||||
item.contract.SetWorkshopGroup(group);
|
||||
// var totalPaymentAndWorkshopList = await _clientRegistrationApplication.GetTotalPaymentAndWorkshopList(oneMonthSum,duration: InstitutionContractDuration.TwelveMonths,false);
|
||||
// item.contract.SetAmount(totalPaymentAndWorkshopList.OneTimeTotalPaymentStr.MoneyToDouble(),
|
||||
// totalPaymentAndWorkshopList.OneTimeValueAddedTaxStr.MoneyToDouble(),totalPaymentAndWorkshopList.DiscountedAmountForOneMonth.MoneyToDouble());
|
||||
// var totalPaymentAndWorkshopList = await _clientRegistrationApplication.GetTotalPaymentAndWorkshopList(oneMonthSum,duration: InstitutionContractDuration.TwelveMonths,false);
|
||||
// item.contract.SetAmount(totalPaymentAndWorkshopList.OneTimeTotalPaymentStr.MoneyToDouble(),
|
||||
// totalPaymentAndWorkshopList.OneTimeValueAddedTaxStr.MoneyToDouble(),totalPaymentAndWorkshopList.DiscountedAmountForOneMonth.MoneyToDouble());
|
||||
}
|
||||
|
||||
var remoteIds = remoteContractsQuery.Select(x => x.contract.id);
|
||||
var inPersonContracts =await query
|
||||
.Where(x=>!remoteIds.Contains(x.contractingParty.id)).ToListAsync();
|
||||
var inPersonContracts = await query
|
||||
.Where(x => !remoteIds.Contains(x.contractingParty.id)).ToListAsync();
|
||||
foreach (var item in inPersonContracts)
|
||||
{
|
||||
var workshops = item.contractingParty.Employers
|
||||
.SelectMany(e => e.WorkshopEmployers.Select(we => we.Workshop)).ToList()
|
||||
.DistinctBy(x=>x.id).ToList();
|
||||
.DistinctBy(x => x.id).ToList();
|
||||
|
||||
var initialWorkshop = workshops
|
||||
.Select(w =>
|
||||
{
|
||||
var personnelCount =
|
||||
w.LeftWorks.Count(lw => lw.StartWorkDate <= today && lw.LeftWorkDate >= today);
|
||||
|
||||
|
||||
bool hasRollCallPlan = true;
|
||||
bool hasRollCallPlanInPerson = false;
|
||||
bool hasCustomizeCheckoutPlan = w.id == 170;
|
||||
bool hasContractPlan = true;
|
||||
bool hasContractPlanInPerson = true;
|
||||
bool hasInsurancePlan =true;
|
||||
bool hasInsurancePlan = true;
|
||||
bool hasInsurancePlanInPerson = true;
|
||||
|
||||
var initial = InstitutionContractWorkshopInitial.CreateManual(w.WorkshopFullName, hasRollCallPlan,
|
||||
hasRollCallPlanInPerson,hasCustomizeCheckoutPlan,
|
||||
hasContractPlan,hasContractPlanInPerson,
|
||||
hasInsurancePlan,hasInsurancePlanInPerson,personnelCount,0,
|
||||
|
||||
var initial = InstitutionContractWorkshopInitial.CreateManual(w.WorkshopFullName,
|
||||
hasRollCallPlan,
|
||||
hasRollCallPlanInPerson, hasCustomizeCheckoutPlan,
|
||||
hasContractPlan, hasContractPlanInPerson,
|
||||
hasInsurancePlan, hasInsurancePlanInPerson, personnelCount, 0,
|
||||
w.id, w.WorkshopEmployers.Select(x => x.EmployerId
|
||||
).ToList());
|
||||
return initial;
|
||||
}).ToList();
|
||||
|
||||
var group = new InstitutionContractWorkshopGroup(item.contract.id, initialWorkshop);
|
||||
|
||||
|
||||
await _context.AddAsync(group);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
|
||||
initialWorkshop.ForEach(x =>
|
||||
{
|
||||
var workshopId = workshops.First(w => x.WorkshopId.Value == w.id).id;
|
||||
x.SetWorkshopId(workshopId);
|
||||
});
|
||||
|
||||
|
||||
item.contract.SetWorkshopGroup(group);
|
||||
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
355
ServiceHost/Areas/Camera/Controllers/CameraController.cs
Normal file
355
ServiceHost/Areas/Camera/Controllers/CameraController.cs
Normal file
@@ -0,0 +1,355 @@
|
||||
using ServiceHost.BaseControllers;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using _0_Framework.Application;
|
||||
using _0_Framework.Exceptions;
|
||||
using AccountManagement.Application.Contracts.Account;
|
||||
using AccountManagement.Application.Contracts.CameraAccount;
|
||||
using AccountManagement.Domain.TaskAgg;
|
||||
using CompanyManagment.App.Contracts.AndroidApkVersion;
|
||||
using CompanyManagment.App.Contracts.PersonnleCode;
|
||||
using CompanyManagment.App.Contracts.RollCall;
|
||||
using CompanyManagment.App.Contracts.RollCallEmployee;
|
||||
using CompanyManagment.App.Contracts.RollCallService;
|
||||
using CompanyManagment.App.Contracts.Employee;
|
||||
using CompanyManagment.App.Contracts.EmployeeFaceEmbedding;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace ServiceHost.Areas.Camera.Controllers;
|
||||
|
||||
public class CameraController : CameraBaseController
|
||||
{
|
||||
private readonly IWebHostEnvironment _webHostEnvironment;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly IEmployeeApplication _employeeApplication;
|
||||
private readonly IRollCallApplication _rollCallApplication;
|
||||
private readonly IRollCallServiceApplication _rollCallServiceApplication;
|
||||
private readonly IRollCallEmployeeApplication _rollCallEmployeeApplication;
|
||||
private readonly IAuthHelper _authHelper;
|
||||
private readonly IPersonnelCodeApplication _personnelCodeApplication;
|
||||
private readonly IAccountApplication _accountApplication;
|
||||
private readonly IPasswordHasher _passwordHasher;
|
||||
private readonly ICameraAccountApplication _cameraAccountApplication;
|
||||
private readonly IEmployeeFaceEmbeddingApplication _employeeFaceEmbeddingApplication;
|
||||
private long _workshopId;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly HttpClient _faceEmbeddingHttpClient;
|
||||
private readonly IAndroidApkVersionApplication _androidApkVersionApplication;
|
||||
|
||||
public CameraController(IWebHostEnvironment webHostEnvironment,
|
||||
IConfiguration configuration,
|
||||
IEmployeeApplication employeeApplication,
|
||||
IRollCallApplication rollCallApplication,
|
||||
IAuthHelper authHelper,
|
||||
IRollCallServiceApplication rollCallServiceApplication,
|
||||
IRollCallEmployeeApplication rollCallEmployeeApplication,
|
||||
IPersonnelCodeApplication personnelCodeApplication,
|
||||
IAccountApplication accountApplication,
|
||||
IPasswordHasher passwordHasher,
|
||||
ICameraAccountApplication cameraAccountApplication,
|
||||
IEmployeeFaceEmbeddingApplication employeeFaceEmbeddingApplication, IHttpClientFactory httpClientFactory, IAndroidApkVersionApplication androidApkVersionApplication)
|
||||
{
|
||||
_webHostEnvironment = webHostEnvironment;
|
||||
_configuration = configuration;
|
||||
_employeeApplication = employeeApplication;
|
||||
_rollCallApplication = rollCallApplication;
|
||||
_authHelper = authHelper;
|
||||
_rollCallServiceApplication = rollCallServiceApplication;
|
||||
_rollCallEmployeeApplication = rollCallEmployeeApplication;
|
||||
_personnelCodeApplication = personnelCodeApplication;
|
||||
_accountApplication = accountApplication;
|
||||
_passwordHasher = passwordHasher;
|
||||
_cameraAccountApplication = cameraAccountApplication;
|
||||
_employeeFaceEmbeddingApplication = employeeFaceEmbeddingApplication;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_androidApkVersionApplication = androidApkVersionApplication;
|
||||
_faceEmbeddingHttpClient = httpClientFactory.CreateClient();
|
||||
_faceEmbeddingHttpClient.BaseAddress = new Uri("http://localhost:8000/");
|
||||
_workshopId= authHelper.GetWorkshopId();
|
||||
|
||||
}
|
||||
|
||||
[HttpPost("login")]
|
||||
[AllowAnonymous]
|
||||
public IActionResult CameraLogin([FromBody] CameraLoginRequest request)
|
||||
{
|
||||
_accountApplication.CameraLogin(request);
|
||||
return Ok(new
|
||||
{
|
||||
WorkshopId = _workshopId,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("embedding")]
|
||||
[AllowAnonymous]
|
||||
public async Task<ActionResult<List<EmployeeFaceEmbeddingDto>>> WorkshopEmbedding()
|
||||
{
|
||||
if (!User.Identity?.IsAuthenticated ?? false)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
_workshopId = _authHelper.GetWorkshopId();
|
||||
var data = await _employeeFaceEmbeddingApplication
|
||||
.GetByWorkshopIdsAsync([_workshopId]);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
[HttpPost("flag/{employeeId:long}")]
|
||||
public async Task<IActionResult> GetFlag(long employeeId)
|
||||
{
|
||||
var flagId = _rollCallApplication.Flag(employeeId, _workshopId);
|
||||
return Ok(new
|
||||
{
|
||||
flagId = flagId,
|
||||
status = flagId == 0 ? "enter": "exit"
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("exit")]
|
||||
public IActionResult Exit([FromBody]RollCallExitRequest request)
|
||||
{
|
||||
var employeeId = request.EmployeeId;
|
||||
var flagId = request.FlagId;
|
||||
bool rollCallEmployee = _rollCallEmployeeApplication.IsEmployeeRollCallActive(employeeId,
|
||||
_workshopId);
|
||||
|
||||
if (!rollCallEmployee)
|
||||
{
|
||||
throw new BadRequestException("پرسنل مورد نظر غیر فعال است");
|
||||
}
|
||||
|
||||
_faceEmbeddingHttpClient.PostAsync("embeddings/refine",
|
||||
JsonContent.Create(new {
|
||||
employeeId,
|
||||
workshopId = _workshopId,
|
||||
embedding =request.Embeddings ,
|
||||
confidence =request.Confidence ,
|
||||
metadata =new{},
|
||||
|
||||
}));
|
||||
|
||||
|
||||
|
||||
var repeatStatus = _rollCallApplication.CheckRepeat(employeeId, _workshopId);
|
||||
switch (repeatStatus)
|
||||
{
|
||||
case "IncomRegistred-InvalidOut":
|
||||
throw new BadRequestException("شما به تازگی ثبت ورود نموده اید ,تا 2 دقیقه نمی توانید ثبت خروج نمایید");
|
||||
|
||||
case "outRegistred-InvalidIncom":
|
||||
throw new BadRequestException("شما به تازگی ثبت خروج نموده اید تا 2 دقیقه نمی توانید ثبت ورود نمایید");
|
||||
|
||||
}
|
||||
|
||||
var res = _rollCallApplication.Edit(flagId);
|
||||
|
||||
if (res.IsSuccedded)
|
||||
{
|
||||
return new JsonResult(new
|
||||
{
|
||||
isSuccess = true,
|
||||
});
|
||||
}
|
||||
|
||||
throw new BadRequestException(res.Message);
|
||||
}
|
||||
|
||||
[HttpPost("enter")]
|
||||
public IActionResult Enter([FromBody]RollCallEnterRequest request)
|
||||
{
|
||||
var employeeId = request.EmployeeId;
|
||||
var now = DateTime.Now;
|
||||
var command = new CreateRollCall()
|
||||
{
|
||||
WorkshopId = _workshopId,
|
||||
EmployeeId = request.EmployeeId,
|
||||
StartDate = now,
|
||||
};
|
||||
bool rollCallEmployee = _rollCallEmployeeApplication.IsEmployeeRollCallActive(employeeId,
|
||||
_workshopId);
|
||||
|
||||
if (!rollCallEmployee)
|
||||
{
|
||||
throw new BadRequestException("پرسنل مورد نظر غیر فعال است");
|
||||
}
|
||||
|
||||
_faceEmbeddingHttpClient.PostAsync("embeddings/refine",
|
||||
JsonContent.Create(new {
|
||||
employeeId,
|
||||
workshopId = _workshopId,
|
||||
embedding =request.Embeddings ,
|
||||
confidence =request.Confidence ,
|
||||
metadata =new{},
|
||||
|
||||
|
||||
}));
|
||||
|
||||
|
||||
|
||||
var repeatStatus = _rollCallApplication.CheckRepeat(employeeId, _workshopId);
|
||||
switch (repeatStatus)
|
||||
{
|
||||
case "IncomRegistred-InvalidOut":
|
||||
throw new BadRequestException("شما به تازگی ثبت ورود نموده اید ,تا 2 دقیقه نمی توانید ثبت خروج نمایید");
|
||||
|
||||
case "outRegistred-InvalidIncom":
|
||||
throw new BadRequestException("شما به تازگی ثبت خروج نموده اید تا 2 دقیقه نمی توانید ثبت ورود نمایید");
|
||||
|
||||
}
|
||||
|
||||
|
||||
var res = _rollCallApplication.Create(command);
|
||||
if (res.IsSuccedded)
|
||||
{
|
||||
return new JsonResult(new
|
||||
{
|
||||
isSuccess = true,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
return new JsonResult(new
|
||||
{
|
||||
isSuccess = false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpGet("check-update")]
|
||||
public async Task<IActionResult> CheckUpdate([FromQuery] ApkType type, [FromQuery] int currentVersionCode = 0)
|
||||
{
|
||||
|
||||
var info = await _androidApkVersionApplication.GetLatestActiveInfo(type, currentVersionCode);
|
||||
return Ok(new
|
||||
{
|
||||
latestVersionCode = info.LatestVersionCode,
|
||||
latestVersionName = info.LatestVersionName,
|
||||
shouldUpdate = info.ShouldUpdate,
|
||||
isForceUpdate = info.IsForceUpdate,
|
||||
downloadUrl = info.DownloadUrl,
|
||||
releaseNotes = info.ReleaseNotes
|
||||
});
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpGet("download")]
|
||||
public async Task<IActionResult> Download([FromQuery] ApkType type)
|
||||
{
|
||||
// Check if APK exists
|
||||
if (!_androidApkVersionApplication.HasAndroidApkToDownload(type))
|
||||
{
|
||||
return NotFound(new { message = $"هیچ فایل APK فعالی برای {type} یافت نشد" });
|
||||
}
|
||||
|
||||
// Get the path to the latest active APK
|
||||
var path = await _androidApkVersionApplication.GetLatestActiveVersionPath(type);
|
||||
|
||||
if (string.IsNullOrEmpty(path) || !System.IO.File.Exists(path))
|
||||
{
|
||||
return NotFound(new { message = "فایل APK یافت نشد" });
|
||||
}
|
||||
|
||||
// Set appropriate file name for download
|
||||
var fileName = type == ApkType.WebView
|
||||
? "Gozareshgir.apk"
|
||||
: "Gozareshgir-FaceDetection.apk";
|
||||
|
||||
// Return the file for download
|
||||
return PhysicalFile(path, "application/vnd.android.package-archive", fileName);
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("SendPersonelCodeToGetEmployeeId")]
|
||||
public IActionResult SendPersonelCodeToGetEmployeeId(long personelCode, long workshopId)
|
||||
{
|
||||
long employeeId = _personnelCodeApplication.GetEmployeeIdByPersonelCode(personelCode, workshopId);
|
||||
|
||||
if (employeeId == 0)
|
||||
{
|
||||
return new JsonResult(new
|
||||
{
|
||||
isSuccess = false,
|
||||
message = "کد پرسنلی یافت نشد",
|
||||
});
|
||||
}
|
||||
|
||||
bool rollcallEmployee = _rollCallEmployeeApplication.IsEmployeeRollCallActive(employeeId, workshopId);
|
||||
if (!rollcallEmployee)
|
||||
{
|
||||
return new JsonResult(new
|
||||
{
|
||||
isSuccess = false,
|
||||
message = "پرسنل مورد نظر غیر فعال است",
|
||||
});
|
||||
}
|
||||
|
||||
var repeatStatus = _rollCallApplication.CheckRepeat(employeeId, workshopId);
|
||||
switch (repeatStatus)
|
||||
{
|
||||
case "IncomRegistred-InvalidOut":
|
||||
return new JsonResult(new
|
||||
{
|
||||
isSuccess = false,
|
||||
message = "شما به تازگی ثبت ورود نموده اید ,تا 2 دقیقه نمی توانید ثبت خروج نمایید",
|
||||
});
|
||||
case "outRegistred-InvalidIncom":
|
||||
return new JsonResult(new
|
||||
{
|
||||
isSuccess = false,
|
||||
message = "شما به تازگی ثبت خروج نموده اید تا 2 دقیقه نمی توانید ثبت ورود نمایید",
|
||||
});
|
||||
}
|
||||
|
||||
return new JsonResult(new
|
||||
{
|
||||
isSuccess = true,
|
||||
message = "",
|
||||
personId = $"{employeeId}",
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("EmployeeFlag")]
|
||||
public IActionResult EmployeeFlag(long employeeId, long workshopId)
|
||||
{
|
||||
var employee = _rollCallEmployeeApplication.GetByEmployeeIdAndWorkshopId(employeeId, workshopId);
|
||||
var employeeFullName = employee?.EmployeeFullName ?? string.Empty;
|
||||
var flagId = _rollCallApplication.Flag(employeeId, workshopId);
|
||||
|
||||
return new JsonResult(new
|
||||
{
|
||||
employeeName = employeeFullName,
|
||||
flag = flagId
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("Logout")]
|
||||
public IActionResult Logout()
|
||||
{
|
||||
_accountApplication.Logout();
|
||||
return new JsonResult(new { isSuccess = true });
|
||||
}
|
||||
}
|
||||
|
||||
public class RollCallExitRequest:RollCallEnterRequest
|
||||
{
|
||||
public long FlagId { get; set; }
|
||||
}
|
||||
public class RollCallEnterRequest
|
||||
{
|
||||
public long EmployeeId { get; set; }
|
||||
public List<double> Embeddings { get; set; }
|
||||
public float Confidence { get; set; }
|
||||
}
|
||||
|
||||
public class CameraFlagRequest
|
||||
{
|
||||
public long EmployeeId { get; set; }
|
||||
public long WorkshopId { get; set; }
|
||||
}
|
||||
@@ -93,8 +93,8 @@ public class FinancialController : ClientBaseController
|
||||
|
||||
if (gatewayResponse.IsSuccess)
|
||||
{
|
||||
_ = await _paymentTransactionApplication.SetTransactionId(transaction.SendId, gatewayResponse.TransactionId);
|
||||
return Redirect(_paymentGateway.GetStartPayUrl(gatewayResponse.TransactionId));
|
||||
_ = await _paymentTransactionApplication.SetTransactionId(transaction.SendId, gatewayResponse.Token);
|
||||
return Redirect(_paymentGateway.GetStartPayUrl(gatewayResponse.Token));
|
||||
}
|
||||
|
||||
if (gatewayResponse.ErrorCode.HasValue)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -65,6 +65,7 @@ namespace ServiceHost.Areas.Client.Pages
|
||||
#region Mahan
|
||||
public bool HasInsuranceToConfirm { get; set; }
|
||||
public bool HasApkToDownload { get; set; }
|
||||
public bool HasFaceDetectionApkToDownload { get; set; }
|
||||
|
||||
#endregion
|
||||
public IndexModel(IAuthHelper authHelper, IPasswordHasher passwordHasher, IWorkshopApplication workshopApplication, ILeaveApplication leaveApplication, IEmployeeApplication employeeApplication, IPaymentToEmployeeItemApplication paymentToEmployeeItemApplication, IPaymentToEmployeeApplication paymentToEmployeeApplication, IHolidayItemApplication holidayItemApplication, IInsuranceListApplication insuranceListApplication, IAndroidApkVersionApplication androidApkVersionApplication, IRollCallServiceApplication rollCallServiceApplication, IPersonnelCodeApplication personnelCodeApplication, ICustomizeWorkshopSettingsApplication customizeWorkshopSettingsApplication, IBankApplication bankApplication, ILeftWorkTempApplication leftWorkTempApplication, IJobApplication jobApplication, ICustomizeWorkshopSettingsApplication customizeWorkshopEmployee, IRollCallEmployeeStatusApplication rollCallEmployeeStatusApplication, ICameraAccountApplication cameraAccountApplication)
|
||||
@@ -100,7 +101,8 @@ namespace ServiceHost.Areas.Client.Pages
|
||||
profilePicture = account.ProfilePhoto;
|
||||
AccountFullName = account.Fullname;
|
||||
var todayGr = DateTime.Now;
|
||||
HasApkToDownload = _androidApkVersionApplication.HasAndroidApkToDownload();
|
||||
HasApkToDownload = _androidApkVersionApplication.HasAndroidApkToDownload(ApkType.WebView);
|
||||
HasFaceDetectionApkToDownload = _androidApkVersionApplication.HasAndroidApkToDownload(ApkType.FaceDetection);
|
||||
|
||||
|
||||
#region Mahan
|
||||
|
||||
@@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
namespace ServiceHost.BaseControllers;
|
||||
|
||||
|
||||
//[ApiExplorerSettings(GroupName = "Camera")]
|
||||
[ApiExplorerSettings(GroupName = "Camera")]
|
||||
[Authorize(Policy = "CameraArea")]
|
||||
[Area("Camera")]
|
||||
[Route("api/[area]/[controller]")]
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
using _0_Framework.Application;
|
||||
using _0_Framework.Application.PaymentGateway;
|
||||
using Company.Domain.BankAgg;
|
||||
using Company.Domain.PaymentTransactionAgg;
|
||||
using CompanyManagment.App.Contracts.FinancialStatment;
|
||||
using CompanyManagment.App.Contracts.FinancilTransaction;
|
||||
using CompanyManagment.App.Contracts.PaymentTransaction;
|
||||
using CompanyManagment.App.Contracts.Workshop;
|
||||
using CompanyManagment.EFCore.Migrations;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ServiceHost.BaseControllers;
|
||||
using System.Globalization;
|
||||
using _0_Framework.Application.PaymentGateway;
|
||||
using Microsoft.Extensions.Options;
|
||||
using CompanyManagment.App.Contracts.FinancialStatment;
|
||||
using CompanyManagment.App.Contracts.FinancilTransaction;
|
||||
using Newtonsoft.Json;
|
||||
using NuGet.Protocol;
|
||||
using Parbad;
|
||||
using ServiceHost.BaseControllers;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Globalization;
|
||||
using System.Net.Http;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
|
||||
namespace ServiceHost.Controllers;
|
||||
|
||||
@@ -18,12 +28,14 @@ public class GeneralController : GeneralBaseController
|
||||
private readonly IPaymentTransactionApplication _paymentTransactionApplication;
|
||||
private readonly IPaymentGateway _paymentGateway;
|
||||
private readonly IFinancialStatmentApplication _financialStatmentApplication;
|
||||
private readonly IOnlinePayment _onlinePayment;
|
||||
|
||||
public GeneralController(IPaymentTransactionApplication paymentTransactionApplication,IHttpClientFactory clientFactory, IFinancialStatmentApplication financialStatmentApplication, IOptions<AppSettingConfiguration> appSetting)
|
||||
public GeneralController(IPaymentTransactionApplication paymentTransactionApplication,IHttpClientFactory clientFactory, IFinancialStatmentApplication financialStatmentApplication, IOptions<AppSettingConfiguration> appSetting, IOnlinePayment onlinePayment)
|
||||
{
|
||||
_paymentTransactionApplication = paymentTransactionApplication;
|
||||
_paymentGateway = new AqayePardakhtPaymentGateway(clientFactory, appSetting);
|
||||
_paymentGateway = new SepehrPaymentGateway(clientFactory);
|
||||
_financialStatmentApplication = financialStatmentApplication;
|
||||
_onlinePayment = onlinePayment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -47,7 +59,103 @@ public class GeneralController : GeneralBaseController
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("/api/callback")]
|
||||
|
||||
[HttpGet("/api/callback"), HttpPost("/api/callback")]
|
||||
public async Task<IActionResult> Verify(SepehrGatewayPayResponse payResponse)
|
||||
{
|
||||
if (!long.TryParse(payResponse.invoiceid, out var paymentTransactionId))
|
||||
{
|
||||
return BadRequest("Invalid invoice_id");
|
||||
}
|
||||
|
||||
var transaction = await _paymentTransactionApplication.GetDetails(paymentTransactionId);
|
||||
|
||||
if (transaction == null)
|
||||
{
|
||||
return NotFound("Transaction not found");
|
||||
}
|
||||
|
||||
if (transaction.Status != PaymentTransactionStatus.Pending)
|
||||
{
|
||||
return BadRequest("این تراکنش قبلا پرداخت شده است");
|
||||
}
|
||||
|
||||
|
||||
if (payResponse.respcode != 0)
|
||||
{
|
||||
return await HandleFailedTransaction(transaction);
|
||||
}
|
||||
|
||||
var verifyCommand = new VerifyPaymentGateWayRequest()
|
||||
{
|
||||
Amount = transaction.Amount,
|
||||
TransactionId = payResponse.invoiceid,
|
||||
DigitalReceipt = payResponse.digitalreceipt
|
||||
};
|
||||
var verifyRes = await _paymentGateway.Verify(verifyCommand, CancellationToken.None);
|
||||
|
||||
|
||||
|
||||
|
||||
if (verifyRes.IsSuccess)
|
||||
{
|
||||
var command = new CreateFinancialStatment()
|
||||
{
|
||||
ContractingPartyId = transaction.ContractingPartyId,
|
||||
Deptor = 0,
|
||||
Creditor = transaction.Amount,
|
||||
DeptorString = "0",
|
||||
TypeOfTransaction = "credit",
|
||||
DescriptionOption = "بابت قرارداد مابین (روابط کار)",
|
||||
Description = "درگاه بانکی",
|
||||
|
||||
};
|
||||
var statementResult = _financialStatmentApplication.CreateFromBankGateway(command);
|
||||
if (!statementResult.IsSuccedded)
|
||||
{
|
||||
return new JsonResult(statementResult);
|
||||
}
|
||||
|
||||
var setSuccessResult = _paymentTransactionApplication.SetSuccess(paymentTransactionId, payResponse.cardnumber, payResponse.issuerbank, payResponse.rrn.ToString(), payResponse.digitalreceipt);
|
||||
|
||||
if (!setSuccessResult.IsSuccedded)
|
||||
{
|
||||
return await HandleFailedTransaction(transaction);
|
||||
}
|
||||
return Redirect(BuildCallbackUrl(transaction.CallBackUrl, true, transaction.Id));
|
||||
}
|
||||
|
||||
// در غیر این صورت تراکنش ناموفق است
|
||||
return await HandleFailedTransaction(transaction);
|
||||
|
||||
|
||||
//var data = JsonConvert.SerializeObject(invoice.AdditionalData);
|
||||
//var statics =
|
||||
//JsonConvert.SerializeObject(res);
|
||||
|
||||
//await _onlinePayment.CancelAsync(invoice);
|
||||
//return new JsonResult(new
|
||||
//{
|
||||
// data,
|
||||
// statics
|
||||
//});
|
||||
|
||||
//// Check if the invoice is new, or it's already processed before.
|
||||
//if (invoice.Status != PaymentFetchResultStatus.ReadyForVerifying)
|
||||
//{
|
||||
// // You can also see if the invoice is already verified before.
|
||||
// var isAlreadyVerified = invoice.IsAlreadyVerified;
|
||||
|
||||
// return Content("The payment was not successful.");
|
||||
//}
|
||||
|
||||
//// Note: Save the verifyResult.TransactionCode in your database.
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
[HttpPost("/api/callback3232")]
|
||||
public async Task<IActionResult> OnGetCallBack(string? transid, string? cardnumber, string? tracking_number,
|
||||
string bank, string invoice_id, string? status,CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -70,7 +178,7 @@ public class GeneralController : GeneralBaseController
|
||||
// اگر شماره کارت یا شماره پیگیری خالی باشد، تراکنش ناموفق است
|
||||
if (string.IsNullOrWhiteSpace(cardnumber) || string.IsNullOrWhiteSpace(tracking_number))
|
||||
{
|
||||
return await HandleFailedTransaction(transaction, paymentTransactionId);
|
||||
return await HandleFailedTransaction(transaction);
|
||||
}
|
||||
|
||||
var verifyCommand = new VerifyPaymentGateWayRequest()
|
||||
@@ -97,10 +205,10 @@ public class GeneralController : GeneralBaseController
|
||||
var statementResult = _financialStatmentApplication.CreateFromBankGateway(command);
|
||||
if (!statementResult.IsSuccedded)
|
||||
{
|
||||
return await HandleFailedTransaction(transaction, paymentTransactionId);
|
||||
return await HandleFailedTransaction(transaction);
|
||||
}
|
||||
|
||||
var setSuccessResult = _paymentTransactionApplication.SetSuccess(paymentTransactionId, cardnumber, bank);
|
||||
var setSuccessResult = _paymentTransactionApplication.SetSuccess(paymentTransactionId, cardnumber, bank,null,null);
|
||||
|
||||
if (!setSuccessResult.IsSuccedded)
|
||||
{
|
||||
@@ -110,12 +218,12 @@ public class GeneralController : GeneralBaseController
|
||||
}
|
||||
|
||||
// در غیر این صورت تراکنش ناموفق است
|
||||
return await HandleFailedTransaction(transaction, paymentTransactionId);
|
||||
return await HandleFailedTransaction(transaction);
|
||||
}
|
||||
|
||||
private async Task<IActionResult> HandleFailedTransaction(PaymentTransactionDetailsViewModel transaction, long transactionId)
|
||||
private async Task<IActionResult> HandleFailedTransaction(PaymentTransactionDetailsViewModel transaction)
|
||||
{
|
||||
var result = _paymentTransactionApplication.SetFailed(transactionId);
|
||||
var result = _paymentTransactionApplication.SetFailed(transaction.Id);
|
||||
if (!result.IsSuccedded)
|
||||
{
|
||||
return new JsonResult(result);
|
||||
@@ -131,4 +239,81 @@ public class GeneralController : GeneralBaseController
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class TokenReq
|
||||
{
|
||||
|
||||
public long Amount { get; set; }
|
||||
|
||||
public string CallbackUrl { get; set; }
|
||||
[Display(Name = "شماره فاکتور")]
|
||||
[MaxLength(100)]
|
||||
[Required]
|
||||
[Key]
|
||||
// be ezaye har pazirande bayad yekta bashad
|
||||
public string invoiceID { get; set; }
|
||||
[Required]
|
||||
public long terminalID { get; set; }
|
||||
/*
|
||||
* JSON Bashad
|
||||
* etelaate takmili site harchi
|
||||
* nabayad char khas dashte bashe (*'"xp_%!+- ...)
|
||||
*/
|
||||
public string Payload { get; set; } = "";
|
||||
public string email { get; set; }
|
||||
}
|
||||
public class TokenResp
|
||||
{
|
||||
// if 0 = success
|
||||
public int Status { get; set; }
|
||||
public string AccessToken { get; set; }
|
||||
}
|
||||
public class PayRequest
|
||||
{
|
||||
[Required]
|
||||
[MaxLength(3000)]
|
||||
public string token { get; set; }
|
||||
[Required]
|
||||
public long terminalID { get; set; }
|
||||
public string nationalCode { get; set; }
|
||||
|
||||
}
|
||||
public class SepehrGatewayPayResponse
|
||||
{
|
||||
/* 0 yni movafaq
|
||||
* -1 yni enseraf
|
||||
* -2 yni etmam zaman
|
||||
*/
|
||||
public int respcode { get; set; }
|
||||
[Display(Name = "متن نتیجه تراکنش")]
|
||||
public string respmsg { get; set; }
|
||||
[Display(Name = "مبلغ کسر شده از مشتری")]
|
||||
public long amount { get; set; }
|
||||
[Display(Name = " شماره فاکتور ")]
|
||||
public string invoiceid { get; set; }
|
||||
[Display(Name = " اطلاعاتی که پذیرنده ارسال کرد ")]
|
||||
public string payload { get; set; }
|
||||
[Display(Name = " شماره ترمینال ")]
|
||||
public long terminalid { get; set; }
|
||||
[Display(Name = " شماره پیگیری ")]
|
||||
public long tracenumber { get; set; }
|
||||
// bayad negah dari she hatman to db
|
||||
[Display(Name = " شماره سند بانکی ")]
|
||||
public long rrn { get; set; }
|
||||
[Display(Name = " زمان و تاریخ پرداخت ")]
|
||||
public string datepaid { get; set; }
|
||||
[Display(Name = " رسید دیجیتال ")]
|
||||
public string digitalreceipt { get; set; }
|
||||
[Display(Name = " نام بانک صادر کننده کارت ")]
|
||||
public string issuerbank { get; set; }
|
||||
[Display(Name = " شماره کارت ")]
|
||||
public string cardnumber { get; set; }
|
||||
}
|
||||
|
||||
public class AdviceReq
|
||||
{
|
||||
[Display(Name = " رسید دیجیتال ")]
|
||||
public string digitalreceipt { get; set; }
|
||||
public long Tid { get; set; }
|
||||
}
|
||||
58
ServiceHost/FaceEmbeddingNotificationService.cs
Normal file
58
ServiceHost/FaceEmbeddingNotificationService.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using _0_Framework.Application.FaceEmbedding;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using ServiceHost.Hubs;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ServiceHost
|
||||
{
|
||||
/// <summary>
|
||||
/// پیادهسازی سرویس اطلاعرسانی Face Embedding با استفاده از SignalR
|
||||
/// </summary>
|
||||
public class FaceEmbeddingNotificationService : IFaceEmbeddingNotificationService
|
||||
{
|
||||
private readonly IHubContext<FaceEmbeddingHub> _hubContext;
|
||||
|
||||
public FaceEmbeddingNotificationService(IHubContext<FaceEmbeddingHub> hubContext)
|
||||
{
|
||||
_hubContext = hubContext;
|
||||
}
|
||||
|
||||
public async Task NotifyEmbeddingCreatedAsync(long workshopId, long employeeId, string employeeFullName)
|
||||
{
|
||||
var groupName = FaceEmbeddingHub.GetGroupName(workshopId);
|
||||
|
||||
await _hubContext.Clients.Group(groupName).SendAsync("EmbeddingCreated", new
|
||||
{
|
||||
workshopId,
|
||||
employeeId,
|
||||
employeeFullName,
|
||||
timestamp = System.DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
||||
public async Task NotifyEmbeddingDeletedAsync(long workshopId, long employeeId)
|
||||
{
|
||||
var groupName = FaceEmbeddingHub.GetGroupName(workshopId);
|
||||
|
||||
await _hubContext.Clients.Group(groupName).SendAsync("EmbeddingDeleted", new
|
||||
{
|
||||
workshopId,
|
||||
employeeId,
|
||||
timestamp = System.DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
||||
public async Task NotifyEmbeddingRefinedAsync(long workshopId, long employeeId)
|
||||
{
|
||||
var groupName = FaceEmbeddingHub.GetGroupName(workshopId);
|
||||
|
||||
await _hubContext.Clients.Group(groupName).SendAsync("EmbeddingRefined", new
|
||||
{
|
||||
workshopId,
|
||||
employeeId,
|
||||
timestamp = System.DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
23
ServiceHost/Hubs/FaceEmbeddingHub.cs
Normal file
23
ServiceHost/Hubs/FaceEmbeddingHub.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
namespace ServiceHost.Hubs
|
||||
{
|
||||
public class FaceEmbeddingHub : Hub
|
||||
{
|
||||
public async Task JoinWorkshopGroup(long workshopId)
|
||||
{
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, GetGroupName(workshopId));
|
||||
}
|
||||
|
||||
public async Task LeaveWorkshopGroup(long workshopId)
|
||||
{
|
||||
await Groups.RemoveFromGroupAsync(Context.ConnectionId, GetGroupName(workshopId));
|
||||
}
|
||||
|
||||
public static string GetGroupName(long workshopId)
|
||||
{
|
||||
return $"group-faceembedding-{workshopId}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,11 @@ public class AndroidApk : Controller
|
||||
}
|
||||
|
||||
[Route("Apk/Android")]
|
||||
public async Task<IActionResult> Index()
|
||||
public async Task<IActionResult> Index([FromQuery] string type = "WebView")
|
||||
{
|
||||
var path = await _androidApkVersionApplication.GetLatestActiveVersionPath();
|
||||
return PhysicalFile(path,"application/vnd.android.package-archive","Gozareshgir.apk");
|
||||
ApkType apkType = type.ToLower() == "facedetection" ? ApkType.FaceDetection : ApkType.WebView;
|
||||
var path = await _androidApkVersionApplication.GetLatestActiveVersionPath(apkType);
|
||||
var fileName = apkType == ApkType.WebView ? "Gozareshgir.apk" : "Gozareshgir-FaceDetection.apk";
|
||||
return PhysicalFile(path,"application/vnd.android.package-archive", fileName);
|
||||
}
|
||||
}
|
||||
@@ -185,10 +185,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (Model.HasApkToDownload)
|
||||
@if (Model.HasFaceDetectionApkToDownload)
|
||||
{
|
||||
<div class="position-fixed d-md-none d-block" style="bottom:18px;" id="downloadAppLogin">
|
||||
<a href="/apk/android" type="button" class="btn-login d-block mx-auto w-100 text-white px-3 py-2 d-flex align-items-center">
|
||||
<a href="/apk/android?type=facedetection" type="button" class="btn-login d-block mx-auto w-100 text-white px-3 py-2 d-flex align-items-center">
|
||||
<svg width="18" height="18" fill="#ffffff" viewBox="-1 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g>
|
||||
@@ -196,7 +196,7 @@
|
||||
<path d="m3.751.61 13.124 7.546-2.813 2.813zm-2.719-.61 12.047 12-12.046 12c-.613-.271-1.033-.874-1.033-1.575 0-.023 0-.046.001-.068v.003-20.719c-.001-.019-.001-.042-.001-.065 0-.701.42-1.304 1.022-1.571l.011-.004zm19.922 10.594c.414.307.679.795.679 1.344 0 .022 0 .043-.001.065v-.003c.004.043.007.094.007.145 0 .516-.25.974-.636 1.258l-.004.003-2.813 1.593-3.046-2.999 3.047-3.047zm-17.203 12.796 10.312-10.359 2.813 2.813z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
<span class="mx-1">دانلود نرم افزار مخصوص موبایل</span>
|
||||
<span class="mx-1">دانلود نرم افزار حضورغیاب اندروید</span>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ public class IndexModel : PageModel
|
||||
[BindProperty] public string Password { get; set; }
|
||||
[BindProperty] public string CaptchaResponse { get; set; }
|
||||
public bool HasApkToDownload { get; set; }
|
||||
public bool HasFaceDetectionApkToDownload { get; set; }
|
||||
private static Timer aTimer;
|
||||
public Login login;
|
||||
public AccountViewModel Search;
|
||||
@@ -76,7 +77,8 @@ public class IndexModel : PageModel
|
||||
|
||||
//_context.SaveChanges();
|
||||
|
||||
HasApkToDownload = _androidApkVersionApplication.HasAndroidApkToDownload();
|
||||
HasApkToDownload = _androidApkVersionApplication.HasAndroidApkToDownload(ApkType.WebView);
|
||||
HasFaceDetectionApkToDownload = _androidApkVersionApplication.HasAndroidApkToDownload(ApkType.FaceDetection);
|
||||
if (User.Identity is { IsAuthenticated: true })
|
||||
{
|
||||
if (User.FindFirstValue("IsCamera") == "true")
|
||||
|
||||
@@ -19,17 +19,21 @@ using ServiceHost.MiddleWare;
|
||||
using WorkFlow.Infrastructure.Config;
|
||||
using _0_Framework.Application.UID;
|
||||
using _0_Framework.Exceptions.Handler;
|
||||
using _0_Framework.Application.FaceEmbedding;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using ServiceHost.Test;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json;
|
||||
using _0_Framework.InfraStructure.Mongo;
|
||||
using Bogus;
|
||||
using CompanyManagment.EFCore.Services;
|
||||
using Microsoft.AspNetCore.CookiePolicy;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using MongoDB.Driver;
|
||||
using Parbad.Builder;
|
||||
using Parbad.Gateway.Sepehr;
|
||||
using Swashbuckle.AspNetCore.SwaggerUI;
|
||||
|
||||
|
||||
@@ -73,6 +77,7 @@ builder.Services.AddTransient<IAuthHelper, AuthHelper>();
|
||||
builder.Services.AddTransient<IGoogleRecaptcha, GoogleRecaptcha>();
|
||||
builder.Services.AddTransient<ISmsService, SmsService>();
|
||||
builder.Services.AddTransient<IUidService, UidService>();
|
||||
builder.Services.AddTransient<IFaceEmbeddingNotificationService, FaceEmbeddingNotificationService>();
|
||||
//services.AddSingleton<IWorkingTest, WorkingTest>();
|
||||
//services.AddHostedService<JobWorker>();
|
||||
|
||||
@@ -306,11 +311,29 @@ builder.Services.AddCors(options =>
|
||||
|
||||
builder.Services.AddExceptionHandler<CustomExceptionHandler>();
|
||||
|
||||
var sepehrTerminalId = builder.Configuration.GetValue<long>("SepehrGateWayTerminalId");
|
||||
|
||||
builder.Services.AddParbad().ConfigureGateways(gateways =>
|
||||
{
|
||||
gateways.AddSepehr().WithAccounts(accounts =>
|
||||
{
|
||||
accounts.AddInMemory(account =>
|
||||
{
|
||||
account.TerminalId = sepehrTerminalId;
|
||||
account.Name="Sepehr Account";
|
||||
});
|
||||
});
|
||||
}).ConfigureHttpContext(httpContext=>httpContext.UseDefaultAspNetCore())
|
||||
.ConfigureStorage(storage =>
|
||||
{
|
||||
storage.UseMemoryCache();
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
app.UseCors("AllowSpecificOrigins");
|
||||
|
||||
|
||||
|
||||
#region Mahan
|
||||
|
||||
//app.UseStatusCodePagesWithRedirects("/error/{0}");
|
||||
@@ -324,6 +347,7 @@ if (builder.Environment.IsDevelopment())
|
||||
await tester.Test();
|
||||
}
|
||||
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
@@ -384,6 +408,7 @@ app.MapHub<CreateContractTarckingHub>("/trackingHub");
|
||||
app.MapHub<SendAccountMessage>("/trackingSmsHub");
|
||||
app.MapHub<HolidayApiHub>("/trackingHolidayHub");
|
||||
app.MapHub<CheckoutHub>("/trackingCheckoutHub");
|
||||
// app.MapHub<FaceEmbeddingHub>("/trackingFaceEmbeddingHub");
|
||||
app.MapRazorPages();
|
||||
app.MapControllers();
|
||||
|
||||
|
||||
@@ -90,6 +90,8 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.2" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="3.5.0" />
|
||||
<PackageReference Include="Parbad.AspNetCore" Version="1.5.0" />
|
||||
<PackageReference Include="Parbad.Storage.Cache" Version="1.5.0" />
|
||||
<PackageReference Include="SocialExplorer.FastDBF" Version="1.0.0" />
|
||||
<PackageReference Include="System.Data.OleDb" Version="8.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.4" />
|
||||
|
||||
@@ -38,7 +38,12 @@
|
||||
"MongoDb": {
|
||||
"ConnectionString": "mongodb://localhost:27017",
|
||||
"DatabaseName": "Gozareshgir"
|
||||
}
|
||||
},
|
||||
"SmsSettings": {
|
||||
"IsTestMode": true,
|
||||
"TestNumbers": [ "09116967898", "09116067106", "09114221321" ]
|
||||
},
|
||||
"SepehrGateWayTerminalId": 99213700
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -31,5 +31,11 @@
|
||||
"MongoDb": {
|
||||
"ConnectionString": "mongodb://localhost:27017",
|
||||
"DatabaseName": "Gozareshgir"
|
||||
}
|
||||
},
|
||||
"SmsSettings": {
|
||||
"IsTestMode": false,
|
||||
"TestNumbers": []
|
||||
},
|
||||
"SepehrGateWayTerminalId": 99213700
|
||||
|
||||
}
|
||||
|
||||
@@ -254,14 +254,14 @@ function loadEmployeeAjax(groupSettingId) {
|
||||
<input type="text" class="form-control" id="searchEmployee" placeholder="جستجو ..." />
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
console.log(responseDataEmployee);
|
||||
responseDataEmployee.forEach(function (itemEmployee) {
|
||||
if (itemEmployee.isShiftChanged === true) {
|
||||
isShiftChangedGlobal = true;
|
||||
}
|
||||
|
||||
htmlEmployee += `<div></div>
|
||||
<div class="my-1 Rtable-row align-items-center position-relative itemResultEmployee" data-employee-id="${itemEmployee.employeeId}">
|
||||
<div class="my-1 Rtable-row align-items-center position-relative itemResultEmployee ${itemEmployee.hasLeft?'disable':''}" data-employee-id="${itemEmployee.employeeId}">
|
||||
<div class="Rtable-cell width1 widthMobile1">
|
||||
<div class="Rtable-cell--content">
|
||||
<span class="d-flex justify-content-center row-index2">
|
||||
|
||||
404520
ServiceHost/wwwroot/embeddings.json
Normal file
404520
ServiceHost/wwwroot/embeddings.json
Normal file
File diff suppressed because it is too large
Load Diff
184
plan-addApkTypeToAndroidApkVersion.prompt.md
Normal file
184
plan-addApkTypeToAndroidApkVersion.prompt.md
Normal file
@@ -0,0 +1,184 @@
|
||||
# Plan: Add ApkType to AndroidApkVersion for WebView and FaceDetection separation
|
||||
|
||||
## Overview
|
||||
|
||||
The user wants to integrate the `ApkType` enum (WebView/FaceDetection) into the `AndroidApkVersion` entity to distinguish between the two different APK types. Currently, all APKs are treated as WebView. The system needs to be updated to support both types with separate handling.
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Update AndroidApkVersion.cs domain entity
|
||||
**File:** `Company.Domain\AndroidApkVersionAgg\AndroidApkVersion.cs`
|
||||
|
||||
- Add `ApkType` property to the class
|
||||
- Update constructor to accept `ApkType` parameter
|
||||
- Modify `Title` property generation to include APK type information
|
||||
- Update the constructor logic to handle both WebView and FaceDetection types
|
||||
|
||||
### 2. Update AndroidApkVersionMapping.cs EF configuration
|
||||
**File:** `CompanyManagment.EFCore\Mapping\AndroidApkVersionMapping.cs`
|
||||
|
||||
- Add mapping configuration for `ApkType` property
|
||||
- Use enum-to-string conversion (similar to existing `IsActive` mapping)
|
||||
- Set appropriate column max length for the enum value
|
||||
|
||||
### 3. Create database migration
|
||||
**Files:** Generated migration files in `CompanyManagment.EFCore\Migrations\`
|
||||
|
||||
- Generate new migration to add `ApkType` column to `AndroidApkVersions` table
|
||||
- Set default value to `ApkType.WebView` for existing records
|
||||
- Apply migration to update database schema
|
||||
|
||||
### 4. Update IAndroidApkVersionRepository.cs interface
|
||||
**File:** `Company.Domain\AndroidApkVersionAgg\IAndroidApkVersionRepository.cs`
|
||||
|
||||
- Modify `GetActives()` to accept `ApkType` parameter for filtering
|
||||
- Modify `GetLatestActiveVersionPath()` to accept `ApkType` parameter
|
||||
- Add methods to handle type-specific queries
|
||||
|
||||
### 5. Update AndroidApkVersionRepository.cs implementation
|
||||
**File:** `CompanyManagment.EFCore\Repository\AndroidApkVersionRepository.cs`
|
||||
|
||||
- Implement type-based filtering in `GetActives()` method
|
||||
- Implement type-based filtering in `GetLatestActiveVersionPath()` method
|
||||
- Add appropriate WHERE clauses to filter by `ApkType`
|
||||
|
||||
### 6. Update IAndroidApkVersionApplication.cs interface
|
||||
**File:** `CompanyManagment.App.Contracts\AndroidApkVersion\IAndroidApkVersionApplication.cs`
|
||||
|
||||
- Add `ApkType` parameter to `CreateAndActive()` method
|
||||
- Add `ApkType` parameter to `CreateAndDeActive()` method
|
||||
- Add `ApkType` parameter to `GetLatestActiveVersionPath()` method
|
||||
- Add `ApkType` parameter to `HasAndroidApkToDownload()` method
|
||||
|
||||
### 7. Update AndroidApkVersionApplication.cs implementation
|
||||
**File:** `CompanyManagment.Application\AndroidApkVersionApplication.cs`
|
||||
|
||||
- Update `CreateAndActive()` method:
|
||||
- Accept `ApkType` parameter
|
||||
- Change storage path from hardcoded "GozreshgirWebView" to dynamic based on type
|
||||
- Use "GozreshgirWebView" for `ApkType.WebView`
|
||||
- Use "GozreshgirFaceDetection" for `ApkType.FaceDetection`
|
||||
- Pass `ApkType` to repository methods when getting/deactivating existing APKs
|
||||
- Pass `ApkType` to entity constructor
|
||||
|
||||
- Update `CreateAndDeActive()` method:
|
||||
- Accept `ApkType` parameter
|
||||
- Update storage path logic similar to `CreateAndActive()`
|
||||
- Pass `ApkType` to entity constructor
|
||||
|
||||
- Update `GetLatestActiveVersionPath()` method:
|
||||
- Accept `ApkType` parameter
|
||||
- Pass type to repository method
|
||||
|
||||
- Update `HasAndroidApkToDownload()` method:
|
||||
- Accept `ApkType` parameter
|
||||
- Filter by type when checking for active APKs
|
||||
|
||||
### 8. Update AndroidApk.cs controller
|
||||
**File:** `ServiceHost\Pages\Apk\AndroidApk.cs`
|
||||
|
||||
- Modify the download endpoint to accept `ApkType` parameter
|
||||
- Options:
|
||||
- Add query string parameter: `/Apk/Android?type=WebView` or `/Apk/Android?type=FaceDetection`
|
||||
- Create separate routes: `/Apk/Android/WebView` and `/Apk/Android/FaceDetection`
|
||||
- Pass the type parameter to `GetLatestActiveVersionPath()` method
|
||||
- Maintain backward compatibility by defaulting to `ApkType.WebView` if no type specified
|
||||
|
||||
### 9. Update admin UI Index.cshtml.cs
|
||||
**File:** `ServiceHost\Areas\AdminNew\Pages\Company\AndroidApk\Index.cshtml.cs`
|
||||
|
||||
- Add property to store selected `ApkType`
|
||||
- Add `[BindProperty]` for ApkType selection
|
||||
- Modify `OnPostUpload()` to pass selected `ApkType` to application method
|
||||
- Create corresponding UI changes in Index.cshtml (if exists) to allow type selection
|
||||
|
||||
### 10. Update client-facing pages
|
||||
**Files:**
|
||||
- `ServiceHost\Pages\login\Index.cshtml.cs`
|
||||
- `ServiceHost\Areas\Client\Pages\Index.cshtml.cs`
|
||||
|
||||
- Update calls to `HasAndroidApkToDownload()` to specify which APK type to check
|
||||
- Consider showing different download buttons/links for WebView vs FaceDetection apps
|
||||
- Update download links to include APK type parameter
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
### Handling Existing Data
|
||||
- All existing `AndroidApkVersion` records should be marked as `ApkType.WebView` by default
|
||||
- Use migration to set default value
|
||||
- No manual data update required if migration includes default value
|
||||
|
||||
### Database Schema Change
|
||||
```sql
|
||||
ALTER TABLE AndroidApkVersions
|
||||
ADD ApkType NVARCHAR(20) NOT NULL DEFAULT 'WebView';
|
||||
```
|
||||
|
||||
## UI Design Considerations
|
||||
|
||||
### Admin Upload Page
|
||||
**Recommended approach:** Single form with radio buttons or dropdown
|
||||
|
||||
- Add radio buttons or dropdown to select APK type before upload
|
||||
- Labels: "WebView Application" and "Face Detection Application"
|
||||
- Group uploads by type in the list/table view
|
||||
- Show type column in the APK list
|
||||
|
||||
### Client Download Pages
|
||||
**Recommended approach:** Separate download buttons
|
||||
|
||||
- Show "Download Gozareshgir WebView" button (existing functionality)
|
||||
- Show "Download Gozareshgir FaceDetection" button (new functionality)
|
||||
- Only show buttons if corresponding APK type is available
|
||||
- Use different icons or colors to distinguish between types
|
||||
|
||||
## Download URL Structure
|
||||
|
||||
**Recommended approach:** Single endpoint with query parameter
|
||||
|
||||
- Current: `/Apk/Android` (defaults to WebView for backward compatibility)
|
||||
- New WebView: `/Apk/Android?type=WebView`
|
||||
- New FaceDetection: `/Apk/Android?type=FaceDetection`
|
||||
|
||||
**Alternative approach:** Separate endpoints
|
||||
- `/Apk/Android/WebView`
|
||||
- `/Apk/Android/FaceDetection`
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
1. ✅ Upload WebView APK successfully
|
||||
2. ✅ Upload FaceDetection APK successfully
|
||||
3. ✅ Both types can coexist in database
|
||||
4. ✅ Activating WebView APK doesn't affect FaceDetection APK
|
||||
5. ✅ Activating FaceDetection APK doesn't affect WebView APK
|
||||
6. ✅ Download correct APK based on type parameter
|
||||
7. ✅ Admin UI shows type information correctly
|
||||
8. ✅ Client pages show correct download availability
|
||||
9. ✅ Backward compatibility maintained (existing links still work)
|
||||
10. ✅ Migration applies successfully to existing database
|
||||
|
||||
## File Summary
|
||||
|
||||
**Files to modify:**
|
||||
1. `Company.Domain\AndroidApkVersionAgg\AndroidApkVersion.cs`
|
||||
2. `CompanyManagment.EFCore\Mapping\AndroidApkVersionMapping.cs`
|
||||
3. `Company.Domain\AndroidApkVersionAgg\IAndroidApkVersionRepository.cs`
|
||||
4. `CompanyManagment.EFCore\Repository\AndroidApkVersionRepository.cs`
|
||||
5. `CompanyManagment.App.Contracts\AndroidApkVersion\IAndroidApkVersionApplication.cs`
|
||||
6. `CompanyManagment.Application\AndroidApkVersionApplication.cs`
|
||||
7. `ServiceHost\Pages\Apk\AndroidApk.cs`
|
||||
8. `ServiceHost\Areas\AdminNew\Pages\Company\AndroidApk\Index.cshtml.cs`
|
||||
9. `ServiceHost\Pages\login\Index.cshtml.cs`
|
||||
10. `ServiceHost\Areas\Client\Pages\Index.cshtml.cs`
|
||||
|
||||
**Files to create:**
|
||||
1. New migration file (auto-generated)
|
||||
2. Possibly `ServiceHost\Areas\AdminNew\Pages\Company\AndroidApk\Index.cshtml` (if doesn't exist)
|
||||
|
||||
## Notes
|
||||
|
||||
- The `ApkType` enum is already defined in `AndroidApkVersion.cs`
|
||||
- Storage folders will be separate: `Storage/Apk/Android/GozreshgirWebView` and `Storage/Apk/Android/GozreshgirFaceDetection`
|
||||
- Each APK type maintains its own active/inactive state independently
|
||||
- Consider adding validation to ensure APK file matches selected type (optional enhancement)
|
||||
|
||||
151
plan-addIsForceToAndroidApkVersion.prompt.md
Normal file
151
plan-addIsForceToAndroidApkVersion.prompt.md
Normal file
@@ -0,0 +1,151 @@
|
||||
# Plan: Add IsForce Field to Android APK Version Management
|
||||
|
||||
## Overview
|
||||
Add support for force update functionality to the Android APK version management system. This allows administrators to specify whether an APK update is mandatory (force update) or optional when uploading new versions. The system now supports two separate APK types: WebView and FaceDetection.
|
||||
|
||||
## Context
|
||||
- The system manages Android APK versions for two different application types
|
||||
- Previously, all updates were treated as optional
|
||||
- Need to add ability to mark certain updates as mandatory
|
||||
- Force update flag should be stored in database and returned via API
|
||||
|
||||
## Requirements
|
||||
1. Add `IsForce` boolean field to the `AndroidApkVersion` entity
|
||||
2. Allow administrators to specify force update status when uploading APK
|
||||
3. Store force update status in database
|
||||
4. Return force update status via API endpoint
|
||||
5. Separate handling for WebView and FaceDetection APK types
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### 1. Domain Layer Updates
|
||||
- ✅ Add `IsForce` property to `AndroidApkVersion` entity
|
||||
- ✅ Update constructor to accept `isForce` parameter with default value of `false`
|
||||
- ✅ File: `Company.Domain/AndroidApkVersionAgg/AndroidApkVersion.cs`
|
||||
|
||||
### 2. Database Mapping
|
||||
- ✅ Add `IsForce` property mapping in `AndroidApkVersionMapping`
|
||||
- ✅ File: `CompanyManagment.EFCore/Mapping/AndroidApkVersionMapping.cs`
|
||||
|
||||
### 3. Application Layer Updates
|
||||
- ✅ Update `IAndroidApkVersionApplication` interface:
|
||||
- Add `isForce` parameter to `CreateAndActive` method
|
||||
- Add `isForce` parameter to `CreateAndDeActive` method
|
||||
- Remove `isForceUpdate` parameter from `GetLatestActiveInfo` method
|
||||
- ✅ File: `CompanyManagment.App.Contracts/AndroidApkVersion/IAndroidApkVersionApplication.cs`
|
||||
|
||||
### 4. Application Implementation
|
||||
- ✅ Update `AndroidApkVersionApplication`:
|
||||
- Pass `isForce` to `AndroidApkVersion` constructor in `CreateAndActive`
|
||||
- Pass `isForce` to `AndroidApkVersion` constructor in `CreateAndDeActive`
|
||||
- Update `GetLatestActiveInfo` to return `IsForce` from database entity instead of parameter
|
||||
- ✅ File: `CompanyManagment.Application/AndroidApkVersionApplication.cs`
|
||||
|
||||
### 5. API Controller Updates
|
||||
- ✅ Update `AndroidApkController`:
|
||||
- Remove `force` parameter from `CheckUpdate` endpoint
|
||||
- API now returns `IsForce` from database
|
||||
- ✅ File: `ServiceHost/Areas/Admin/Controllers/AndroidApkController.cs`
|
||||
|
||||
### 6. Admin UI Updates
|
||||
- ✅ Add `IsForce` property to `IndexModel`
|
||||
- ✅ Add checkbox for force update in upload form
|
||||
- ✅ Pass `IsForce` value to `CreateAndActive` method
|
||||
- ✅ Files:
|
||||
- `ServiceHost/Areas/AdminNew/Pages/Company/AndroidApk/Index.cshtml.cs`
|
||||
- `ServiceHost/Areas/AdminNew/Pages/Company/AndroidApk/Index.cshtml`
|
||||
|
||||
### 7. Database Migration (To Be Done)
|
||||
- ⚠️ **REQUIRED**: Create and run migration to add `IsForce` column to `AndroidApkVersions` table
|
||||
- Command: `Add-Migration AddIsForceToAndroidApkVersion`
|
||||
- Then: `Update-Database`
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Check for Updates
|
||||
```http
|
||||
GET /api/android-apk/check-update?type={ApkType}¤tVersionCode={int}
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `type`: Enum value - `WebView` or `FaceDetection`
|
||||
- `currentVersionCode`: Current version code of installed app (integer)
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"latestVersionCode": 120,
|
||||
"latestVersionName": "1.2.0",
|
||||
"shouldUpdate": true,
|
||||
"isForceUpdate": false,
|
||||
"downloadUrl": "/Apk/Android?type=WebView",
|
||||
"releaseNotes": "Bug fixes and improvements"
|
||||
}
|
||||
```
|
||||
|
||||
## APK Type Separation
|
||||
|
||||
The system now fully supports two separate APK types:
|
||||
1. **WebView**: Original web-view based application
|
||||
- Stored in: `Storage/Apk/Android/GozreshgirWebView/`
|
||||
- Title format: `Gozareshgir-WebView-{version}-{date}`
|
||||
|
||||
2. **FaceDetection**: New face detection application
|
||||
- Stored in: `Storage/Apk/Android/GozreshgirFaceDetection/`
|
||||
- Title format: `Gozareshgir-FaceDetection-{version}-{date}`
|
||||
|
||||
Each APK type maintains its own:
|
||||
- Version history
|
||||
- Active version
|
||||
- Force update settings
|
||||
- Download endpoint
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Admin Upload with Force Update
|
||||
1. Navigate to admin APK upload page
|
||||
2. Select APK file
|
||||
3. Choose APK type (WebView or FaceDetection)
|
||||
4. Check "آپدیت اجباری (Force Update)" if update should be mandatory
|
||||
5. Click Upload
|
||||
|
||||
### Client Check for Update (WebView)
|
||||
```http
|
||||
GET /api/android-apk/check-update?type=WebView¤tVersionCode=100
|
||||
```
|
||||
|
||||
### Client Check for Update (FaceDetection)
|
||||
```http
|
||||
GET /api/android-apk/check-update?type=FaceDetection¤tVersionCode=50
|
||||
```
|
||||
|
||||
## Testing Checklist
|
||||
- [ ] Test uploading APK with force update enabled for WebView
|
||||
- [ ] Test uploading APK with force update disabled for WebView
|
||||
- [ ] Test uploading APK with force update enabled for FaceDetection
|
||||
- [ ] Test uploading APK with force update disabled for FaceDetection
|
||||
- [ ] Verify API returns correct `isForceUpdate` value for WebView
|
||||
- [ ] Verify API returns correct `isForceUpdate` value for FaceDetection
|
||||
- [ ] Verify only one active version exists per APK type
|
||||
- [ ] Test migration creates `IsForce` column correctly
|
||||
- [ ] Verify existing records default to `false` for `IsForce`
|
||||
|
||||
## Notes
|
||||
- Default value for `IsForce` is `false` (optional update)
|
||||
- When uploading new active APK, all previous active versions of same type are deactivated
|
||||
- Each APK type is managed independently
|
||||
- Force update flag is stored per version, not globally
|
||||
- API returns force update status from the latest active version in database
|
||||
|
||||
## Files Modified
|
||||
1. `Company.Domain/AndroidApkVersionAgg/AndroidApkVersion.cs`
|
||||
2. `CompanyManagment.EFCore/Mapping/AndroidApkVersionMapping.cs`
|
||||
3. `CompanyManagment.App.Contracts/AndroidApkVersion/IAndroidApkVersionApplication.cs`
|
||||
4. `CompanyManagment.Application/AndroidApkVersionApplication.cs`
|
||||
5. `ServiceHost/Areas/Admin/Controllers/AndroidApkController.cs`
|
||||
6. `ServiceHost/Areas/AdminNew/Pages/Company/AndroidApk/Index.cshtml.cs`
|
||||
7. `ServiceHost/Areas/AdminNew/Pages/Company/AndroidApk/Index.cshtml`
|
||||
|
||||
## Migration Required
|
||||
⚠️ **Important**: Don't forget to create and run the database migration to add the `IsForce` column.
|
||||
|
||||
Reference in New Issue
Block a user