[Add] MasterData Services.
This commit is contained in:
187
AMREZ.EOP.Infrastructures/Repositories/SubdistrictRepository.cs
Normal file
187
AMREZ.EOP.Infrastructures/Repositories/SubdistrictRepository.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
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.Subdistrict;
|
||||
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 SubdistrictRepository : ISubdistrictRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public SubdistrictRepository(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<Subdistrict?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
return await db.Subdistricts.AsNoTracking()
|
||||
.Include(x => x.District)
|
||||
.ThenInclude(d => d.Province)
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<Subdistrict>> SearchEffectiveAsync(
|
||||
Guid tenantId,
|
||||
SubdistrictListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.Subdistricts.AsNoTracking()
|
||||
.Include(s => s.District)
|
||||
.ThenInclude(d => d.Province)
|
||||
.Where(s => s.Scope == "tenant" && s.TenantId == tid && !s.IsDeleted);
|
||||
|
||||
var overriddenIds = db.Subdistricts
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.SubdistrictBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.Subdistricts.AsNoTracking()
|
||||
.Include(s => s.District)
|
||||
.ThenInclude(d => d.Province)
|
||||
.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);
|
||||
|
||||
// Filter by District
|
||||
if (req.DistrictId.HasValue && req.DistrictId.Value != Guid.Empty)
|
||||
q = q.Where(x => x.DistrictId == req.DistrictId.Value);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.DistrictCode))
|
||||
{
|
||||
var dd = req.DistrictCode.Trim();
|
||||
q = q.Where(x => x.District.Code == dd);
|
||||
}
|
||||
|
||||
// Filter by Province
|
||||
if (req.ProvinceId.HasValue && req.ProvinceId.Value != Guid.Empty)
|
||||
q = q.Where(x => x.District.ProvinceId == req.ProvinceId.Value);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.ProvinceCode))
|
||||
{
|
||||
var pd = req.ProvinceCode.Trim();
|
||||
q = q.Where(x => x.District.Province.Code == pd);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x =>
|
||||
x.Code.Contains(s) ||
|
||||
x.Name.Contains(s) ||
|
||||
x.Code.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
|
||||
var items = await q
|
||||
.OrderBy(x => x.District.Province.Code)
|
||||
.ThenBy(x => x.District.Code)
|
||||
.ThenBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<Subdistrict>(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.Subdistricts.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(Subdistrict entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.Subdistricts.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Subdistrict entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.Subdistricts.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 s = await db.Subdistricts.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (s is null) return 0;
|
||||
|
||||
if (s.Scope == "global")
|
||||
{
|
||||
var exists = await db.SubdistrictBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new SubdistrictBlock
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.SubdistrictBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (s.TenantId != tid) return 0;
|
||||
|
||||
s.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user