feat: 增强仓储CRUD与种子配置
This commit is contained in:
@@ -30,6 +30,24 @@
|
||||
{ "Key": "new", "Value": "新店", "SortOrder": 20 }
|
||||
]
|
||||
}
|
||||
],
|
||||
"SystemParameters": [
|
||||
{ "Key": "site_name", "Value": "外卖SaaS Demo", "Description": "演示环境站点名称", "SortOrder": 10, "IsEnabled": true },
|
||||
{ "Key": "order_auto_cancel_minutes", "Value": "30", "Description": "待支付自动取消时间(分钟)", "SortOrder": 20, "IsEnabled": true }
|
||||
]
|
||||
}
|
||||
},
|
||||
"Identity": {
|
||||
"AdminSeed": {
|
||||
"Users": [
|
||||
{
|
||||
"Account": "admin",
|
||||
"DisplayName": "平台管理员",
|
||||
"Password": "Admin@123456",
|
||||
"TenantId": 1000000000001,
|
||||
"Roles": [ "PlatformAdmin" ],
|
||||
"Permissions": [ "merchant:*", "store:*", "product:*", "order:*", "payment:*", "delivery:*" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,4 +39,14 @@ public interface IDeliveryRepository
|
||||
/// 持久化变更。
|
||||
/// </summary>
|
||||
Task SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 更新配送单。
|
||||
/// </summary>
|
||||
Task UpdateDeliveryOrderAsync(DeliveryOrder deliveryOrder, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 删除配送单及事件。
|
||||
/// </summary>
|
||||
Task DeleteDeliveryOrderAsync(long deliveryOrderId, long tenantId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@@ -60,4 +60,14 @@ public interface IMerchantRepository
|
||||
/// 持久化变更。
|
||||
/// </summary>
|
||||
Task SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 更新商户信息。
|
||||
/// </summary>
|
||||
Task UpdateMerchantAsync(Merchant merchant, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 删除商户。
|
||||
/// </summary>
|
||||
Task DeleteMerchantAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@@ -66,4 +66,14 @@ public interface IOrderRepository
|
||||
/// 持久化变更。
|
||||
/// </summary>
|
||||
Task SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 更新订单。
|
||||
/// </summary>
|
||||
Task UpdateOrderAsync(Order order, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 删除订单。
|
||||
/// </summary>
|
||||
Task DeleteOrderAsync(long orderId, long tenantId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@@ -39,4 +39,14 @@ public interface IPaymentRepository
|
||||
/// 持久化变更。
|
||||
/// </summary>
|
||||
Task SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 更新支付记录。
|
||||
/// </summary>
|
||||
Task UpdatePaymentAsync(PaymentRecord payment, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 删除支付记录及关联退款。
|
||||
/// </summary>
|
||||
Task DeletePaymentAsync(long paymentId, long tenantId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@@ -100,4 +100,49 @@ public interface IProductRepository
|
||||
/// 持久化变更。
|
||||
/// </summary>
|
||||
Task SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 更新商品。
|
||||
/// </summary>
|
||||
Task UpdateProductAsync(Product product, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 删除商品。
|
||||
/// </summary>
|
||||
Task DeleteProductAsync(long productId, long tenantId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 更新分类。
|
||||
/// </summary>
|
||||
Task UpdateCategoryAsync(ProductCategory category, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 删除分类。
|
||||
/// </summary>
|
||||
Task DeleteCategoryAsync(long categoryId, long tenantId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 删除商品下的 SKU。
|
||||
/// </summary>
|
||||
Task RemoveSkusAsync(long productId, long tenantId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 删除商品下的加料组及选项。
|
||||
/// </summary>
|
||||
Task RemoveAddonGroupsAsync(long productId, long tenantId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 删除商品下的规格组及选项。
|
||||
/// </summary>
|
||||
Task RemoveAttributeGroupsAsync(long productId, long tenantId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 删除商品媒资。
|
||||
/// </summary>
|
||||
Task RemoveMediaAssetsAsync(long productId, long tenantId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 删除商品定价规则。
|
||||
/// </summary>
|
||||
Task RemovePricingRulesAsync(long productId, long tenantId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@@ -90,4 +90,14 @@ public interface IStoreRepository
|
||||
/// 持久化变更。
|
||||
/// </summary>
|
||||
Task SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 更新门店。
|
||||
/// </summary>
|
||||
Task UpdateStoreAsync(Store store, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 删除门店。
|
||||
/// </summary>
|
||||
Task DeleteStoreAsync(long storeId, long tenantId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@@ -26,4 +26,9 @@ public sealed class AppSeedOptions
|
||||
/// 基础字典分组。
|
||||
/// </summary>
|
||||
public List<DictionarySeedGroupOptions> DictionaryGroups { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 系统参数配置。
|
||||
/// </summary>
|
||||
public List<SystemParameterSeedOptions> SystemParameters { get; set; } = new();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace TakeoutSaaS.Infrastructure.App.Options;
|
||||
|
||||
/// <summary>
|
||||
/// 系统参数种子配置项。
|
||||
/// </summary>
|
||||
public sealed class SystemParameterSeedOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// 目标租户,null 时使用默认租户或 0。
|
||||
/// </summary>
|
||||
public long? TenantId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 参数键。
|
||||
/// </summary>
|
||||
public string Key { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 参数值。
|
||||
/// </summary>
|
||||
public string Value { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 说明。
|
||||
/// </summary>
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 排序。
|
||||
/// </summary>
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用。
|
||||
/// </summary>
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using TakeoutSaaS.Domain.Dictionary.Entities;
|
||||
using TakeoutSaaS.Domain.Dictionary.Enums;
|
||||
using TakeoutSaaS.Domain.Tenants.Entities;
|
||||
using TakeoutSaaS.Domain.Tenants.Enums;
|
||||
using TakeoutSaaS.Infrastructure.App.Options;
|
||||
@@ -142,81 +144,147 @@ public sealed class AppDataSeeder : IHostedService
|
||||
/// </summary>
|
||||
private async Task EnsureDictionarySeedsAsync(DictionaryDbContext dbContext, long? defaultTenantId, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_options.DictionaryGroups == null || _options.DictionaryGroups.Count == 0)
|
||||
var dictionaryGroups = _options.DictionaryGroups ?? new List<DictionarySeedGroupOptions>();
|
||||
var hasDictionaryGroups = dictionaryGroups.Count > 0;
|
||||
|
||||
if (!hasDictionaryGroups)
|
||||
{
|
||||
_logger.LogInformation("AppSeed 未配置基础字典,跳过字典种子");
|
||||
}
|
||||
|
||||
if (hasDictionaryGroups)
|
||||
{
|
||||
foreach (var groupOptions in dictionaryGroups)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(groupOptions.Code) || string.IsNullOrWhiteSpace(groupOptions.Name))
|
||||
{
|
||||
_logger.LogWarning("AppSeed 跳过字典分组,Code 或 Name 为空");
|
||||
continue;
|
||||
}
|
||||
|
||||
var tenantId = groupOptions.TenantId ?? defaultTenantId ?? 0;
|
||||
var code = groupOptions.Code.Trim();
|
||||
|
||||
var group = await dbContext.DictionaryGroups
|
||||
.IgnoreQueryFilters()
|
||||
.FirstOrDefaultAsync(x => x.TenantId == tenantId && x.Code == code, cancellationToken);
|
||||
|
||||
if (group == null)
|
||||
{
|
||||
group = new DictionaryGroup
|
||||
{
|
||||
Id = 0,
|
||||
TenantId = tenantId,
|
||||
Code = code,
|
||||
Name = groupOptions.Name.Trim(),
|
||||
Scope = groupOptions.Scope,
|
||||
Description = groupOptions.Description?.Trim(),
|
||||
IsEnabled = groupOptions.IsEnabled
|
||||
};
|
||||
|
||||
await dbContext.DictionaryGroups.AddAsync(group, cancellationToken);
|
||||
_logger.LogInformation("AppSeed 创建字典分组 {GroupCode} (Tenant: {TenantId})", code, tenantId);
|
||||
}
|
||||
else
|
||||
{
|
||||
var groupUpdated = false;
|
||||
|
||||
if (!string.Equals(group.Name, groupOptions.Name, StringComparison.Ordinal))
|
||||
{
|
||||
group.Name = groupOptions.Name.Trim();
|
||||
groupUpdated = true;
|
||||
}
|
||||
|
||||
if (!string.Equals(group.Description, groupOptions.Description, StringComparison.Ordinal))
|
||||
{
|
||||
group.Description = groupOptions.Description?.Trim();
|
||||
groupUpdated = true;
|
||||
}
|
||||
|
||||
if (group.Scope != groupOptions.Scope)
|
||||
{
|
||||
group.Scope = groupOptions.Scope;
|
||||
groupUpdated = true;
|
||||
}
|
||||
|
||||
if (group.IsEnabled != groupOptions.IsEnabled)
|
||||
{
|
||||
group.IsEnabled = groupOptions.IsEnabled;
|
||||
groupUpdated = true;
|
||||
}
|
||||
|
||||
if (groupUpdated)
|
||||
{
|
||||
dbContext.DictionaryGroups.Update(group);
|
||||
}
|
||||
}
|
||||
|
||||
await UpsertDictionaryItemsAsync(dbContext, group, groupOptions.Items, tenantId, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
await EnsureSystemParametersAsync(dbContext, defaultTenantId, cancellationToken);
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确保系统参数以字典形式幂等种子。
|
||||
/// </summary>
|
||||
private async Task EnsureSystemParametersAsync(DictionaryDbContext dbContext, long? defaultTenantId, CancellationToken cancellationToken)
|
||||
{
|
||||
var systemParameters = _options.SystemParameters ?? new List<SystemParameterSeedOptions>();
|
||||
|
||||
if (systemParameters.Count == 0)
|
||||
{
|
||||
_logger.LogInformation("AppSeed 未配置系统参数,跳过系统参数种子");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var groupOptions in _options.DictionaryGroups)
|
||||
var grouped = systemParameters
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.Key) && !string.IsNullOrWhiteSpace(x.Value))
|
||||
.GroupBy(x => x.TenantId ?? defaultTenantId ?? 0);
|
||||
|
||||
if (!grouped.Any())
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(groupOptions.Code) || string.IsNullOrWhiteSpace(groupOptions.Name))
|
||||
{
|
||||
_logger.LogWarning("AppSeed 跳过字典分组,Code 或 Name 为空");
|
||||
continue;
|
||||
}
|
||||
_logger.LogInformation("AppSeed 系统参数配置为空,跳过系统参数种子");
|
||||
return;
|
||||
}
|
||||
|
||||
var tenantId = groupOptions.TenantId ?? defaultTenantId ?? 0;
|
||||
var code = groupOptions.Code.Trim();
|
||||
|
||||
var group = await dbContext.DictionaryGroups
|
||||
foreach (var group in grouped)
|
||||
{
|
||||
var tenantId = group.Key;
|
||||
var dictionaryGroup = await dbContext.DictionaryGroups
|
||||
.IgnoreQueryFilters()
|
||||
.FirstOrDefaultAsync(x => x.TenantId == tenantId && x.Code == code, cancellationToken);
|
||||
.FirstOrDefaultAsync(x => x.TenantId == tenantId && x.Code == "system_parameters", cancellationToken);
|
||||
|
||||
if (group == null)
|
||||
if (dictionaryGroup == null)
|
||||
{
|
||||
group = new DictionaryGroup
|
||||
dictionaryGroup = new DictionaryGroup
|
||||
{
|
||||
Id = 0,
|
||||
TenantId = tenantId,
|
||||
Code = code,
|
||||
Name = groupOptions.Name.Trim(),
|
||||
Scope = groupOptions.Scope,
|
||||
Description = groupOptions.Description?.Trim(),
|
||||
IsEnabled = groupOptions.IsEnabled
|
||||
Code = "system_parameters",
|
||||
Name = "系统参数",
|
||||
Scope = tenantId == 0 ? DictionaryScope.System : DictionaryScope.Business,
|
||||
Description = "系统参数配置",
|
||||
IsEnabled = true
|
||||
};
|
||||
|
||||
await dbContext.DictionaryGroups.AddAsync(group, cancellationToken);
|
||||
_logger.LogInformation("AppSeed 创建字典分组 {GroupCode} (Tenant: {TenantId})", code, tenantId);
|
||||
await dbContext.DictionaryGroups.AddAsync(dictionaryGroup, cancellationToken);
|
||||
_logger.LogInformation("AppSeed 创建系统参数分组 (Tenant: {TenantId})", tenantId);
|
||||
}
|
||||
else
|
||||
|
||||
var seedItems = group.Select(x => new DictionarySeedItemOptions
|
||||
{
|
||||
var groupUpdated = false;
|
||||
Key = x.Key.Trim(),
|
||||
Value = x.Value.Trim(),
|
||||
Description = x.Description?.Trim(),
|
||||
SortOrder = x.SortOrder,
|
||||
IsEnabled = x.IsEnabled
|
||||
});
|
||||
|
||||
if (!string.Equals(group.Name, groupOptions.Name, StringComparison.Ordinal))
|
||||
{
|
||||
group.Name = groupOptions.Name.Trim();
|
||||
groupUpdated = true;
|
||||
}
|
||||
|
||||
if (!string.Equals(group.Description, groupOptions.Description, StringComparison.Ordinal))
|
||||
{
|
||||
group.Description = groupOptions.Description?.Trim();
|
||||
groupUpdated = true;
|
||||
}
|
||||
|
||||
if (group.Scope != groupOptions.Scope)
|
||||
{
|
||||
group.Scope = groupOptions.Scope;
|
||||
groupUpdated = true;
|
||||
}
|
||||
|
||||
if (group.IsEnabled != groupOptions.IsEnabled)
|
||||
{
|
||||
group.IsEnabled = groupOptions.IsEnabled;
|
||||
groupUpdated = true;
|
||||
}
|
||||
|
||||
if (groupUpdated)
|
||||
{
|
||||
dbContext.DictionaryGroups.Update(group);
|
||||
}
|
||||
}
|
||||
|
||||
await UpsertDictionaryItemsAsync(dbContext, group, groupOptions.Items, tenantId, cancellationToken);
|
||||
await UpsertDictionaryItemsAsync(dbContext, dictionaryGroup, seedItems, tenantId, cancellationToken);
|
||||
}
|
||||
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -68,4 +68,35 @@ public sealed class EfDeliveryRepository : IDeliveryRepository
|
||||
{
|
||||
return _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task UpdateDeliveryOrderAsync(DeliveryOrder deliveryOrder, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.DeliveryOrders.Update(deliveryOrder);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task DeleteDeliveryOrderAsync(long deliveryOrderId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var events = await _context.DeliveryEvents
|
||||
.Where(x => x.TenantId == tenantId && x.DeliveryOrderId == deliveryOrderId)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (events.Count > 0)
|
||||
{
|
||||
_context.DeliveryEvents.RemoveRange(events);
|
||||
}
|
||||
|
||||
var existing = await _context.DeliveryOrders
|
||||
.Where(x => x.TenantId == tenantId && x.Id == deliveryOrderId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (existing == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_context.DeliveryOrders.Remove(existing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,4 +113,26 @@ public sealed class EfMerchantRepository : IMerchantRepository
|
||||
{
|
||||
return _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task UpdateMerchantAsync(Merchant merchant, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.Merchants.Update(merchant);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task DeleteMerchantAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var existing = await _context.Merchants
|
||||
.Where(x => x.TenantId == tenantId && x.Id == merchantId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (existing == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_context.Merchants.Remove(existing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,4 +130,49 @@ public sealed class EfOrderRepository : IOrderRepository
|
||||
{
|
||||
return _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task UpdateOrderAsync(Order order, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.Orders.Update(order);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task DeleteOrderAsync(long orderId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var items = await _context.OrderItems
|
||||
.Where(x => x.TenantId == tenantId && x.OrderId == orderId)
|
||||
.ToListAsync(cancellationToken);
|
||||
if (items.Count > 0)
|
||||
{
|
||||
_context.OrderItems.RemoveRange(items);
|
||||
}
|
||||
|
||||
var histories = await _context.OrderStatusHistories
|
||||
.Where(x => x.TenantId == tenantId && x.OrderId == orderId)
|
||||
.ToListAsync(cancellationToken);
|
||||
if (histories.Count > 0)
|
||||
{
|
||||
_context.OrderStatusHistories.RemoveRange(histories);
|
||||
}
|
||||
|
||||
var refunds = await _context.RefundRequests
|
||||
.Where(x => x.TenantId == tenantId && x.OrderId == orderId)
|
||||
.ToListAsync(cancellationToken);
|
||||
if (refunds.Count > 0)
|
||||
{
|
||||
_context.RefundRequests.RemoveRange(refunds);
|
||||
}
|
||||
|
||||
var existing = await _context.Orders
|
||||
.Where(x => x.TenantId == tenantId && x.Id == orderId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (existing == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_context.Orders.Remove(existing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,4 +68,33 @@ public sealed class EfPaymentRepository : IPaymentRepository
|
||||
{
|
||||
return _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task UpdatePaymentAsync(PaymentRecord payment, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.PaymentRecords.Update(payment);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task DeletePaymentAsync(long paymentId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var refunds = await _context.PaymentRefundRecords
|
||||
.Where(x => x.TenantId == tenantId && x.PaymentRecordId == paymentId)
|
||||
.ToListAsync(cancellationToken);
|
||||
if (refunds.Count > 0)
|
||||
{
|
||||
_context.PaymentRefundRecords.RemoveRange(refunds);
|
||||
}
|
||||
|
||||
var existing = await _context.PaymentRecords
|
||||
.Where(x => x.TenantId == tenantId && x.Id == paymentId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (existing == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_context.PaymentRecords.Remove(existing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,4 +224,163 @@ public sealed class EfProductRepository : IProductRepository
|
||||
{
|
||||
return _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task UpdateProductAsync(Product product, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.Products.Update(product);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task DeleteProductAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await RemovePricingRulesAsync(productId, tenantId, cancellationToken);
|
||||
await RemoveMediaAssetsAsync(productId, tenantId, cancellationToken);
|
||||
await RemoveAttributeGroupsAsync(productId, tenantId, cancellationToken);
|
||||
await RemoveAddonGroupsAsync(productId, tenantId, cancellationToken);
|
||||
await RemoveSkusAsync(productId, tenantId, cancellationToken);
|
||||
|
||||
var existing = await _context.Products
|
||||
.Where(x => x.TenantId == tenantId && x.Id == productId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (existing == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_context.Products.Remove(existing);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task UpdateCategoryAsync(ProductCategory category, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.ProductCategories.Update(category);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task DeleteCategoryAsync(long categoryId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var existing = await _context.ProductCategories
|
||||
.Where(x => x.TenantId == tenantId && x.Id == categoryId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (existing == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_context.ProductCategories.Remove(existing);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task RemoveSkusAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var skus = await _context.ProductSkus
|
||||
.Where(x => x.TenantId == tenantId && x.ProductId == productId)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (skus.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_context.ProductSkus.RemoveRange(skus);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task RemoveAddonGroupsAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var groupIds = await _context.ProductAddonGroups
|
||||
.Where(x => x.TenantId == tenantId && x.ProductId == productId)
|
||||
.Select(x => x.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (groupIds.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var options = await _context.ProductAddonOptions
|
||||
.Where(x => x.TenantId == tenantId && groupIds.Contains(x.AddonGroupId))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (options.Count > 0)
|
||||
{
|
||||
_context.ProductAddonOptions.RemoveRange(options);
|
||||
}
|
||||
|
||||
var groups = await _context.ProductAddonGroups
|
||||
.Where(x => groupIds.Contains(x.Id))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (groups.Count > 0)
|
||||
{
|
||||
_context.ProductAddonGroups.RemoveRange(groups);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task RemoveAttributeGroupsAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var groupIds = await _context.ProductAttributeGroups
|
||||
.Where(x => x.TenantId == tenantId && x.ProductId == productId)
|
||||
.Select(x => x.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (groupIds.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var options = await _context.ProductAttributeOptions
|
||||
.Where(x => x.TenantId == tenantId && groupIds.Contains(x.AttributeGroupId))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (options.Count > 0)
|
||||
{
|
||||
_context.ProductAttributeOptions.RemoveRange(options);
|
||||
}
|
||||
|
||||
var groups = await _context.ProductAttributeGroups
|
||||
.Where(x => groupIds.Contains(x.Id))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (groups.Count > 0)
|
||||
{
|
||||
_context.ProductAttributeGroups.RemoveRange(groups);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task RemoveMediaAssetsAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var assets = await _context.ProductMediaAssets
|
||||
.Where(x => x.TenantId == tenantId && x.ProductId == productId)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (assets.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_context.ProductMediaAssets.RemoveRange(assets);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task RemovePricingRulesAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var rules = await _context.ProductPricingRules
|
||||
.Where(x => x.TenantId == tenantId && x.ProductId == productId)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (rules.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_context.ProductPricingRules.RemoveRange(rules);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,4 +171,26 @@ public sealed class EfStoreRepository : IStoreRepository
|
||||
{
|
||||
return _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task UpdateStoreAsync(Store store, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.Stores.Update(store);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task DeleteStoreAsync(long storeId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var existing = await _context.Stores
|
||||
.Where(x => x.TenantId == tenantId && x.Id == storeId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (existing == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_context.Stores.Remove(existing);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user