Files
Thanakarn Klangkasame 1e636aa3d5 [Add] MasterData Services.
2025-11-26 10:29:56 +07:00

199 lines
8.8 KiB
C#

using AMREZ.EOP.Abstractions.Applications.Tenancy;
using AMREZ.EOP.Abstractions.Applications.UseCases.Authentications;
using AMREZ.EOP.Abstractions.Applications.UseCases.HumanResources;
using AMREZ.EOP.Abstractions.Applications.UseCases.ImportData.Location;
using AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Brand;
using AMREZ.EOP.Abstractions.Applications.UseCases.Payments.SlipVerification;
using AMREZ.EOP.Abstractions.Applications.UseCases.Payments.SlipVerification.QrDecode;
using AMREZ.EOP.Abstractions.Applications.UseCases.Tenancy;
using AMREZ.EOP.Abstractions.Infrastructures.Common;
using AMREZ.EOP.Abstractions.Infrastructures.Integrations.SCB.Clients;
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
using AMREZ.EOP.Abstractions.Security;
using AMREZ.EOP.Abstractions.Storage;
using AMREZ.EOP.Application.UseCases.Authentications;
using AMREZ.EOP.Application.UseCases.HumanResources;
using AMREZ.EOP.Application.UseCases.ImportData.Location;
using AMREZ.EOP.Application.UseCases.MasterData.Brand;
using AMREZ.EOP.Application.UseCases.Payments.SlipVerification;
using AMREZ.EOP.Application.UseCases.Payments.SlipVerification.BankDetect;
using AMREZ.EOP.Application.UseCases.Payments.SlipVerification.QrDecode;
using AMREZ.EOP.Application.UseCases.Tenancy;
using AMREZ.EOP.Domain.Entities.MasterData;
using AMREZ.EOP.Domain.Shared.Payments;
using AMREZ.EOP.Domain.Shared.Tenancy;
using AMREZ.EOP.Infrastructures.Data;
using AMREZ.EOP.Infrastructures.Integrations.SCB.Clients;
using AMREZ.EOP.Infrastructures.Options;
using AMREZ.EOP.Infrastructures.Repositories;
using AMREZ.EOP.Infrastructures.Security;
using AMREZ.EOP.Infrastructures.Storage;
using AMREZ.EOP.Infrastructures.Tenancy;
using AMREZ.EOP.Infrastructures.UnitOfWork;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
namespace AMREZ.EOP.Infrastructures.DependencyInjections;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddInfrastructure(this IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddScoped<IJwtFactory, JwtFactory>();
services.AddSingleton(new SCBOptions());
services.AddHttpClient<IScbSlipClient, ScbSlipClient>((sp, http) =>
{
var o = sp.GetRequiredService<SCBOptions>();
var baseUrl = o.BaseUrl.EndsWith('/') ? o.BaseUrl[..^1] : o.BaseUrl;
http.BaseAddress = new Uri(baseUrl + "/v1/");
http.Timeout = TimeSpan.FromSeconds(20);
});
// Options
services.AddOptions<AuthOptions>()
.BindConfiguration("Connections")
.ValidateOnStart();
// Tenancy core (order matters for AddDbContext factory resolution)
services.AddSingleton(sp =>
{
var map = new TenantMap();
var opts = sp.GetRequiredService<IOptions<AuthOptions>>().Value;
map.UpsertTenant("public", new TenantContext
{
Id = "public",
Schema = "public",
ConnectionString = string.IsNullOrWhiteSpace(opts.DefaultConnection) ? null : opts.DefaultConnection,
Mode = TenantMode.Rls
});
return map;
});
services.AddHostedService<TenantMapLoader>();
services.AddScoped<SearchPathInterceptor>();
services.AddScoped<TenantRlsInterceptor>();
services.AddScoped<ITenantResolver, DefaultTenantResolver>();
services.AddScoped<ITenantDbContextFactory, TenantDbContextFactory>();
services.AddScoped<ITenantProvisioner, TenantProvisioner>();
// DbContext (attach RLS/search_path interceptors)
services.AddDbContext<AppDbContext>((sp, o) =>
{
var cfg = sp.GetRequiredService<IOptions<AuthOptions>>().Value;
o.UseNpgsql(cfg.DefaultConnection);
o.AddInterceptors(
sp.GetRequiredService<SearchPathInterceptor>(),
sp.GetRequiredService<TenantRlsInterceptor>()
);
});
// Db scope / UoW
services.AddScoped<IDbScope, DbScope>();
services.AddScoped<EFUnitOfWork>();
services.AddScoped<IUnitOfWork, EFUnitOfWork>();
// Security
services.AddScoped<IPasswordHasher, BcryptPasswordHasher>();
// Repositories — Auth & HR แยกกัน
services.AddScoped<IUserRepository, UserRepository>();
services.AddScoped<IUserProfileRepository, UserProfileRepository>();
services.AddScoped<ITenantRepository, TenantRepository>();
// UseCases — Authentication
services.AddScoped<ILoginUseCase, LoginUseCase>();
services.AddScoped<IRegisterUseCase, RegisterUseCase>();
services.AddScoped<IChangePasswordUseCase, ChangePasswordUseCase>();
services.AddScoped<IAddEmailIdentityUseCase, AddEmailIdentityUseCase>();
services.AddScoped<IVerifyEmailUseCase, VerifyEmailUseCase>();
services.AddScoped<IEnableTotpUseCase, EnableTotpUseCase>();
services.AddScoped<IDisableMfaUseCase, DisableMfaUseCase>();
services.AddScoped<ILogoutUseCase, LogoutUseCase>();
services.AddScoped<ILogoutAllUseCase, LogoutAllUseCase>();
services.AddScoped<IIssueTokenPairUseCase, IssueTokenPairUseCase>();
services.AddScoped<IRefreshUseCase, RefreshUseCase>();
// UseCases — HR
services.AddScoped<IUpsertUserProfileUseCase, UpsertUserProfileUseCase>();
services.AddScoped<IAddEmploymentUseCase, AddEmploymentUseCase>();
services.AddScoped<IEndEmploymentUseCase, EndEmploymentUseCase>();
services.AddScoped<IAddEmployeeAddressUseCase, AddEmployeeAddressUseCase>();
services.AddScoped<ISetPrimaryAddressUseCase, SetPrimaryAddressUseCase>();
services.AddScoped<IAddEmergencyContactUseCase, AddEmergencyContactUseCase>();
services.AddScoped<ISetPrimaryEmergencyContactUseCase, SetPrimaryEmergencyContactUseCase>();
services.AddScoped<IAddEmployeeBankAccountUseCase, AddEmployeeBankAccountUseCase>();
services.AddScoped<ISetPrimaryBankAccountUseCase, SetPrimaryBankAccountUseCase>();
// UseCases — Tenancy
services.AddScoped<ICreateTenantUseCase, CreateTenantUseCase>();
services.AddScoped<IUpdateTenantUseCase, UpdateTenantUseCase>();
services.AddScoped<IDeleteTenantUseCase, DeleteTenantUseCase>();
services.AddScoped<IMapDomainUseCase, MapDomainUseCase>();
services.AddScoped<IUnmapDomainUseCase, UnmapDomainUseCase>();
services.AddScoped<IAddBaseDomainUseCase, AddBaseDomainUseCase>();
services.AddScoped<IRemoveBaseDomainUseCase, RemoveBaseDomainUseCase>();
services.AddScoped<IListTenantsUseCase, ListTenantsUseCase>();
services.AddScoped<IListDomainsUseCase, ListDomainsUseCase>();
// UseCase & helpers (no OCR)
services.AddScoped<IQrDecoder, ZxingQrDecoder>();
services.AddScoped<IVerifyFromImageUseCase, VerifyFromImageUseCase>();
services.AddMasterDataBrands();
services.AddMasterDataLocations();
// Redis (optional)
services.AddSingleton<IConnectionMultiplexer?>(sp =>
{
var opts = sp.GetRequiredService<IOptions<AuthOptions>>().Value;
var conn = opts.RedisConnection;
if (string.IsNullOrWhiteSpace(conn)) return null;
try
{
var cfg = ConfigurationOptions.Parse(conn, true);
cfg.AbortOnConnectFail = false;
cfg.ConnectTimeout = 1000;
cfg.SyncTimeout = 1000;
var mux = ConnectionMultiplexer.Connect(cfg);
return mux.IsConnected ? mux : null;
}
catch { return null; }
});
return services;
}
public static IServiceCollection AddMasterDataBrands(this IServiceCollection services)
{
// Repo
services.AddScoped<IBrandRepository, BrandRepository>();
// UseCases
services.AddScoped<IListBrandsUseCase, ListBrandsUseCase>();
services.AddScoped<IGetBrandUseCase, GetBrandUseCase>();
services.AddScoped<ICreateBrandUseCase, CreateBrandUseCase>();
services.AddScoped<IUpdateBrandUseCase, UpdateBrandUseCase>();
services.AddScoped<IDeleteBrandUseCase, DeleteBrandUseCase>();
return services;
}
public static IServiceCollection AddMasterDataLocations(this IServiceCollection services)
{
// Repo
services.AddScoped<IProvinceRepository, ProvinceRepository>();
services.AddScoped<IDistrictRepository, DistrictRepository>();
services.AddScoped<ISubdistrictRepository, SubdistrictRepository>();
// UseCases
services.AddScoped<ILocationImportUseCase, LocationImportUseCase>();
return services;
}
}