refactor(architecture): harden module boundaries
Some checks failed
ci / release-gate (push) Has been cancelled
Some checks failed
ci / release-gate (push) Has been cancelled
This commit is contained in:
@@ -15,7 +15,7 @@ internal sealed class PointAdministrationService(CommerceAdministrationDependenc
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
await AssertAdminAsync(actor, cancellationToken);
|
||||
var tasks = dbContext.PointActivityTasks.AsNoTracking()
|
||||
var tasks = pointsPersistence.PointActivityTasks.AsNoTracking()
|
||||
.Where(item => item.TenantId == actor.TenantId);
|
||||
if (!string.IsNullOrWhiteSpace(query.Status))
|
||||
tasks = tasks.Where(item => item.Status == ParsePointTaskStatus(query.Status));
|
||||
@@ -38,16 +38,16 @@ internal sealed class PointAdministrationService(CommerceAdministrationDependenc
|
||||
throw new CommerceException("Point task points and claim limit must be positive.", "invalid_point_task");
|
||||
|
||||
var task = command.Id.HasValue
|
||||
? await dbContext.PointActivityTasks.SingleOrDefaultAsync(
|
||||
? await pointsPersistence.PointActivityTasks.SingleOrDefaultAsync(
|
||||
item => item.TenantId == actor.TenantId && item.Id == command.Id.Value,
|
||||
cancellationToken)
|
||||
: await dbContext.PointActivityTasks.SingleOrDefaultAsync(
|
||||
: await pointsPersistence.PointActivityTasks.SingleOrDefaultAsync(
|
||||
item => item.TenantId == actor.TenantId && item.TaskKey == command.TaskKey.Trim(),
|
||||
cancellationToken);
|
||||
if (task is null)
|
||||
{
|
||||
task = new PointActivityTask { TenantId = actor.TenantId };
|
||||
dbContext.PointActivityTasks.Add(task);
|
||||
pointsPersistence.PointActivityTasks.Add(task);
|
||||
}
|
||||
|
||||
task.TaskKey = command.TaskKey.Trim();
|
||||
@@ -63,7 +63,7 @@ internal sealed class PointAdministrationService(CommerceAdministrationDependenc
|
||||
task.Rules = JsonObjectOrDefault(command.Rules);
|
||||
task.Metadata = JsonObjectOrDefault(command.Metadata);
|
||||
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
await unitOfWork.SaveChangesAsync(cancellationToken);
|
||||
return task;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ internal sealed class PointAdministrationService(CommerceAdministrationDependenc
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
await AssertAdminAsync(actor, cancellationToken);
|
||||
var claims = dbContext.PointActivityClaims.AsNoTracking()
|
||||
var claims = pointsPersistence.PointActivityClaims.AsNoTracking()
|
||||
.Where(item => item.TenantId == actor.TenantId);
|
||||
if (query.UserId.HasValue) claims = claims.Where(item => item.UserId == query.UserId.Value);
|
||||
|
||||
@@ -90,7 +90,7 @@ internal sealed class PointAdministrationService(CommerceAdministrationDependenc
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
await AssertAdminAsync(actor, cancellationToken);
|
||||
var items = dbContext.PointExchangeItems.AsNoTracking()
|
||||
var items = pointsPersistence.PointExchangeItems.AsNoTracking()
|
||||
.Where(item => item.TenantId == actor.TenantId);
|
||||
if (!string.IsNullOrWhiteSpace(query.Status))
|
||||
items = items.Where(item => item.Status == ParsePointExchangeItemStatus(query.Status));
|
||||
@@ -116,16 +116,16 @@ internal sealed class PointAdministrationService(CommerceAdministrationDependenc
|
||||
throw new CommerceException("Point exchange item cost must be positive.", "invalid_point_exchange_item");
|
||||
|
||||
var item = command.Id.HasValue
|
||||
? await dbContext.PointExchangeItems.SingleOrDefaultAsync(
|
||||
? await pointsPersistence.PointExchangeItems.SingleOrDefaultAsync(
|
||||
entry => entry.TenantId == actor.TenantId && entry.Id == command.Id.Value,
|
||||
cancellationToken)
|
||||
: await dbContext.PointExchangeItems.SingleOrDefaultAsync(
|
||||
: await pointsPersistence.PointExchangeItems.SingleOrDefaultAsync(
|
||||
entry => entry.TenantId == actor.TenantId && entry.ItemKey == command.ItemKey.Trim(),
|
||||
cancellationToken);
|
||||
if (item is null)
|
||||
{
|
||||
item = new PointExchangeItem { TenantId = actor.TenantId };
|
||||
dbContext.PointExchangeItems.Add(item);
|
||||
pointsPersistence.PointExchangeItems.Add(item);
|
||||
}
|
||||
|
||||
item.RegionId = command.RegionId;
|
||||
@@ -143,7 +143,7 @@ internal sealed class PointAdministrationService(CommerceAdministrationDependenc
|
||||
item.FulfillmentPayload = JsonObjectOrDefault(command.FulfillmentPayload);
|
||||
item.Metadata = JsonObjectOrDefault(command.Metadata);
|
||||
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
await unitOfWork.SaveChangesAsync(cancellationToken);
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ internal sealed class PointAdministrationService(CommerceAdministrationDependenc
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
await AssertAdminAsync(actor, cancellationToken);
|
||||
var orders = dbContext.PointExchangeOrders.AsNoTracking()
|
||||
var orders = pointsPersistence.PointExchangeOrders.AsNoTracking()
|
||||
.Where(item => item.TenantId == actor.TenantId);
|
||||
if (query.UserId.HasValue) orders = orders.Where(item => item.UserId == query.UserId.Value);
|
||||
|
||||
@@ -173,7 +173,7 @@ internal sealed class PointAdministrationService(CommerceAdministrationDependenc
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
await AssertAdminAsync(actor, cancellationToken);
|
||||
var order = await dbContext.PointExchangeOrders
|
||||
var order = await pointsPersistence.PointExchangeOrders
|
||||
.SingleOrDefaultAsync(item => item.TenantId == actor.TenantId && item.Id == command.OrderId,
|
||||
cancellationToken)
|
||||
?? throw new CommerceException("Point exchange order was not found.",
|
||||
@@ -189,7 +189,7 @@ internal sealed class PointAdministrationService(CommerceAdministrationDependenc
|
||||
order.CancelledAt ??= DateTimeOffset.UtcNow;
|
||||
}
|
||||
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
await unitOfWork.SaveChangesAsync(cancellationToken);
|
||||
return order;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,10 @@ using Tiku.Infrastructure.Persistence;
|
||||
|
||||
namespace Tiku.Infrastructure.Points;
|
||||
|
||||
public sealed class PointService(TikuDbContext dbContext) : IPointService
|
||||
public sealed class PointService(IPointsPersistence pointsPersistence,
|
||||
ILearningPersistence learningPersistence,
|
||||
ICommercePersistence commercePersistence,
|
||||
IIdentityPersistence identityPersistence) : IPointService
|
||||
{
|
||||
public async Task<PointSummaryItem> GetSummaryAsync(
|
||||
PointActor actor,
|
||||
@@ -28,7 +31,7 @@ public sealed class PointService(TikuDbContext dbContext) : IPointService
|
||||
await AssertActiveMemberAsync(actor, cancellationToken);
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var limit = Math.Clamp(query.Limit ?? 50, 1, 200);
|
||||
var tasks = await dbContext.PointActivityTasks
|
||||
var tasks = await pointsPersistence.PointActivityTasks
|
||||
.AsNoTracking()
|
||||
.Where(item =>
|
||||
item.TenantId == actor.TenantId &&
|
||||
@@ -40,7 +43,7 @@ public sealed class PointService(TikuDbContext dbContext) : IPointService
|
||||
.Take(limit)
|
||||
.ToArrayAsync(cancellationToken);
|
||||
var taskIds = tasks.Select(item => item.Id).ToArray();
|
||||
var claimCounts = await dbContext.PointActivityClaims
|
||||
var claimCounts = await pointsPersistence.PointActivityClaims
|
||||
.AsNoTracking()
|
||||
.Where(item =>
|
||||
item.TenantId == actor.TenantId &&
|
||||
@@ -67,7 +70,7 @@ public sealed class PointService(TikuDbContext dbContext) : IPointService
|
||||
await AssertActiveMemberAsync(actor, cancellationToken);
|
||||
var taskKey = NormalizeKey(command.TaskKey, "task_key_required");
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var task = await dbContext.PointActivityTasks
|
||||
var task = await pointsPersistence.PointActivityTasks
|
||||
.SingleOrDefaultAsync(item =>
|
||||
item.TenantId == actor.TenantId &&
|
||||
item.TaskKey == taskKey,
|
||||
@@ -81,7 +84,7 @@ public sealed class PointService(TikuDbContext dbContext) : IPointService
|
||||
if (!string.IsNullOrWhiteSpace(command.SourceType) && command.SourceId.HasValue)
|
||||
{
|
||||
var sourceType = NormalizeOptional(command.SourceType);
|
||||
var existing = await dbContext.PointActivityClaims
|
||||
var existing = await pointsPersistence.PointActivityClaims
|
||||
.AsNoTracking()
|
||||
.Where(item =>
|
||||
item.TenantId == actor.TenantId &&
|
||||
@@ -95,7 +98,7 @@ public sealed class PointService(TikuDbContext dbContext) : IPointService
|
||||
if (existing is not null) return ToClaimItem(existing);
|
||||
}
|
||||
|
||||
var claimedCount = await dbContext.PointActivityClaims.CountAsync(
|
||||
var claimedCount = await pointsPersistence.PointActivityClaims.CountAsync(
|
||||
item =>
|
||||
item.TenantId == actor.TenantId &&
|
||||
item.UserId == actor.UserId &&
|
||||
@@ -119,9 +122,9 @@ public sealed class PointService(TikuDbContext dbContext) : IPointService
|
||||
ClaimedAt = now,
|
||||
Metadata = JsonSerializer.SerializeToElement(new { source = "student_points" })
|
||||
};
|
||||
dbContext.PointActivityClaims.Add(claim);
|
||||
pointsPersistence.PointActivityClaims.Add(claim);
|
||||
var balanceAfter = (await GetSummaryCoreAsync(actor, cancellationToken)).BalancePoints + task.Points;
|
||||
dbContext.UserScoreEvents.Add(new UserScoreEvent
|
||||
learningPersistence.UserScoreEvents.Add(new UserScoreEvent
|
||||
{
|
||||
TenantId = actor.TenantId,
|
||||
UserId = actor.UserId,
|
||||
@@ -139,7 +142,7 @@ public sealed class PointService(TikuDbContext dbContext) : IPointService
|
||||
task.TaskKey
|
||||
})
|
||||
});
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
await pointsPersistence.SaveChangesAsync(cancellationToken);
|
||||
return ToClaimItem(claim);
|
||||
}
|
||||
|
||||
@@ -151,7 +154,7 @@ public sealed class PointService(TikuDbContext dbContext) : IPointService
|
||||
await AssertActiveMemberAsync(actor, cancellationToken);
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var balance = (await GetSummaryCoreAsync(actor, cancellationToken)).BalancePoints;
|
||||
var items = dbContext.PointExchangeItems
|
||||
var items = pointsPersistence.PointExchangeItems
|
||||
.AsNoTracking()
|
||||
.Where(item =>
|
||||
item.TenantId == actor.TenantId &&
|
||||
@@ -175,11 +178,11 @@ public sealed class PointService(TikuDbContext dbContext) : IPointService
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
await AssertActiveMemberAsync(actor, cancellationToken);
|
||||
await using var transaction = dbContext.Database.IsRelational()
|
||||
? await dbContext.Database.BeginTransactionAsync(cancellationToken)
|
||||
await using var transaction = pointsPersistence.Database.IsRelational()
|
||||
? await pointsPersistence.Database.BeginTransactionAsync(cancellationToken)
|
||||
: null;
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var item = await dbContext.PointExchangeItems
|
||||
var item = await pointsPersistence.PointExchangeItems
|
||||
.SingleOrDefaultAsync(entry =>
|
||||
entry.TenantId == actor.TenantId &&
|
||||
entry.Id == command.ItemId,
|
||||
@@ -214,8 +217,8 @@ public sealed class PointService(TikuDbContext dbContext) : IPointService
|
||||
FulfillmentSnapshot = item.FulfillmentPayload,
|
||||
Metadata = JsonSerializer.SerializeToElement(new { source = "student_points_exchange" })
|
||||
};
|
||||
dbContext.PointExchangeOrders.Add(order);
|
||||
dbContext.UserScoreEvents.Add(new UserScoreEvent
|
||||
pointsPersistence.PointExchangeOrders.Add(order);
|
||||
learningPersistence.UserScoreEvents.Add(new UserScoreEvent
|
||||
{
|
||||
TenantId = actor.TenantId,
|
||||
UserId = actor.UserId,
|
||||
@@ -235,7 +238,7 @@ public sealed class PointService(TikuDbContext dbContext) : IPointService
|
||||
if (item.ItemType == PointExchangeItemType.Entitlement)
|
||||
await GrantEntitlementAsync(actor, item, order, now, cancellationToken);
|
||||
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
await pointsPersistence.SaveChangesAsync(cancellationToken);
|
||||
if (transaction is not null) await transaction.CommitAsync(cancellationToken);
|
||||
return ToExchangeOrderItem(order);
|
||||
}
|
||||
@@ -246,7 +249,7 @@ public sealed class PointService(TikuDbContext dbContext) : IPointService
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
await AssertActiveMemberAsync(actor, cancellationToken);
|
||||
var orders = dbContext.PointExchangeOrders
|
||||
var orders = pointsPersistence.PointExchangeOrders
|
||||
.AsNoTracking()
|
||||
.Where(item => item.TenantId == actor.TenantId && item.UserId == actor.UserId);
|
||||
if (!string.IsNullOrWhiteSpace(query.Status))
|
||||
@@ -263,14 +266,14 @@ public sealed class PointService(TikuDbContext dbContext) : IPointService
|
||||
PointActor actor,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var earned = await dbContext.PointActivityClaims
|
||||
var earned = await pointsPersistence.PointActivityClaims
|
||||
.AsNoTracking()
|
||||
.Where(item =>
|
||||
item.TenantId == actor.TenantId &&
|
||||
item.UserId == actor.UserId &&
|
||||
item.Status == PointActivityClaimStatus.Claimed)
|
||||
.SumAsync(item => (int?)item.Points, cancellationToken) ?? 0;
|
||||
var spent = await dbContext.PointExchangeOrders
|
||||
var spent = await pointsPersistence.PointExchangeOrders
|
||||
.AsNoTracking()
|
||||
.Where(item =>
|
||||
item.TenantId == actor.TenantId &&
|
||||
@@ -288,7 +291,7 @@ public sealed class PointService(TikuDbContext dbContext) : IPointService
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var days = item.Days ?? ReadInt(item.FulfillmentPayload, "days") ?? 0;
|
||||
var current = await dbContext.Entitlements
|
||||
var current = await commercePersistence.Entitlements
|
||||
.Where(entry =>
|
||||
entry.TenantId == actor.TenantId &&
|
||||
entry.UserId == actor.UserId &&
|
||||
@@ -298,7 +301,7 @@ public sealed class PointService(TikuDbContext dbContext) : IPointService
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (current is null)
|
||||
{
|
||||
dbContext.Entitlements.Add(new Entitlement
|
||||
commercePersistence.Entitlements.Add(new Entitlement
|
||||
{
|
||||
TenantId = actor.TenantId,
|
||||
UserId = actor.UserId,
|
||||
@@ -324,7 +327,7 @@ public sealed class PointService(TikuDbContext dbContext) : IPointService
|
||||
|
||||
private async Task AssertActiveMemberAsync(PointActor actor, CancellationToken cancellationToken)
|
||||
{
|
||||
var exists = await dbContext.TenantMemberships.AnyAsync(item =>
|
||||
var exists = await identityPersistence.TenantMemberships.AnyAsync(item =>
|
||||
item.TenantId == actor.TenantId &&
|
||||
item.UserId == actor.UserId &&
|
||||
item.Status == MembershipStatus.Active,
|
||||
|
||||
Reference in New Issue
Block a user