feat: 完成租户个人中心 API 首版实现
This commit is contained in:
@@ -56,6 +56,7 @@ public static class AppServiceCollectionExtensions
|
||||
services.AddScoped<ITenantPackageRepository, EfTenantPackageRepository>();
|
||||
services.AddScoped<ITenantQuotaUsageRepository, EfTenantQuotaUsageRepository>();
|
||||
services.AddScoped<ITenantQuotaUsageHistoryRepository, EfTenantQuotaUsageHistoryRepository>();
|
||||
services.AddScoped<ITenantVisibilityRoleRuleRepository, TenantVisibilityRoleRuleRepository>();
|
||||
services.AddScoped<IInventoryRepository, EfInventoryRepository>();
|
||||
services.AddScoped<IQuotaPackageRepository, EfQuotaPackageRepository>();
|
||||
services.AddScoped<IStatisticsRepository, EfStatisticsRepository>();
|
||||
|
||||
@@ -11,6 +11,39 @@ namespace TakeoutSaaS.Infrastructure.App.Persistence.Repositories;
|
||||
/// </summary>
|
||||
public sealed class TenantPaymentRepository(TakeoutAppDbContext context) : ITenantPaymentRepository
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<(IReadOnlyList<TenantPayment> Items, int Total)> SearchPagedAsync(
|
||||
long tenantId,
|
||||
DateTime from,
|
||||
DateTime to,
|
||||
int page,
|
||||
int pageSize,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. 归一化分页参数
|
||||
var normalizedPage = page <= 0 ? 1 : page;
|
||||
var normalizedPageSize = pageSize <= 0 ? 20 : pageSize;
|
||||
|
||||
// 2. 构建查询(按支付时间倒序)
|
||||
var query = context.TenantPayments
|
||||
.AsNoTracking()
|
||||
.Where(x => x.DeletedAt == null
|
||||
&& x.TenantId == tenantId
|
||||
&& (x.PaidAt ?? x.CreatedAt) >= from
|
||||
&& (x.PaidAt ?? x.CreatedAt) <= to);
|
||||
|
||||
// 3. 执行分页
|
||||
var total = await query.CountAsync(cancellationToken);
|
||||
var items = await query
|
||||
.OrderByDescending(x => x.PaidAt ?? x.CreatedAt)
|
||||
.Skip((normalizedPage - 1) * normalizedPageSize)
|
||||
.Take(normalizedPageSize)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// 4. 返回分页结果
|
||||
return (items, total);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<TenantPayment>> GetByBillingIdAsync(long billingStatementId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TakeoutSaaS.Domain.Tenants.Entities;
|
||||
using TakeoutSaaS.Domain.Tenants.Repositories;
|
||||
using TakeoutSaaS.Infrastructure.App.Persistence;
|
||||
|
||||
namespace TakeoutSaaS.Infrastructure.App.Persistence.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// 租户可见角色规则仓储实现。
|
||||
/// </summary>
|
||||
public sealed class TenantVisibilityRoleRuleRepository(TakeoutAppDbContext context) : ITenantVisibilityRoleRuleRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// 按租户获取规则。
|
||||
/// </summary>
|
||||
/// <param name="tenantId">租户 ID。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>规则实体或 null。</returns>
|
||||
public Task<TenantVisibilityRoleRule?> FindByTenantIdAsync(long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return context.TenantVisibilityRoleRules
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(x => x.TenantId == tenantId && x.DeletedAt == null, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增规则。
|
||||
/// </summary>
|
||||
/// <param name="rule">规则实体。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>异步任务。</returns>
|
||||
public Task AddAsync(TenantVisibilityRoleRule rule, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return context.TenantVisibilityRoleRules.AddAsync(rule, cancellationToken).AsTask();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新规则。
|
||||
/// </summary>
|
||||
/// <param name="rule">规则实体。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>异步任务。</returns>
|
||||
public Task UpdateAsync(TenantVisibilityRoleRule rule, CancellationToken cancellationToken = default)
|
||||
{
|
||||
context.TenantVisibilityRoleRules.Update(rule);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存变更。
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>异步任务。</returns>
|
||||
public Task SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,10 @@ public sealed class TakeoutAppDbContext(
|
||||
/// </summary>
|
||||
public DbSet<TenantVerificationProfile> TenantVerificationProfiles => Set<TenantVerificationProfile>();
|
||||
/// <summary>
|
||||
/// 租户账单/配额可见角色规则。
|
||||
/// </summary>
|
||||
public DbSet<TenantVisibilityRoleRule> TenantVisibilityRoleRules => Set<TenantVisibilityRoleRule>();
|
||||
/// <summary>
|
||||
/// 配额包定义。
|
||||
/// </summary>
|
||||
public DbSet<QuotaPackage> QuotaPackages => Set<QuotaPackage>();
|
||||
@@ -394,6 +398,7 @@ public sealed class TakeoutAppDbContext(
|
||||
ConfigureTenantAnnouncement(modelBuilder.Entity<TenantAnnouncement>());
|
||||
ConfigureTenantAnnouncementRead(modelBuilder.Entity<TenantAnnouncementRead>());
|
||||
ConfigureTenantVerificationProfile(modelBuilder.Entity<TenantVerificationProfile>());
|
||||
ConfigureTenantVisibilityRoleRule(modelBuilder.Entity<TenantVisibilityRoleRule>());
|
||||
ConfigureQuotaPackage(modelBuilder.Entity<QuotaPackage>());
|
||||
ConfigureTenantQuotaPackagePurchase(modelBuilder.Entity<TenantQuotaPackagePurchase>());
|
||||
ConfigureMerchantDocument(modelBuilder.Entity<MerchantDocument>());
|
||||
@@ -834,6 +839,18 @@ public sealed class TakeoutAppDbContext(
|
||||
builder.HasIndex(x => new { x.TenantId, x.Channel, x.SentAt });
|
||||
}
|
||||
|
||||
private static void ConfigureTenantVisibilityRoleRule(EntityTypeBuilder<TenantVisibilityRoleRule> builder)
|
||||
{
|
||||
builder.ToTable("tenant_visibility_role_rules");
|
||||
builder.HasKey(x => x.Id);
|
||||
builder.Property(x => x.TenantId).IsRequired();
|
||||
builder.Property(x => x.QuotaVisibleRoleCodes).HasColumnType("text[]");
|
||||
builder.Property(x => x.BillingVisibleRoleCodes).HasColumnType("text[]");
|
||||
builder.Property(x => x.UpdatedBy).IsRequired();
|
||||
builder.Property(x => x.UpdatedAt).IsRequired();
|
||||
builder.HasIndex(x => x.TenantId).IsUnique();
|
||||
}
|
||||
|
||||
private static void ConfigureTenantAnnouncement(EntityTypeBuilder<TenantAnnouncement> builder)
|
||||
{
|
||||
builder.ToTable("tenant_announcements");
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TakeoutSaaS.Domain.Tenants.Entities;
|
||||
using TakeoutSaaS.Domain.Tenants.Repositories;
|
||||
using TakeoutSaaS.Infrastructure.Logs.Persistence;
|
||||
@@ -9,6 +10,44 @@ namespace TakeoutSaaS.Infrastructure.Logs.Repositories;
|
||||
/// </summary>
|
||||
public sealed class EfOperationLogRepository(TakeoutLogsDbContext logsContext) : IOperationLogRepository
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<(IReadOnlyList<OperationLog> Items, int Total)> SearchByOperatorPagedAsync(
|
||||
long tenantId,
|
||||
string operatorId,
|
||||
DateTime from,
|
||||
DateTime to,
|
||||
int page,
|
||||
int pageSize,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. 归一化参数
|
||||
var normalizedOperatorId = operatorId.Trim();
|
||||
var normalizedPage = page <= 0 ? 1 : page;
|
||||
var normalizedPageSize = pageSize <= 0 ? 50 : pageSize;
|
||||
|
||||
// 2. 构建查询(操作人 + 时间窗 + 租户约束)
|
||||
var query = logsContext.OperationLogs
|
||||
.AsNoTracking()
|
||||
.Where(x => x.DeletedAt == null
|
||||
&& x.OperatorId == normalizedOperatorId
|
||||
&& x.CreatedAt >= from
|
||||
&& x.CreatedAt <= to
|
||||
&& x.Parameters != null
|
||||
&& (EF.Functions.ILike(x.Parameters, $"%\"tenantId\":{tenantId}%")
|
||||
|| EF.Functions.ILike(x.Parameters, $"%\"TenantId\":{tenantId}%")));
|
||||
|
||||
// 3. 查询总数与分页项
|
||||
var total = await query.CountAsync(cancellationToken);
|
||||
var items = await query
|
||||
.OrderByDescending(x => x.CreatedAt)
|
||||
.Skip((normalizedPage - 1) * normalizedPageSize)
|
||||
.Take(normalizedPageSize)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// 4. 返回分页结果
|
||||
return (items, total);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddAsync(OperationLog log, CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user