feat: Add SitePageSettings - simplified page management system
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m2s
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m2s
- New entities: SitePageSettings + SitePageImage (4 fixed pages) - EF Migration: AddSitePageSettings (SitePageSettings + SitePageImages tables) - Proto: sitepagesettings.proto with 5 RPCs - CQRS: GetPageSettings, GetAllPageSettings, SavePageSettings, SavePageImage, DeletePageImage - gRPC Service: SitePageSettingsService (auto-registered) - Proto NuGet bumped to 0.0.180
This commit is contained in:
@@ -80,6 +80,10 @@ public interface IApplicationDbContext
|
|||||||
DbSet<SitePage> SitePages { get; }
|
DbSet<SitePage> SitePages { get; }
|
||||||
DbSet<SitePageSection> SitePageSections { get; }
|
DbSet<SitePageSection> SitePageSections { get; }
|
||||||
|
|
||||||
|
// ============= Site Page Settings (Simplified) =============
|
||||||
|
DbSet<SitePageSettings> SitePageSettingsEntities { get; }
|
||||||
|
DbSet<SitePageImage> SitePageImages { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// دسترسی به DatabaseFacade برای اجرای raw SQL و Stored Procedures
|
/// دسترسی به DatabaseFacade برای اجرای raw SQL و Stored Procedures
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
using CMSMicroservice.Application.Common.Interfaces;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Application.SitePageSettingsCQ.Commands.DeletePageImage;
|
||||||
|
|
||||||
|
// ── Command ──
|
||||||
|
public class DeletePageImageCommand : IRequest<Unit>
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Handler ──
|
||||||
|
public class DeletePageImageCommandHandler : IRequestHandler<DeletePageImageCommand, Unit>
|
||||||
|
{
|
||||||
|
private readonly IApplicationDbContext _context;
|
||||||
|
|
||||||
|
public DeletePageImageCommandHandler(IApplicationDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Unit> Handle(DeletePageImageCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var entity = await _context.SitePageImages
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == request.Id && !x.IsDeleted, cancellationToken);
|
||||||
|
|
||||||
|
if (entity != null)
|
||||||
|
{
|
||||||
|
entity.IsDeleted = true; // Soft delete
|
||||||
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Unit.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
+96
@@ -0,0 +1,96 @@
|
|||||||
|
using CMSMicroservice.Application.Common.FileManager;
|
||||||
|
using CMSMicroservice.Application.Common.Interfaces;
|
||||||
|
using CMSMicroservice.Domain.Entities.Content;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Application.SitePageSettingsCQ.Commands.SavePageImage;
|
||||||
|
|
||||||
|
// ── Command ──
|
||||||
|
public class SavePageImageCommand : IRequest<SavePageImageResult>
|
||||||
|
{
|
||||||
|
public long Id { get; set; } // 0 = ایجاد جدید
|
||||||
|
public long SitePageSettingsId { get; set; }
|
||||||
|
public string ImageGroup { get; set; } = default!;
|
||||||
|
public string? Title { get; set; }
|
||||||
|
public string? Subtitle { get; set; }
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public string? LinkUrl { get; set; }
|
||||||
|
public string? IconName { get; set; }
|
||||||
|
public int SortOrder { get; set; }
|
||||||
|
public bool IsActive { get; set; } = true;
|
||||||
|
|
||||||
|
// Image upload
|
||||||
|
public byte[]? ImageFileBytes { get; set; }
|
||||||
|
public string? ImageFileMime { get; set; }
|
||||||
|
public string? ImageFileName { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SavePageImageResult
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
public bool Success { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Handler ──
|
||||||
|
public class SavePageImageCommandHandler : IRequestHandler<SavePageImageCommand, SavePageImageResult>
|
||||||
|
{
|
||||||
|
private readonly IApplicationDbContext _context;
|
||||||
|
private readonly IFileManager _fileManager;
|
||||||
|
|
||||||
|
public SavePageImageCommandHandler(IApplicationDbContext context, IFileManager fileManager)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_fileManager = fileManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SavePageImageResult> Handle(SavePageImageCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
SitePageImage entity;
|
||||||
|
|
||||||
|
if (request.Id > 0)
|
||||||
|
{
|
||||||
|
// ویرایش
|
||||||
|
entity = await _context.SitePageImages
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == request.Id && !x.IsDeleted, cancellationToken)
|
||||||
|
?? throw new Exception($"تصویر با شناسه {request.Id} یافت نشد");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ایجاد جدید
|
||||||
|
entity = new SitePageImage
|
||||||
|
{
|
||||||
|
SitePageSettingsId = request.SitePageSettingsId,
|
||||||
|
ImageGroup = request.ImageGroup,
|
||||||
|
ImagePath = string.Empty // placeholder — will be set below
|
||||||
|
};
|
||||||
|
_context.SitePageImages.Add(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.Title = request.Title;
|
||||||
|
entity.Subtitle = request.Subtitle;
|
||||||
|
entity.Description = request.Description;
|
||||||
|
entity.LinkUrl = request.LinkUrl;
|
||||||
|
entity.IconName = request.IconName;
|
||||||
|
entity.SortOrder = request.SortOrder;
|
||||||
|
entity.IsActive = request.IsActive;
|
||||||
|
|
||||||
|
// آپلود تصویر
|
||||||
|
if (request.ImageFileBytes is { Length: > 0 })
|
||||||
|
{
|
||||||
|
var result = await _fileManager.UploadImageAsync(
|
||||||
|
$"Images/SitePageSettings/{request.ImageGroup}",
|
||||||
|
request.ImageFileBytes,
|
||||||
|
request.ImageFileMime ?? "image/jpeg",
|
||||||
|
request.ImageFileName,
|
||||||
|
cancellationToken);
|
||||||
|
|
||||||
|
entity.ImagePath = result.Main.Path;
|
||||||
|
entity.ThumbnailPath = result.Thumbnail.Path;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
return new SavePageImageResult { Id = entity.Id, Success = true };
|
||||||
|
}
|
||||||
|
}
|
||||||
+83
@@ -0,0 +1,83 @@
|
|||||||
|
using CMSMicroservice.Application.Common.FileManager;
|
||||||
|
using CMSMicroservice.Application.Common.Interfaces;
|
||||||
|
using CMSMicroservice.Domain.Entities.Content;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Application.SitePageSettingsCQ.Commands.SavePageSettings;
|
||||||
|
|
||||||
|
// ── Command ──
|
||||||
|
public class SavePageSettingsCommand : IRequest<SavePageSettingsResult>
|
||||||
|
{
|
||||||
|
public string PageKey { get; set; } = default!;
|
||||||
|
public string Title { get; set; } = default!;
|
||||||
|
public string? MetaDescription { get; set; }
|
||||||
|
public string? HeroTitle { get; set; }
|
||||||
|
public string? HeroSubtitle { get; set; }
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
public string? SettingsJson { get; set; }
|
||||||
|
|
||||||
|
// Hero image upload
|
||||||
|
public byte[]? ImageFileBytes { get; set; }
|
||||||
|
public string? ImageFileMime { get; set; }
|
||||||
|
public string? ImageFileName { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SavePageSettingsResult
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
public bool Success { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Handler ──
|
||||||
|
public class SavePageSettingsCommandHandler : IRequestHandler<SavePageSettingsCommand, SavePageSettingsResult>
|
||||||
|
{
|
||||||
|
private readonly IApplicationDbContext _context;
|
||||||
|
private readonly IFileManager _fileManager;
|
||||||
|
|
||||||
|
public SavePageSettingsCommandHandler(IApplicationDbContext context, IFileManager fileManager)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_fileManager = fileManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SavePageSettingsResult> Handle(SavePageSettingsCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var entity = await _context.SitePageSettingsEntities
|
||||||
|
.FirstOrDefaultAsync(x => x.PageKey == request.PageKey && !x.IsDeleted, cancellationToken);
|
||||||
|
|
||||||
|
if (entity == null)
|
||||||
|
{
|
||||||
|
// ایجاد صفحه جدید (فقط برای seed اولیه — صفحات ثابت هستند)
|
||||||
|
entity = new SitePageSettings
|
||||||
|
{
|
||||||
|
PageKey = request.PageKey,
|
||||||
|
};
|
||||||
|
_context.SitePageSettingsEntities.Add(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.Title = request.Title;
|
||||||
|
entity.MetaDescription = request.MetaDescription;
|
||||||
|
entity.HeroTitle = request.HeroTitle;
|
||||||
|
entity.HeroSubtitle = request.HeroSubtitle;
|
||||||
|
entity.IsActive = request.IsActive;
|
||||||
|
entity.SettingsJson = request.SettingsJson;
|
||||||
|
|
||||||
|
// آپلود تصویر هیرو
|
||||||
|
if (request.ImageFileBytes is { Length: > 0 })
|
||||||
|
{
|
||||||
|
var result = await _fileManager.UploadImageAsync(
|
||||||
|
"Images/SitePageSettings",
|
||||||
|
request.ImageFileBytes,
|
||||||
|
request.ImageFileMime ?? "image/jpeg",
|
||||||
|
request.ImageFileName,
|
||||||
|
cancellationToken);
|
||||||
|
|
||||||
|
entity.HeroImagePath = result.Main.Path;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
return new SavePageSettingsResult { Id = entity.Id, Success = true };
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Application.SitePageSettingsCQ.Commands.SavePageSettings;
|
||||||
|
|
||||||
|
public class SavePageSettingsCommandValidator : AbstractValidator<SavePageSettingsCommand>
|
||||||
|
{
|
||||||
|
private static readonly string[] ValidPageKeys = { "landing", "about", "contact", "licenses" };
|
||||||
|
|
||||||
|
public SavePageSettingsCommandValidator()
|
||||||
|
{
|
||||||
|
RuleFor(x => x.PageKey)
|
||||||
|
.NotEmpty().WithMessage("کلید صفحه الزامی است")
|
||||||
|
.MaximumLength(50)
|
||||||
|
.Must(key => ValidPageKeys.Contains(key))
|
||||||
|
.WithMessage("کلید صفحه باید یکی از مقادیر landing, about, contact, licenses باشد");
|
||||||
|
|
||||||
|
RuleFor(x => x.Title)
|
||||||
|
.NotEmpty().WithMessage("عنوان صفحه الزامی است")
|
||||||
|
.MaximumLength(200).WithMessage("عنوان صفحه حداکثر ۲۰۰ کاراکتر");
|
||||||
|
|
||||||
|
RuleFor(x => x.MetaDescription)
|
||||||
|
.MaximumLength(300).WithMessage("توضیحات متا حداکثر ۳۰۰ کاراکتر");
|
||||||
|
|
||||||
|
RuleFor(x => x.HeroTitle)
|
||||||
|
.MaximumLength(200).WithMessage("عنوان هیرو حداکثر ۲۰۰ کاراکتر");
|
||||||
|
|
||||||
|
RuleFor(x => x.HeroSubtitle)
|
||||||
|
.MaximumLength(500).WithMessage("زیرعنوان هیرو حداکثر ۵۰۰ کاراکتر");
|
||||||
|
}
|
||||||
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
using CMSMicroservice.Application.Common.Interfaces;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Application.SitePageSettingsCQ.Queries.GetAllPageSettings;
|
||||||
|
|
||||||
|
// ── Query ──
|
||||||
|
public class GetAllPageSettingsQuery : IRequest<List<PageSettingsSummaryDto>>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Handler ──
|
||||||
|
public class GetAllPageSettingsQueryHandler : IRequestHandler<GetAllPageSettingsQuery, List<PageSettingsSummaryDto>>
|
||||||
|
{
|
||||||
|
private readonly IApplicationDbContext _context;
|
||||||
|
|
||||||
|
public GetAllPageSettingsQueryHandler(IApplicationDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<PageSettingsSummaryDto>> Handle(GetAllPageSettingsQuery request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return await _context.SitePageSettingsEntities
|
||||||
|
.Where(x => !x.IsDeleted)
|
||||||
|
.OrderBy(x => x.Id)
|
||||||
|
.Select(x => new PageSettingsSummaryDto
|
||||||
|
{
|
||||||
|
Id = x.Id,
|
||||||
|
PageKey = x.PageKey,
|
||||||
|
Title = x.Title,
|
||||||
|
IsActive = x.IsActive,
|
||||||
|
ImageCount = x.Images.Count(i => !i.IsDeleted),
|
||||||
|
LastModified = x.LastModified ?? x.Created
|
||||||
|
})
|
||||||
|
.ToListAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── DTO ──
|
||||||
|
public class PageSettingsSummaryDto
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
public string PageKey { get; set; } = string.Empty;
|
||||||
|
public string Title { get; set; } = string.Empty;
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
public int ImageCount { get; set; }
|
||||||
|
public DateTime LastModified { get; set; }
|
||||||
|
}
|
||||||
+92
@@ -0,0 +1,92 @@
|
|||||||
|
using CMSMicroservice.Application.Common.Interfaces;
|
||||||
|
using CMSMicroservice.Domain.Entities.Content;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Application.SitePageSettingsCQ.Queries.GetPageSettings;
|
||||||
|
|
||||||
|
// ── Query ──
|
||||||
|
public class GetPageSettingsQuery : IRequest<PageSettingsDto?>
|
||||||
|
{
|
||||||
|
public string PageKey { get; set; } = default!;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Handler ──
|
||||||
|
public class GetPageSettingsQueryHandler : IRequestHandler<GetPageSettingsQuery, PageSettingsDto?>
|
||||||
|
{
|
||||||
|
private readonly IApplicationDbContext _context;
|
||||||
|
|
||||||
|
public GetPageSettingsQueryHandler(IApplicationDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PageSettingsDto?> Handle(GetPageSettingsQuery request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var entity = await _context.SitePageSettingsEntities
|
||||||
|
.Include(x => x.Images.Where(i => !i.IsDeleted && i.IsActive).OrderBy(i => i.SortOrder))
|
||||||
|
.FirstOrDefaultAsync(x => x.PageKey == request.PageKey && !x.IsDeleted, cancellationToken);
|
||||||
|
|
||||||
|
if (entity == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return MapToDto(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PageSettingsDto MapToDto(SitePageSettings entity) => new()
|
||||||
|
{
|
||||||
|
Id = entity.Id,
|
||||||
|
PageKey = entity.PageKey,
|
||||||
|
Title = entity.Title,
|
||||||
|
MetaDescription = entity.MetaDescription,
|
||||||
|
HeroTitle = entity.HeroTitle,
|
||||||
|
HeroSubtitle = entity.HeroSubtitle,
|
||||||
|
HeroImagePath = entity.HeroImagePath,
|
||||||
|
IsActive = entity.IsActive,
|
||||||
|
SettingsJson = entity.SettingsJson,
|
||||||
|
Images = entity.Images.Select(i => new PageImageDto
|
||||||
|
{
|
||||||
|
Id = i.Id,
|
||||||
|
ImageGroup = i.ImageGroup,
|
||||||
|
Title = i.Title,
|
||||||
|
Subtitle = i.Subtitle,
|
||||||
|
Description = i.Description,
|
||||||
|
ImagePath = i.ImagePath,
|
||||||
|
ThumbnailPath = i.ThumbnailPath,
|
||||||
|
LinkUrl = i.LinkUrl,
|
||||||
|
IconName = i.IconName,
|
||||||
|
SortOrder = i.SortOrder,
|
||||||
|
IsActive = i.IsActive
|
||||||
|
}).ToList()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── DTOs ──
|
||||||
|
public class PageSettingsDto
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
public string PageKey { get; set; } = string.Empty;
|
||||||
|
public string Title { get; set; } = string.Empty;
|
||||||
|
public string? MetaDescription { get; set; }
|
||||||
|
public string? HeroTitle { get; set; }
|
||||||
|
public string? HeroSubtitle { get; set; }
|
||||||
|
public string? HeroImagePath { get; set; }
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
public string? SettingsJson { get; set; }
|
||||||
|
public List<PageImageDto> Images { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PageImageDto
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
public string ImageGroup { get; set; } = string.Empty;
|
||||||
|
public string? Title { get; set; }
|
||||||
|
public string? Subtitle { get; set; }
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public string ImagePath { get; set; } = string.Empty;
|
||||||
|
public string? ThumbnailPath { get; set; }
|
||||||
|
public string? LinkUrl { get; set; }
|
||||||
|
public string? IconName { get; set; }
|
||||||
|
public int SortOrder { get; set; }
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
using CMSMicroservice.Domain.Common;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Domain.Entities.Content;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// تصاویر مرتبط با صفحات سایت
|
||||||
|
/// شامل: مجوزها، اعضای تیم، ارزشها و سایر آیتمهای تصویری
|
||||||
|
/// </summary>
|
||||||
|
public class SitePageImage : BaseAuditableEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه صفحه مرتبط
|
||||||
|
/// </summary>
|
||||||
|
public long SitePageSettingsId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// گروه تصویر (licenses, team, values)
|
||||||
|
/// </summary>
|
||||||
|
public string ImageGroup { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// عنوان
|
||||||
|
/// </summary>
|
||||||
|
public string? Title { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// زیرعنوان
|
||||||
|
/// </summary>
|
||||||
|
public string? Subtitle { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// توضیحات
|
||||||
|
/// </summary>
|
||||||
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// مسیر تصویر اصلی
|
||||||
|
/// </summary>
|
||||||
|
public string ImagePath { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// مسیر تامبنیل
|
||||||
|
/// </summary>
|
||||||
|
public string? ThumbnailPath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// لینک خارجی (برای مجوزها: لینک به سایت مرجع)
|
||||||
|
/// </summary>
|
||||||
|
public string? LinkUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// نام آیکون Material
|
||||||
|
/// </summary>
|
||||||
|
public string? IconName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ترتیب نمایش
|
||||||
|
/// </summary>
|
||||||
|
public int SortOrder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// فعال بودن
|
||||||
|
/// </summary>
|
||||||
|
public bool IsActive { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// صفحه مرتبط
|
||||||
|
/// </summary>
|
||||||
|
public virtual SitePageSettings SitePageSettings { get; set; } = null!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
using CMSMicroservice.Domain.Common;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Domain.Entities.Content;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// تنظیمات صفحات سایت — هر صفحه یک رکورد با تنظیمات JSON تایپشده
|
||||||
|
/// صفحات ثابت: landing, about, contact, licenses
|
||||||
|
/// </summary>
|
||||||
|
public class SitePageSettings : BaseAuditableEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// کلید یکتای صفحه (landing, about, contact, licenses)
|
||||||
|
/// </summary>
|
||||||
|
public string PageKey { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// عنوان صفحه
|
||||||
|
/// </summary>
|
||||||
|
public string Title { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// توضیح متا برای SEO
|
||||||
|
/// </summary>
|
||||||
|
public string? MetaDescription { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// عنوان بخش Hero
|
||||||
|
/// </summary>
|
||||||
|
public string? HeroTitle { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// زیرعنوان بخش Hero
|
||||||
|
/// </summary>
|
||||||
|
public string? HeroSubtitle { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// مسیر تصویر Hero
|
||||||
|
/// </summary>
|
||||||
|
public string? HeroImagePath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// فعال بودن صفحه
|
||||||
|
/// </summary>
|
||||||
|
public bool IsActive { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// تنظیمات اختصاصی صفحه به فرمت JSON
|
||||||
|
/// ساختار JSON بر اساس PageKey متفاوت است
|
||||||
|
/// </summary>
|
||||||
|
public string? SettingsJson { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// تصاویر مرتبط با صفحه (مجوزها، تیم، ارزشها)
|
||||||
|
/// </summary>
|
||||||
|
public virtual ICollection<SitePageImage> Images { get; set; } = new List<SitePageImage>();
|
||||||
|
}
|
||||||
@@ -152,4 +152,8 @@ public class ApplicationDbContext : DbContext, IApplicationDbContext
|
|||||||
// ============= Content Management DbSets =============
|
// ============= Content Management DbSets =============
|
||||||
public DbSet<SitePage> SitePages => Set<SitePage>();
|
public DbSet<SitePage> SitePages => Set<SitePage>();
|
||||||
public DbSet<SitePageSection> SitePageSections => Set<SitePageSection>();
|
public DbSet<SitePageSection> SitePageSections => Set<SitePageSection>();
|
||||||
|
|
||||||
|
// ============= Site Page Settings (Simplified) =============
|
||||||
|
public DbSet<SitePageSettings> SitePageSettingsEntities => Set<SitePageSettings>();
|
||||||
|
public DbSet<SitePageImage> SitePageImages => Set<SitePageImage>();
|
||||||
}
|
}
|
||||||
|
|||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
using CMSMicroservice.Domain.Entities.Content;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
|
||||||
|
|
||||||
|
public class SitePageImageConfiguration : IEntityTypeConfiguration<SitePageImage>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<SitePageImage> builder)
|
||||||
|
{
|
||||||
|
builder.ToTable("SitePageImages");
|
||||||
|
builder.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
builder.Property(x => x.SitePageSettingsId)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
builder.Property(x => x.ImageGroup)
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
builder.Property(x => x.Title)
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
builder.Property(x => x.Subtitle)
|
||||||
|
.HasMaxLength(300);
|
||||||
|
|
||||||
|
builder.Property(x => x.ImagePath)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
builder.Property(x => x.IconName)
|
||||||
|
.HasMaxLength(100);
|
||||||
|
|
||||||
|
builder.Property(x => x.LinkUrl)
|
||||||
|
.HasMaxLength(500);
|
||||||
|
|
||||||
|
builder.Property(x => x.SortOrder)
|
||||||
|
.IsRequired()
|
||||||
|
.HasDefaultValue(0);
|
||||||
|
|
||||||
|
builder.Property(x => x.IsActive)
|
||||||
|
.IsRequired()
|
||||||
|
.HasDefaultValue(true);
|
||||||
|
|
||||||
|
// رابطه با SitePageSettings
|
||||||
|
builder.HasOne(x => x.SitePageSettings)
|
||||||
|
.WithMany(x => x.Images)
|
||||||
|
.HasForeignKey(x => x.SitePageSettingsId)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
// ایندکس ترکیبی
|
||||||
|
builder.HasIndex(x => new { x.SitePageSettingsId, x.ImageGroup })
|
||||||
|
.HasDatabaseName("IX_SitePageImages_SettingsId_Group");
|
||||||
|
}
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
using CMSMicroservice.Domain.Entities.Content;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
|
||||||
|
|
||||||
|
public class SitePageSettingsConfiguration : IEntityTypeConfiguration<SitePageSettings>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<SitePageSettings> builder)
|
||||||
|
{
|
||||||
|
builder.ToTable("SitePageSettings");
|
||||||
|
builder.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
builder.Property(x => x.PageKey)
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
builder.Property(x => x.Title)
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
builder.Property(x => x.MetaDescription)
|
||||||
|
.HasMaxLength(300);
|
||||||
|
|
||||||
|
builder.Property(x => x.HeroTitle)
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
builder.Property(x => x.HeroSubtitle)
|
||||||
|
.HasMaxLength(500);
|
||||||
|
|
||||||
|
builder.Property(x => x.IsActive)
|
||||||
|
.IsRequired()
|
||||||
|
.HasDefaultValue(true);
|
||||||
|
|
||||||
|
// SettingsJson — ستون JSON بدون محدودیت طول
|
||||||
|
builder.Property(x => x.SettingsJson)
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
// ایندکس یکتا روی PageKey
|
||||||
|
builder.HasIndex(x => x.PageKey)
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("IX_SitePageSettings_PageKey");
|
||||||
|
}
|
||||||
|
}
|
||||||
+4674
File diff suppressed because it is too large
Load Diff
+102
@@ -0,0 +1,102 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddSitePageSettings : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "SitePageSettings",
|
||||||
|
schema: "CMS",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
.Annotation("SqlServer:Identity", "1, 1"),
|
||||||
|
PageKey = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
|
||||||
|
Title = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
|
||||||
|
MetaDescription = table.Column<string>(type: "nvarchar(300)", maxLength: 300, nullable: true),
|
||||||
|
HeroTitle = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
|
||||||
|
HeroSubtitle = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||||
|
HeroImagePath = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
IsActive = table.Column<bool>(type: "bit", nullable: false, defaultValue: true),
|
||||||
|
SettingsJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
CreatedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
LastModified = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
LastModifiedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
IsDeleted = table.Column<bool>(type: "bit", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_SitePageSettings", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "SitePageImages",
|
||||||
|
schema: "CMS",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
.Annotation("SqlServer:Identity", "1, 1"),
|
||||||
|
SitePageSettingsId = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
ImageGroup = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
|
||||||
|
Title = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
|
||||||
|
Subtitle = table.Column<string>(type: "nvarchar(300)", maxLength: 300, nullable: true),
|
||||||
|
Description = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
ImagePath = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||||
|
ThumbnailPath = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
LinkUrl = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||||
|
IconName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
|
||||||
|
SortOrder = table.Column<int>(type: "int", nullable: false, defaultValue: 0),
|
||||||
|
IsActive = table.Column<bool>(type: "bit", nullable: false, defaultValue: true),
|
||||||
|
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
CreatedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
LastModified = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
LastModifiedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
IsDeleted = table.Column<bool>(type: "bit", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_SitePageImages", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_SitePageImages_SitePageSettings_SitePageSettingsId",
|
||||||
|
column: x => x.SitePageSettingsId,
|
||||||
|
principalSchema: "CMS",
|
||||||
|
principalTable: "SitePageSettings",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_SitePageImages_SettingsId_Group",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "SitePageImages",
|
||||||
|
columns: new[] { "SitePageSettingsId", "ImageGroup" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_SitePageSettings_PageKey",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "SitePageSettings",
|
||||||
|
column: "PageKey",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "SitePageImages",
|
||||||
|
schema: "CMS");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "SitePageSettings",
|
||||||
|
schema: "CMS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+156
@@ -826,6 +826,81 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.ToTable("SitePages", "CMS");
|
b.ToTable("SitePages", "CMS");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageImage", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("Created")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedBy")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("IconName")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)");
|
||||||
|
|
||||||
|
b.Property<string>("ImageGroup")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("ImagePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsActive")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasDefaultValue(true);
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastModified")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("LastModifiedBy")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("LinkUrl")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<long>("SitePageSettingsId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<int>("SortOrder")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasDefaultValue(0);
|
||||||
|
|
||||||
|
b.Property<string>("Subtitle")
|
||||||
|
.HasMaxLength(300)
|
||||||
|
.HasColumnType("nvarchar(300)");
|
||||||
|
|
||||||
|
b.Property<string>("ThumbnailPath")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("SitePageSettingsId", "ImageGroup")
|
||||||
|
.HasDatabaseName("IX_SitePageImages_SettingsId_Group");
|
||||||
|
|
||||||
|
b.ToTable("SitePageImages", "CMS");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageSection", b =>
|
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageSection", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
@@ -900,6 +975,71 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.ToTable("SitePageSections", "CMS");
|
b.ToTable("SitePageSections", "CMS");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageSettings", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("Created")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedBy")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("HeroImagePath")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("HeroSubtitle")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<string>("HeroTitle")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsActive")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasDefaultValue(true);
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastModified")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("LastModifiedBy")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("MetaDescription")
|
||||||
|
.HasMaxLength(300)
|
||||||
|
.HasColumnType("nvarchar(300)");
|
||||||
|
|
||||||
|
b.Property<string>("PageKey")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("SettingsJson")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PageKey")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("IX_SitePageSettings_PageKey");
|
||||||
|
|
||||||
|
b.ToTable("SitePageSettings", "CMS");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Contract", b =>
|
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Contract", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
@@ -3807,6 +3947,17 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.Navigation("WeekDefinition");
|
b.Navigation("WeekDefinition");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageImage", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("CMSMicroservice.Domain.Entities.Content.SitePageSettings", "SitePageSettings")
|
||||||
|
.WithMany("Images")
|
||||||
|
.HasForeignKey("SitePageSettingsId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("SitePageSettings");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageSection", b =>
|
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageSection", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("CMSMicroservice.Domain.Entities.Content.SitePage", "SitePage")
|
b.HasOne("CMSMicroservice.Domain.Entities.Content.SitePage", "SitePage")
|
||||||
@@ -4363,6 +4514,11 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.Navigation("Sections");
|
b.Navigation("Sections");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageSettings", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Images");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Contract", b =>
|
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Contract", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("UserContracts");
|
b.Navigation("UserContracts");
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<Version>0.0.179</Version>
|
<Version>0.0.180</Version>
|
||||||
<DebugType>None</DebugType>
|
<DebugType>None</DebugType>
|
||||||
<DebugSymbols>False</DebugSymbols>
|
<DebugSymbols>False</DebugSymbols>
|
||||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||||
@@ -71,6 +71,7 @@
|
|||||||
<Protobuf Include="Protos\blogcategory.proto" ProtoRoot="Protos\" GrpcServices="Both" />
|
<Protobuf Include="Protos\blogcategory.proto" ProtoRoot="Protos\" GrpcServices="Both" />
|
||||||
<Protobuf Include="Protos\blogpostimage.proto" ProtoRoot="Protos\" GrpcServices="Both" />
|
<Protobuf Include="Protos\blogpostimage.proto" ProtoRoot="Protos\" GrpcServices="Both" />
|
||||||
<Protobuf Include="Protos\sitepage.proto" ProtoRoot="Protos\" GrpcServices="Both" />
|
<Protobuf Include="Protos\sitepage.proto" ProtoRoot="Protos\" GrpcServices="Both" />
|
||||||
|
<Protobuf Include="Protos\sitepagesettings.proto" ProtoRoot="Protos\" GrpcServices="Both" />
|
||||||
<!-- Image Resolver Service -->
|
<!-- Image Resolver Service -->
|
||||||
<Protobuf Include="Protos\imageresolver.proto" ProtoRoot="Protos\" GrpcServices="Both" />
|
<Protobuf Include="Protos\imageresolver.proto" ProtoRoot="Protos\" GrpcServices="Both" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package sitepagesettings;
|
||||||
|
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
import "google/protobuf/wrappers.proto";
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
import "google/api/annotations.proto";
|
||||||
|
|
||||||
|
option csharp_namespace = "CMSMicroservice.Protobuf.Protos.SitePageSettings";
|
||||||
|
|
||||||
|
// ══════════════════════════════════════════════════════════════
|
||||||
|
// سرویس مدیریت صفحات سایت (نسخه سادهشده)
|
||||||
|
// ۴ صفحه ثابت: landing, about, contact, licenses
|
||||||
|
// ══════════════════════════════════════════════════════════════
|
||||||
|
service SitePageSettingsContract
|
||||||
|
{
|
||||||
|
// دریافت تنظیمات صفحه با کلید — مورد استفاده FrontOffice
|
||||||
|
rpc GetPageSettings(GetPageSettingsRequest) returns (PageSettingsResponse){
|
||||||
|
option (google.api.http) = { get: "/api/page-settings/{page_key}" };
|
||||||
|
};
|
||||||
|
|
||||||
|
// دریافت لیست همه صفحات — مورد استفاده BackOffice
|
||||||
|
rpc GetAllPageSettings(GetAllPageSettingsRequest) returns (GetAllPageSettingsResponse){
|
||||||
|
option (google.api.http) = { get: "/api/page-settings" };
|
||||||
|
};
|
||||||
|
|
||||||
|
// ذخیره تنظیمات صفحه — مورد استفاده BackOffice
|
||||||
|
rpc SavePageSettings(SavePageSettingsRequest) returns (SavePageSettingsResponse){
|
||||||
|
option (google.api.http) = { put: "/api/page-settings" body: "*" };
|
||||||
|
};
|
||||||
|
|
||||||
|
// ذخیره تصویر صفحه (مجوزها، تیم، ارزشها) — مورد استفاده BackOffice
|
||||||
|
rpc SavePageImage(SavePageImageRequest) returns (SavePageImageResponse){
|
||||||
|
option (google.api.http) = { put: "/api/page-settings/images" body: "*" };
|
||||||
|
};
|
||||||
|
|
||||||
|
// حذف تصویر صفحه — مورد استفاده BackOffice
|
||||||
|
rpc DeletePageImage(DeletePageImageRequest) returns (google.protobuf.Empty){
|
||||||
|
option (google.api.http) = { delete: "/api/page-settings/images/{id}" };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ══════════════════════════════════════════════════════════════
|
||||||
|
// Get Page Settings
|
||||||
|
// ══════════════════════════════════════════════════════════════
|
||||||
|
message GetPageSettingsRequest
|
||||||
|
{
|
||||||
|
string page_key = 1; // "landing" | "about" | "contact" | "licenses"
|
||||||
|
}
|
||||||
|
|
||||||
|
message PageSettingsResponse
|
||||||
|
{
|
||||||
|
int64 id = 1;
|
||||||
|
string page_key = 2;
|
||||||
|
string title = 3;
|
||||||
|
google.protobuf.StringValue meta_description = 4;
|
||||||
|
google.protobuf.StringValue hero_title = 5;
|
||||||
|
google.protobuf.StringValue hero_subtitle = 6;
|
||||||
|
google.protobuf.StringValue hero_image_path = 7;
|
||||||
|
bool is_active = 8;
|
||||||
|
string settings_json = 9; // JSON تنظیمات — ساختار بر اساس page_key متفاوت
|
||||||
|
repeated PageImageItem images = 10; // تصاویر مرتبط (مجوزها، تیم، ارزشها)
|
||||||
|
}
|
||||||
|
|
||||||
|
message PageImageItem
|
||||||
|
{
|
||||||
|
int64 id = 1;
|
||||||
|
string image_group = 2; // "licenses" | "team" | "values"
|
||||||
|
google.protobuf.StringValue title = 3;
|
||||||
|
google.protobuf.StringValue subtitle = 4;
|
||||||
|
google.protobuf.StringValue description = 5;
|
||||||
|
string image_path = 6;
|
||||||
|
google.protobuf.StringValue thumbnail_path = 7;
|
||||||
|
google.protobuf.StringValue link_url = 8;
|
||||||
|
google.protobuf.StringValue icon_name = 9;
|
||||||
|
int32 sort_order = 10;
|
||||||
|
bool is_active = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ══════════════════════════════════════════════════════════════
|
||||||
|
// Get All Page Settings (Summary)
|
||||||
|
// ══════════════════════════════════════════════════════════════
|
||||||
|
message GetAllPageSettingsRequest {}
|
||||||
|
|
||||||
|
message GetAllPageSettingsResponse
|
||||||
|
{
|
||||||
|
repeated PageSettingsSummary pages = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PageSettingsSummary
|
||||||
|
{
|
||||||
|
int64 id = 1;
|
||||||
|
string page_key = 2;
|
||||||
|
string title = 3;
|
||||||
|
bool is_active = 4;
|
||||||
|
int32 image_count = 5;
|
||||||
|
google.protobuf.Timestamp last_modified = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ══════════════════════════════════════════════════════════════
|
||||||
|
// Save Page Settings
|
||||||
|
// ══════════════════════════════════════════════════════════════
|
||||||
|
message SavePageSettingsRequest
|
||||||
|
{
|
||||||
|
string page_key = 1; // کلید صفحه (نمیتونه تغییر کنه)
|
||||||
|
string title = 2;
|
||||||
|
google.protobuf.StringValue meta_description = 3;
|
||||||
|
google.protobuf.StringValue hero_title = 4;
|
||||||
|
google.protobuf.StringValue hero_subtitle = 5;
|
||||||
|
bool is_active = 6;
|
||||||
|
string settings_json = 7; // JSON تنظیمات
|
||||||
|
PageSettingsImageFile hero_image_file = 8; // آپلود تصویر Hero (اختیاری)
|
||||||
|
}
|
||||||
|
|
||||||
|
message SavePageSettingsResponse
|
||||||
|
{
|
||||||
|
int64 id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ══════════════════════════════════════════════════════════════
|
||||||
|
// Save Page Image (Add/Update)
|
||||||
|
// ══════════════════════════════════════════════════════════════
|
||||||
|
message SavePageImageRequest
|
||||||
|
{
|
||||||
|
int64 id = 1; // 0 = ایجاد جدید، > 0 = ویرایش
|
||||||
|
int64 site_page_settings_id = 2; // شناسه صفحه
|
||||||
|
string image_group = 3; // "licenses" | "team" | "values"
|
||||||
|
google.protobuf.StringValue title = 4;
|
||||||
|
google.protobuf.StringValue subtitle = 5;
|
||||||
|
google.protobuf.StringValue description = 6;
|
||||||
|
google.protobuf.StringValue link_url = 7;
|
||||||
|
google.protobuf.StringValue icon_name = 8;
|
||||||
|
int32 sort_order = 9;
|
||||||
|
bool is_active = 10;
|
||||||
|
PageSettingsImageFile image_file = 11; // فایل تصویر (اختیاری برای ویرایش)
|
||||||
|
}
|
||||||
|
|
||||||
|
message SavePageImageResponse
|
||||||
|
{
|
||||||
|
int64 id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ══════════════════════════════════════════════════════════════
|
||||||
|
// Delete Page Image
|
||||||
|
// ══════════════════════════════════════════════════════════════
|
||||||
|
message DeletePageImageRequest
|
||||||
|
{
|
||||||
|
int64 id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ══════════════════════════════════════════════════════════════
|
||||||
|
// File Upload Model
|
||||||
|
// ══════════════════════════════════════════════════════════════
|
||||||
|
message PageSettingsImageFile
|
||||||
|
{
|
||||||
|
bytes file = 1;
|
||||||
|
string mime = 2;
|
||||||
|
string file_name = 3;
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
using CMSMicroservice.Protobuf.Protos.SitePageSettings;
|
||||||
|
using CMSMicroservice.Application.SitePageSettingsCQ.Commands.DeletePageImage;
|
||||||
|
using CMSMicroservice.Application.SitePageSettingsCQ.Commands.SavePageImage;
|
||||||
|
using CMSMicroservice.Application.SitePageSettingsCQ.Commands.SavePageSettings;
|
||||||
|
using CMSMicroservice.Application.SitePageSettingsCQ.Queries.GetAllPageSettings;
|
||||||
|
using CMSMicroservice.Application.SitePageSettingsCQ.Queries.GetPageSettings;
|
||||||
|
using Google.Protobuf.WellKnownTypes;
|
||||||
|
using Grpc.Core;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.WebApi.Services;
|
||||||
|
|
||||||
|
public class SitePageSettingsService : SitePageSettingsContract.SitePageSettingsContractBase
|
||||||
|
{
|
||||||
|
private readonly ISender _sender;
|
||||||
|
|
||||||
|
public SitePageSettingsService(ISender sender)
|
||||||
|
{
|
||||||
|
_sender = sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── GetPageSettings ──
|
||||||
|
public override async Task<PageSettingsResponse> GetPageSettings(GetPageSettingsRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var query = new GetPageSettingsQuery { PageKey = request.PageKey };
|
||||||
|
var result = await _sender.Send(query, context.CancellationToken);
|
||||||
|
|
||||||
|
if (result == null)
|
||||||
|
throw new RpcException(new Status(StatusCode.NotFound, $"صفحه با کلید '{request.PageKey}' یافت نشد"));
|
||||||
|
|
||||||
|
return MapToResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── GetAllPageSettings ──
|
||||||
|
public override async Task<GetAllPageSettingsResponse> GetAllPageSettings(GetAllPageSettingsRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var query = new GetAllPageSettingsQuery();
|
||||||
|
var result = await _sender.Send(query, context.CancellationToken);
|
||||||
|
|
||||||
|
var response = new GetAllPageSettingsResponse();
|
||||||
|
foreach (var item in result)
|
||||||
|
{
|
||||||
|
response.Pages.Add(new PageSettingsSummary
|
||||||
|
{
|
||||||
|
Id = item.Id,
|
||||||
|
PageKey = item.PageKey,
|
||||||
|
Title = item.Title,
|
||||||
|
IsActive = item.IsActive,
|
||||||
|
ImageCount = item.ImageCount,
|
||||||
|
LastModified = Timestamp.FromDateTime(DateTime.SpecifyKind(item.LastModified, DateTimeKind.Utc))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── SavePageSettings ──
|
||||||
|
public override async Task<SavePageSettingsResponse> SavePageSettings(SavePageSettingsRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var command = new SavePageSettingsCommand
|
||||||
|
{
|
||||||
|
PageKey = request.PageKey,
|
||||||
|
Title = request.Title,
|
||||||
|
MetaDescription = request.MetaDescription,
|
||||||
|
HeroTitle = request.HeroTitle,
|
||||||
|
HeroSubtitle = request.HeroSubtitle,
|
||||||
|
IsActive = request.IsActive,
|
||||||
|
SettingsJson = request.SettingsJson,
|
||||||
|
ImageFileBytes = request.HeroImageFile?.File?.ToByteArray(),
|
||||||
|
ImageFileMime = request.HeroImageFile?.Mime,
|
||||||
|
ImageFileName = request.HeroImageFile?.FileName
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await _sender.Send(command, context.CancellationToken);
|
||||||
|
return new SavePageSettingsResponse { Id = result.Id, Success = result.Success };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── SavePageImage ──
|
||||||
|
public override async Task<SavePageImageResponse> SavePageImage(SavePageImageRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var command = new SavePageImageCommand
|
||||||
|
{
|
||||||
|
Id = request.Id,
|
||||||
|
SitePageSettingsId = request.SitePageSettingsId,
|
||||||
|
ImageGroup = request.ImageGroup,
|
||||||
|
Title = request.Title,
|
||||||
|
Subtitle = request.Subtitle,
|
||||||
|
Description = request.Description,
|
||||||
|
LinkUrl = request.LinkUrl,
|
||||||
|
IconName = request.IconName,
|
||||||
|
SortOrder = request.SortOrder,
|
||||||
|
IsActive = request.IsActive,
|
||||||
|
ImageFileBytes = request.ImageFile?.File?.ToByteArray(),
|
||||||
|
ImageFileMime = request.ImageFile?.Mime,
|
||||||
|
ImageFileName = request.ImageFile?.FileName
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await _sender.Send(command, context.CancellationToken);
|
||||||
|
return new SavePageImageResponse { Id = result.Id, Success = result.Success };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── DeletePageImage ──
|
||||||
|
public override async Task<Empty> DeletePageImage(DeletePageImageRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var command = new DeletePageImageCommand { Id = request.Id };
|
||||||
|
await _sender.Send(command, context.CancellationToken);
|
||||||
|
return new Empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Private Mapping Helpers ──
|
||||||
|
private static PageSettingsResponse MapToResponse(PageSettingsDto dto)
|
||||||
|
{
|
||||||
|
var response = new PageSettingsResponse
|
||||||
|
{
|
||||||
|
Id = dto.Id,
|
||||||
|
PageKey = dto.PageKey,
|
||||||
|
Title = dto.Title,
|
||||||
|
MetaDescription = dto.MetaDescription,
|
||||||
|
HeroTitle = dto.HeroTitle,
|
||||||
|
HeroSubtitle = dto.HeroSubtitle,
|
||||||
|
HeroImagePath = dto.HeroImagePath,
|
||||||
|
IsActive = dto.IsActive,
|
||||||
|
SettingsJson = dto.SettingsJson ?? string.Empty
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var img in dto.Images)
|
||||||
|
{
|
||||||
|
response.Images.Add(new PageImageItem
|
||||||
|
{
|
||||||
|
Id = img.Id,
|
||||||
|
ImageGroup = img.ImageGroup,
|
||||||
|
Title = img.Title,
|
||||||
|
Subtitle = img.Subtitle,
|
||||||
|
Description = img.Description,
|
||||||
|
ImagePath = img.ImagePath,
|
||||||
|
ThumbnailPath = img.ThumbnailPath,
|
||||||
|
LinkUrl = img.LinkUrl,
|
||||||
|
IconName = img.IconName,
|
||||||
|
SortOrder = img.SortOrder,
|
||||||
|
IsActive = img.IsActive
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user