Initial code commit.

This commit is contained in:
2026-02-03 10:44:31 +08:00
parent 8927c5ae0e
commit d69fe2cc1f
99 changed files with 10839 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
using Microsoft.AspNetCore.Http;
using Sufi.Demo.PeopleDirectory.Application.Contracts.Services;
using System.Security.Claims;
namespace Sufi.Demo.PeopleDirectory.Infrastructure.Identity
{
public class CurrentUserService : ICurrentUserService
{
public string? UserId { get; }
public List<KeyValuePair<string, string>>? Claims { get; set; }
public CurrentUserService(IHttpContextAccessor httpContextAccessor)
{
var userClaim = httpContextAccessor.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier);
if (userClaim != null)
{
UserId = userClaim.Value;
}
Claims = httpContextAccessor.HttpContext?.User?.Claims.AsEnumerable().Select(item => new KeyValuePair<string, string>(item.Type, item.Value)).ToList();
}
}
}

View File

@@ -0,0 +1,38 @@
using Microsoft.Extensions.DependencyInjection;
using Quartz;
using Sufi.Demo.PeopleDirectory.Application.Contracts.Services;
using Sufi.Demo.PeopleDirectory.Infrastructure.Identity;
using Sufi.Demo.PeopleDirectory.Infrastructure.Jobs;
using Sufi.Demo.PeopleDirectory.Infrastructure.Services;
namespace Sufi.Demo.PeopleDirectory.Infrastructure
{
public static class InfrastructureServiceRegistration
{
public static IServiceCollection AddInfrastructureServices(this IServiceCollection services)
{
services.AddHybridCache();
services.AddTransient<ICurrentUserService, CurrentUserService>()
.AddTransient<IAppCache, AppCache>();
// Some background jobs here.
services.AddQuartz(options =>
{
var jobKey = new JobKey("ClearPersistentDataJob");
options.AddJob<ClearPersistentDataJob>(opt => opt.WithIdentity(jobKey));
options.AddTrigger(opt =>
{
opt.ForJob(jobKey)
.WithIdentity("ClearPersistentDataJob-trigger")
.WithSimpleSchedule(x => x
.WithIntervalInMinutes(10)
.RepeatForever());
});
});
services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);
return services;
}
}
}

View File

@@ -0,0 +1,61 @@
using Quartz;
using Sufi.Demo.PeopleDirectory.Application.Contracts.Repositories;
using Sufi.Demo.PeopleDirectory.Application.Contracts.Services;
using Sufi.Demo.PeopleDirectory.Domain.Entities.Misc;
namespace Sufi.Demo.PeopleDirectory.Infrastructure.Jobs
{
/// <summary>
/// Represents the cleanup job.
/// </summary>
/// <remarks>
/// Initialize an instance of <see cref="ClearPersistentDataJob"/> class.
/// </remarks>
/// <param name="unitOfWorkInt"></param>
/// <param name="unitOfWorkString"></param>
public class ClearPersistentDataJob(IUnitOfWork<int> unitOfWorkInt, IUnitOfWork<string> unitOfWorkString, IAppCache appCache) : IJob
{
private const string LastDateDeletedKey = "LastDateDeleted";
private const string DateTimeFormat = "yyyy-MM-dd HH:mm:ss.ffff";
/// <summary>
/// Job implementation to be executed.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task Execute(IJobExecutionContext context)
{
// Get all contacts to be deleted.
var contactsToDelete = await unitOfWorkInt.Repository<Contact>().GetAllAsync();
// Skips if nothing to delete.
if (contactsToDelete.Count == 0)
return;
// Delete all contacts.
foreach (var contact in contactsToDelete)
{
await unitOfWorkInt.Repository<Contact>().DeleteAsync(contact);
}
// Update the last date deleted in the ServerInfo table.
var infoToUpdate = await unitOfWorkString.Repository<ServerInfo>().GetByIdAsync(LastDateDeletedKey);
if (infoToUpdate != null)
{
infoToUpdate.Value = DateTime.UtcNow.ToString(DateTimeFormat);
await unitOfWorkString.Repository<ServerInfo>().UpdateAsync(infoToUpdate);
}
else
{
var infoToAdd = new ServerInfo { Id = LastDateDeletedKey, Value = DateTime.UtcNow.ToString(DateTimeFormat) };
await unitOfWorkString.Repository<ServerInfo>().AddAsync(infoToAdd);
}
// Commit the changes to the database.
await unitOfWorkString.Commit(context.CancellationToken);
// Clear cache entries related to contacts.
await appCache.ResetAsync();
}
}
}

View File

@@ -0,0 +1,36 @@
using Microsoft.Extensions.Caching.Hybrid;
using Sufi.Demo.PeopleDirectory.Application.Contracts.Services;
namespace Sufi.Demo.PeopleDirectory.Infrastructure.Services
{
public class AppCache(
HybridCache hybridCache
) : IAppCache
{
public ValueTask<T> GetOrAddAsync<T>(string key, Func<CancellationToken, ValueTask<T>> factory,
IEnumerable<string>? tags = null, TimeSpan? absoluteExpireTime = null)
{
var options = new HybridCacheEntryOptions
{
Expiration = absoluteExpireTime ?? TimeSpan.FromSeconds(30),
};
return hybridCache.GetOrCreateAsync<T>(key, factory, options, tags);
}
public ValueTask RemoveAsync(string key)
{
return hybridCache.RemoveAsync(key);
}
public ValueTask RemoveByTagAsync(string tag)
{
return hybridCache.RemoveByTagAsync(tag);
}
public ValueTask ResetAsync()
{
return hybridCache.RemoveByTagAsync("*");
}
}
}

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.3.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" Version="9.10.0" />
<PackageReference Include="Quartz.AspNetCore" Version="3.14.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Sufi.Demo.PeopleDirectory.Application\Sufi.Demo.PeopleDirectory.Application.csproj" />
</ItemGroup>
</Project>