feat:商户管理

This commit is contained in:
2025-12-29 16:40:27 +08:00
parent 57f4c2d394
commit dd91c1010a
62 changed files with 10536 additions and 165 deletions

View File

@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
using TakeoutSaaS.Domain.Common.Enums;
using TakeoutSaaS.Domain.Merchants.Entities;
using TakeoutSaaS.Domain.Merchants.Enums;
using TakeoutSaaS.Domain.Merchants.Repositories;
@@ -24,6 +25,26 @@ public sealed class EfMerchantRepository(TakeoutAppDbContext context, TakeoutLog
.FirstOrDefaultAsync(cancellationToken);
}
/// <inheritdoc />
public Task<Merchant?> FindByIdAsync(long merchantId, CancellationToken cancellationToken = default)
{
return context.Merchants
.IgnoreQueryFilters()
.AsNoTracking()
.Where(x => x.Id == merchantId)
.FirstOrDefaultAsync(cancellationToken);
}
/// <inheritdoc />
public Task<Merchant?> FindByTenantIdAsync(long tenantId, CancellationToken cancellationToken = default)
{
return context.Merchants
.IgnoreQueryFilters()
.AsNoTracking()
.Where(x => x.TenantId == tenantId)
.FirstOrDefaultAsync(cancellationToken);
}
/// <inheritdoc />
public async Task<IReadOnlyList<Merchant>> SearchAsync(long tenantId, MerchantStatus? status, CancellationToken cancellationToken = default)
{
@@ -208,9 +229,79 @@ public sealed class EfMerchantRepository(TakeoutAppDbContext context, TakeoutLog
public async Task<IReadOnlyList<MerchantAuditLog>> GetAuditLogsAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default)
{
return await logsContext.MerchantAuditLogs
.IgnoreQueryFilters()
.AsNoTracking()
.Where(x => x.TenantId == tenantId && x.MerchantId == merchantId)
.OrderByDescending(x => x.CreatedAt)
.ToListAsync(cancellationToken);
}
/// <inheritdoc />
public async Task<IReadOnlyList<Merchant>> SearchAsync(
long? tenantId,
MerchantStatus? status,
OperatingMode? operatingMode,
string? keyword,
CancellationToken cancellationToken = default)
{
var query = context.Merchants
.IgnoreQueryFilters()
.AsNoTracking()
.AsQueryable();
if (tenantId.HasValue && tenantId.Value > 0)
{
query = query.Where(x => x.TenantId == tenantId.Value);
}
if (status.HasValue)
{
query = query.Where(x => x.Status == status.Value);
}
if (operatingMode.HasValue)
{
query = query.Where(x => x.OperatingMode == operatingMode.Value);
}
if (!string.IsNullOrWhiteSpace(keyword))
{
var normalized = keyword.Trim();
query = query.Where(x =>
EF.Functions.ILike(x.BrandName, $"%{normalized}%") ||
EF.Functions.ILike(x.BusinessLicenseNumber ?? string.Empty, $"%{normalized}%"));
}
return await query
.OrderByDescending(x => x.CreatedAt)
.ToListAsync(cancellationToken);
}
/// <inheritdoc />
public Task AddChangeLogAsync(MerchantChangeLog log, CancellationToken cancellationToken = default)
{
return logsContext.MerchantChangeLogs.AddAsync(log, cancellationToken).AsTask();
}
/// <inheritdoc />
public async Task<IReadOnlyList<MerchantChangeLog>> GetChangeLogsAsync(
long merchantId,
long tenantId,
string? fieldName = null,
CancellationToken cancellationToken = default)
{
var query = logsContext.MerchantChangeLogs
.IgnoreQueryFilters()
.AsNoTracking()
.Where(x => x.TenantId == tenantId && x.MerchantId == merchantId);
if (!string.IsNullOrWhiteSpace(fieldName))
{
query = query.Where(x => x.FieldName == fieldName);
}
return await query
.OrderByDescending(x => x.CreatedAt)
.ToListAsync(cancellationToken);
}
}

View File

@@ -25,6 +25,16 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos
.FirstOrDefaultAsync(cancellationToken);
}
/// <inheritdoc />
public async Task<IReadOnlyList<Store>> GetByMerchantIdAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default)
{
return await context.Stores
.AsNoTracking()
.Where(x => x.TenantId == tenantId && x.MerchantId == merchantId)
.OrderBy(x => x.Name)
.ToListAsync(cancellationToken);
}
/// <inheritdoc />
public async Task<IReadOnlyList<Store>> SearchAsync(long tenantId, StoreStatus? status, CancellationToken cancellationToken = default)
{
@@ -44,6 +54,31 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos
return stores;
}
/// <inheritdoc />
public async Task<Dictionary<long, int>> GetStoreCountsAsync(long? tenantId, IReadOnlyCollection<long> merchantIds, CancellationToken cancellationToken = default)
{
if (merchantIds.Count == 0)
{
return new Dictionary<long, int>();
}
var query = context.Stores.AsNoTracking();
if (!tenantId.HasValue || tenantId.Value <= 0)
{
query = query.IgnoreQueryFilters();
}
else
{
query = query.Where(x => x.TenantId == tenantId.Value);
}
return await query
.Where(x => merchantIds.Contains(x.MerchantId))
.GroupBy(x => x.MerchantId)
.Select(group => new { group.Key, Count = group.Count() })
.ToDictionaryAsync(x => x.Key, x => x.Count, cancellationToken);
}
/// <inheritdoc />
public async Task<IReadOnlyList<StoreBusinessHour>> GetBusinessHoursAsync(long storeId, long tenantId, CancellationToken cancellationToken = default)
{