feat(T4.13): add PackageFeature CRUD support
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 9m41s

- Proto: add feature_ids to Create/Update/Get package messages
- Commands: add FeatureIds to Create/Update commands
- Handlers: sync PackageFeature join entities on create/update
- Queries: include PackageFeatures, map FeatureIds in responses
- Bump NuGet to v0.0.188
This commit is contained in:
masoodafar-web
2026-02-26 21:59:24 +03:30
parent aaaf7fc1ca
commit dcd11351a3
10 changed files with 69 additions and 7 deletions
@@ -22,4 +22,5 @@ public record CreateNewPackageCommand : IRequest<CreateNewPackageResponseDto>
public int MaxNetworkLevel { get; init; } = 15; public int MaxNetworkLevel { get; init; } = 15;
public long MagicWalletMaxDeposit { get; init; } = 1_000_000_000; public long MagicWalletMaxDeposit { get; init; } = 1_000_000_000;
public long MagicWalletMaxCredit { get; init; } = 2_500_000_000; public long MagicWalletMaxCredit { get; init; } = 2_500_000_000;
public List<long> FeatureIds { get; init; } = new();
} }
@@ -16,6 +16,22 @@ public class CreateNewPackageCommandHandler : IRequestHandler<CreateNewPackageCo
await _context.Packages.AddAsync(entity, cancellationToken); await _context.Packages.AddAsync(entity, cancellationToken);
entity.AddDomainEvent(new CreateNewPackageEvent(entity)); entity.AddDomainEvent(new CreateNewPackageEvent(entity));
await _context.SaveChangesAsync(cancellationToken); await _context.SaveChangesAsync(cancellationToken);
// Sync PackageFeatures
if (request.FeatureIds.Any())
{
foreach (var featureId in request.FeatureIds)
{
_context.PackageFeatures.Add(new PackageFeature
{
PackageId = entity.Id,
ClubFeatureId = featureId,
IsIncluded = true
});
}
await _context.SaveChangesAsync(cancellationToken);
}
return entity.Adapt<CreateNewPackageResponseDto>(); return entity.Adapt<CreateNewPackageResponseDto>();
} }
} }
@@ -24,4 +24,5 @@ public record UpdatePackageCommand : IRequest<Unit>
public int MaxNetworkLevel { get; init; } public int MaxNetworkLevel { get; init; }
public long MagicWalletMaxDeposit { get; init; } public long MagicWalletMaxDeposit { get; init; }
public long MagicWalletMaxCredit { get; init; } public long MagicWalletMaxCredit { get; init; }
public List<long> FeatureIds { get; init; } = new();
} }
@@ -16,6 +16,23 @@ public class UpdatePackageCommandHandler : IRequestHandler<UpdatePackageCommand,
request.Adapt(entity); request.Adapt(entity);
_context.Packages.Update(entity); _context.Packages.Update(entity);
entity.AddDomainEvent(new UpdatePackageEvent(entity)); entity.AddDomainEvent(new UpdatePackageEvent(entity));
// Sync PackageFeatures — remove old, add new
var existingFeatures = await _context.PackageFeatures
.Where(pf => pf.PackageId == request.Id)
.ToListAsync(cancellationToken);
_context.PackageFeatures.RemoveRange(existingFeatures);
foreach (var featureId in request.FeatureIds)
{
_context.PackageFeatures.Add(new PackageFeature
{
PackageId = request.Id,
ClubFeatureId = featureId,
IsIncluded = true
});
}
await _context.SaveChangesAsync(cancellationToken); await _context.SaveChangesAsync(cancellationToken);
return Unit.Value; return Unit.Value;
} }
@@ -14,6 +14,7 @@ public class GetAllPackageByFilterQueryHandler : IRequestHandler<GetAllPackageBy
.Where(x => !x.IsDeleted) .Where(x => !x.IsDeleted)
.ApplyOrder(sortBy: request.SortBy) .ApplyOrder(sortBy: request.SortBy)
.AsNoTracking() .AsNoTracking()
.Include(x => x.PackageFeatures)
.AsQueryable(); .AsQueryable();
if (request.Filter is not null) if (request.Filter is not null)
{ {
@@ -25,11 +26,24 @@ public class GetAllPackageByFilterQueryHandler : IRequestHandler<GetAllPackageBy
.Where(x => request.Filter.Price == null || x.Price == request.Filter.Price) .Where(x => request.Filter.Price == null || x.Price == request.Filter.Price)
; ;
} }
var packages = await query
.PaginatedListAsync(paginationState: request.PaginationState)
.ToListAsync(cancellationToken);
var models = packages.Select(p =>
{
var model = p.Adapt<GetAllPackageByFilterResponseModel>();
model.FeatureIds = p.PackageFeatures?
.Where(pf => pf.IsIncluded)
.Select(pf => pf.ClubFeatureId)
.ToList() ?? new();
return model;
}).ToList();
return new GetAllPackageByFilterResponseDto return new GetAllPackageByFilterResponseDto
{ {
MetaData = await query.GetMetaData(request.PaginationState, cancellationToken), MetaData = await query.GetMetaData(request.PaginationState, cancellationToken),
Models = await query.PaginatedListAsync(paginationState: request.PaginationState) Models = models
.ProjectToType<GetAllPackageByFilterResponseModel>().ToListAsync(cancellationToken)
}; };
} }
} }
@@ -31,4 +31,5 @@ public class GetAllPackageByFilterResponseDto
public int MaxNetworkLevel { get; set; } public int MaxNetworkLevel { get; set; }
public long MagicWalletMaxDeposit { get; set; } public long MagicWalletMaxDeposit { get; set; }
public long MagicWalletMaxCredit { get; set; } public long MagicWalletMaxCredit { get; set; }
public List<long> FeatureIds { get; set; } = new();
} }
@@ -11,12 +11,19 @@ public class GetPackageQueryHandler : IRequestHandler<GetPackageQuery, GetPackag
public async Task<GetPackageResponseDto> Handle(GetPackageQuery request, public async Task<GetPackageResponseDto> Handle(GetPackageQuery request,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var response = await _context.Packages var package = await _context.Packages
.AsNoTracking() .AsNoTracking()
.Include(x => x.PackageFeatures)
.Where(x => x.Id == request.Id) .Where(x => x.Id == request.Id)
.ProjectToType<GetPackageResponseDto>() .FirstOrDefaultAsync(cancellationToken)
.FirstOrDefaultAsync(cancellationToken); ?? throw new NotFoundException(nameof(Package), request.Id);
return response ?? throw new NotFoundException(nameof(Package), request.Id); var response = package.Adapt<GetPackageResponseDto>();
response.FeatureIds = package.PackageFeatures?
.Where(pf => pf.IsIncluded)
.Select(pf => pf.ClubFeatureId)
.ToList() ?? new();
return response;
} }
} }
@@ -24,4 +24,5 @@ public class GetPackageResponseDto
public int MaxNetworkLevel { get; set; } public int MaxNetworkLevel { get; set; }
public long MagicWalletMaxDeposit { get; set; } public long MagicWalletMaxDeposit { get; set; }
public long MagicWalletMaxCredit { get; set; } public long MagicWalletMaxCredit { get; set; }
public List<long> FeatureIds { get; set; } = new();
} }
@@ -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.187</Version> <Version>0.0.188</Version>
<DebugType>None</DebugType> <DebugType>None</DebugType>
<DebugSymbols>False</DebugSymbols> <DebugSymbols>False</DebugSymbols>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild> <GeneratePackageOnBuild>False</GeneratePackageOnBuild>
@@ -99,6 +99,7 @@ message CreateNewPackageRequest
int32 max_network_level = 15; int32 max_network_level = 15;
int64 magic_wallet_max_deposit = 16; int64 magic_wallet_max_deposit = 16;
int64 magic_wallet_max_credit = 17; int64 magic_wallet_max_credit = 17;
repeated int64 feature_ids = 18;
} }
message CreateNewPackageResponse message CreateNewPackageResponse
{ {
@@ -124,6 +125,7 @@ message UpdatePackageRequest
int32 max_network_level = 16; int32 max_network_level = 16;
int64 magic_wallet_max_deposit = 17; int64 magic_wallet_max_deposit = 17;
int64 magic_wallet_max_credit = 18; int64 magic_wallet_max_credit = 18;
repeated int64 feature_ids = 19;
} }
message DeletePackageRequest message DeletePackageRequest
{ {
@@ -152,6 +154,7 @@ message GetPackageResponse
int32 max_network_level = 15; int32 max_network_level = 15;
int64 magic_wallet_max_deposit = 16; int64 magic_wallet_max_deposit = 16;
int64 magic_wallet_max_credit = 17; int64 magic_wallet_max_credit = 17;
repeated int64 feature_ids = 18;
} }
message GetAllPackageByFilterRequest message GetAllPackageByFilterRequest
{ {
@@ -191,6 +194,7 @@ message GetAllPackageByFilterResponseModel
int32 max_network_level = 15; int32 max_network_level = 15;
int64 magic_wallet_max_deposit = 16; int64 magic_wallet_max_deposit = 16;
int64 magic_wallet_max_credit = 17; int64 magic_wallet_max_credit = 17;
repeated int64 feature_ids = 18;
} }
message GetUserPackageStatusRequest message GetUserPackageStatusRequest