[Add] MasterData Services.
This commit is contained in:
148
AMREZ.EOP.Infrastructures/Repositories/CategoryRepository.cs
Normal file
148
AMREZ.EOP.Infrastructures/Repositories/CategoryRepository.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Category;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class CategoryRepository : ICategoryRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public CategoryRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<Category?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.Categories.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<Category>> SearchEffectiveAsync(Guid tenantId, CategoryListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.Categories.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.Categories
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.CategoryBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.Categories.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<Category>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.Categories.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(Category entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.Categories.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Category entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.Categories.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.Categories.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.CategoryBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new CategoryBlock
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.CategoryBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user