feat(AppVersion): Add version tracking system for frontend cache invalidation
Build and Deploy to Production / build-and-deploy (push) Successful in 1m37s
Build and Deploy to Production / build-and-deploy (push) Successful in 1m37s
- Add AppVersion entity with CurrentVersion, MinRequiredVersion, RequiresFullCacheClear - Add GetAppVersion query to check app version and compare with client version - Add GetAllAppVersions query for admin panel - Add UpdateAppVersion command to update/create app versions - Add appversion.proto for gRPC communication - Add AppVersionService for gRPC endpoints - Add database migration for AppVersions table
This commit is contained in:
+42
@@ -0,0 +1,42 @@
|
||||
namespace CMSMicroservice.Application.AppVersionCQ.Commands.UpdateAppVersion;
|
||||
|
||||
/// <summary>
|
||||
/// Command برای آپدیت یا ایجاد نسخه جدید اپلیکیشن
|
||||
/// </summary>
|
||||
public record UpdateAppVersionCommand : IRequest<Unit>
|
||||
{
|
||||
/// <summary>
|
||||
/// نام اپلیکیشن (FrontOffice, BackOffice, MobileApp)
|
||||
/// </summary>
|
||||
public string AppName { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// شماره نسخه جدید (مثلاً 1.2.3)
|
||||
/// </summary>
|
||||
public string CurrentVersion { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// حداقل نسخه مورد نیاز
|
||||
/// </summary>
|
||||
public string? MinRequiredVersion { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// آیا کش کامل باید پاک بشه؟
|
||||
/// </summary>
|
||||
public bool RequiresFullCacheClear { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// پیام آپدیت
|
||||
/// </summary>
|
||||
public string? UpdateMessage { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// توضیحات تغییرات این نسخه
|
||||
/// </summary>
|
||||
public string? ReleaseNotes { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// دلیل آپدیت (برای لاگ)
|
||||
/// </summary>
|
||||
public string? UpdateReason { get; init; }
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
using CMSMicroservice.Domain.Entities.Configuration;
|
||||
|
||||
namespace CMSMicroservice.Application.AppVersionCQ.Commands.UpdateAppVersion;
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای آپدیت یا ایجاد نسخه جدید اپلیکیشن
|
||||
/// </summary>
|
||||
public class UpdateAppVersionCommandHandler : IRequestHandler<UpdateAppVersionCommand, Unit>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly ILogger<UpdateAppVersionCommandHandler> _logger;
|
||||
|
||||
public UpdateAppVersionCommandHandler(
|
||||
IApplicationDbContext context,
|
||||
ILogger<UpdateAppVersionCommandHandler> logger)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<Unit> Handle(UpdateAppVersionCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// پیدا کردن نسخه موجود برای این اپ
|
||||
var existingVersion = await _context.AppVersions
|
||||
.Where(v => v.AppName == request.AppName && !v.IsDeleted)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (existingVersion != null)
|
||||
{
|
||||
// آپدیت نسخه موجود
|
||||
existingVersion.CurrentVersion = request.CurrentVersion;
|
||||
existingVersion.MinRequiredVersion = request.MinRequiredVersion ?? request.CurrentVersion;
|
||||
existingVersion.RequiresFullCacheClear = request.RequiresFullCacheClear;
|
||||
existingVersion.UpdateMessage = request.UpdateMessage;
|
||||
existingVersion.ReleaseNotes = request.ReleaseNotes;
|
||||
|
||||
_logger.LogInformation(
|
||||
"App version updated: {AppName} v{Version}, CacheClear={CacheClear}, Reason={Reason}",
|
||||
request.AppName, request.CurrentVersion, request.RequiresFullCacheClear, request.UpdateReason);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ایجاد نسخه جدید
|
||||
var newVersion = new AppVersion
|
||||
{
|
||||
AppName = request.AppName,
|
||||
CurrentVersion = request.CurrentVersion,
|
||||
MinRequiredVersion = request.MinRequiredVersion ?? request.CurrentVersion,
|
||||
RequiresFullCacheClear = request.RequiresFullCacheClear,
|
||||
UpdateMessage = request.UpdateMessage,
|
||||
ReleaseNotes = request.ReleaseNotes,
|
||||
IsActive = true
|
||||
};
|
||||
|
||||
_context.AppVersions.Add(newVersion);
|
||||
|
||||
_logger.LogInformation(
|
||||
"New app version created: {AppName} v{Version}, CacheClear={CacheClear}, Reason={Reason}",
|
||||
request.AppName, request.CurrentVersion, request.RequiresFullCacheClear, request.UpdateReason);
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
return Unit.Value;
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
namespace CMSMicroservice.Application.AppVersionCQ.Queries.GetAllAppVersions;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت همه نسخههای اپلیکیشنها
|
||||
/// </summary>
|
||||
public record GetAllAppVersionsQuery(bool IncludeInactive = false) : IRequest<List<AppVersionItemDto>>;
|
||||
|
||||
/// <summary>
|
||||
/// DTO برای هر آیتم نسخه اپلیکیشن
|
||||
/// </summary>
|
||||
public record AppVersionItemDto
|
||||
{
|
||||
public long Id { get; init; }
|
||||
public string AppName { get; init; } = string.Empty;
|
||||
public string CurrentVersion { get; init; } = string.Empty;
|
||||
public string MinRequiredVersion { get; init; } = string.Empty;
|
||||
public bool RequiresFullCacheClear { get; init; }
|
||||
public string? UpdateMessage { get; init; }
|
||||
public string? ReleaseNotes { get; init; }
|
||||
public bool IsActive { get; init; }
|
||||
public DateTime Created { get; init; }
|
||||
public DateTime? LastModified { get; init; }
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
namespace CMSMicroservice.Application.AppVersionCQ.Queries.GetAllAppVersions;
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت همه نسخههای اپلیکیشنها
|
||||
/// </summary>
|
||||
public class GetAllAppVersionsQueryHandler : IRequestHandler<GetAllAppVersionsQuery, List<AppVersionItemDto>>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public GetAllAppVersionsQueryHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<List<AppVersionItemDto>> Handle(GetAllAppVersionsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.AppVersions
|
||||
.Where(v => !v.IsDeleted);
|
||||
|
||||
if (!request.IncludeInactive)
|
||||
{
|
||||
query = query.Where(v => v.IsActive);
|
||||
}
|
||||
|
||||
var versions = await query
|
||||
.OrderBy(v => v.AppName)
|
||||
.Select(v => new AppVersionItemDto
|
||||
{
|
||||
Id = v.Id,
|
||||
AppName = v.AppName,
|
||||
CurrentVersion = v.CurrentVersion,
|
||||
MinRequiredVersion = v.MinRequiredVersion,
|
||||
RequiresFullCacheClear = v.RequiresFullCacheClear,
|
||||
UpdateMessage = v.UpdateMessage,
|
||||
ReleaseNotes = v.ReleaseNotes,
|
||||
IsActive = v.IsActive,
|
||||
Created = v.Created,
|
||||
LastModified = v.LastModified
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return versions;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
namespace CMSMicroservice.Application.AppVersionCQ.Queries.GetAppVersion;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت آخرین نسخه یک اپلیکیشن
|
||||
/// </summary>
|
||||
public record GetAppVersionQuery(string AppName, string? CurrentClientVersion = null) : IRequest<AppVersionDto?>;
|
||||
|
||||
/// <summary>
|
||||
/// DTO برای اطلاعات نسخه اپلیکیشن
|
||||
/// </summary>
|
||||
public record AppVersionDto
|
||||
{
|
||||
public bool Found { get; init; }
|
||||
public string AppName { get; init; } = string.Empty;
|
||||
public string CurrentVersion { get; init; } = string.Empty;
|
||||
public string MinRequiredVersion { get; init; } = string.Empty;
|
||||
public bool RequiresFullCacheClear { get; init; }
|
||||
public bool RequiresUpdate { get; init; }
|
||||
public string? UpdateMessage { get; init; }
|
||||
public string? ReleaseNotes { get; init; }
|
||||
public DateTime? LastUpdated { get; init; }
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
using CMSMicroservice.Domain.Entities.Configuration;
|
||||
|
||||
namespace CMSMicroservice.Application.AppVersionCQ.Queries.GetAppVersion;
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت آخرین نسخه اپلیکیشن
|
||||
/// </summary>
|
||||
public class GetAppVersionQueryHandler : IRequestHandler<GetAppVersionQuery, AppVersionDto?>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public GetAppVersionQueryHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<AppVersionDto?> Handle(GetAppVersionQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var appVersion = await _context.AppVersions
|
||||
.Where(v => v.AppName == request.AppName && v.IsActive && !v.IsDeleted)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (appVersion == null)
|
||||
{
|
||||
return new AppVersionDto
|
||||
{
|
||||
Found = false,
|
||||
AppName = request.AppName
|
||||
};
|
||||
}
|
||||
|
||||
// مقایسه ورژن کلاینت با حداقل نسخه مورد نیاز
|
||||
bool requiresUpdate = false;
|
||||
if (!string.IsNullOrEmpty(request.CurrentClientVersion) && !string.IsNullOrEmpty(appVersion.MinRequiredVersion))
|
||||
{
|
||||
requiresUpdate = CompareVersions(request.CurrentClientVersion, appVersion.MinRequiredVersion) < 0;
|
||||
}
|
||||
|
||||
return new AppVersionDto
|
||||
{
|
||||
Found = true,
|
||||
AppName = appVersion.AppName,
|
||||
CurrentVersion = appVersion.CurrentVersion,
|
||||
MinRequiredVersion = appVersion.MinRequiredVersion,
|
||||
RequiresFullCacheClear = appVersion.RequiresFullCacheClear,
|
||||
RequiresUpdate = requiresUpdate,
|
||||
UpdateMessage = appVersion.UpdateMessage,
|
||||
ReleaseNotes = appVersion.ReleaseNotes,
|
||||
LastUpdated = appVersion.LastModified ?? appVersion.Created
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// مقایسه دو نسخه (مثلاً 1.2.3 با 1.3.0)
|
||||
/// برگشت: منفی = اولی کوچکتر، مثبت = اولی بزرگتر، صفر = برابر
|
||||
/// </summary>
|
||||
private static int CompareVersions(string version1, string version2)
|
||||
{
|
||||
var v1Parts = version1.Split('.').Select(s => int.TryParse(s, out var n) ? n : 0).ToArray();
|
||||
var v2Parts = version2.Split('.').Select(s => int.TryParse(s, out var n) ? n : 0).ToArray();
|
||||
|
||||
var maxLen = Math.Max(v1Parts.Length, v2Parts.Length);
|
||||
|
||||
for (int i = 0; i < maxLen; i++)
|
||||
{
|
||||
var v1 = i < v1Parts.Length ? v1Parts[i] : 0;
|
||||
var v2 = i < v2Parts.Length ? v2Parts[i] : 0;
|
||||
|
||||
if (v1 != v2)
|
||||
return v1.CompareTo(v2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ public interface IApplicationDbContext
|
||||
DbSet<CommissionPayoutHistory> CommissionPayoutHistories { get; }
|
||||
DbSet<WorkerExecutionLog> WorkerExecutionLogs { get; }
|
||||
DbSet<DayaLoanContract> DayaLoanContracts { get; }
|
||||
DbSet<AppVersion> AppVersions { get; }
|
||||
|
||||
// ============= Discount Shop =============
|
||||
DbSet<DiscountProduct> DiscountProducts { get; }
|
||||
|
||||
Reference in New Issue
Block a user