using MediatR; using System.Security.Cryptography; using TakeoutSaaS.Application.App.Stores.Commands; using TakeoutSaaS.Application.App.Stores.Enums; using TakeoutSaaS.Application.App.Stores.Services; using TakeoutSaaS.Domain.Stores.Entities; using TakeoutSaaS.Domain.Stores.Enums; using TakeoutSaaS.Domain.Stores.Repositories; using TakeoutSaaS.Shared.Abstractions.Constants; using TakeoutSaaS.Shared.Abstractions.Exceptions; namespace TakeoutSaaS.Application.App.Stores.Handlers; /// /// 租户端创建门店命令处理器。 /// public sealed class CreateStoreCommandHandler( StoreContextService storeContextService, IStoreRepository storeRepository) : IRequestHandler { /// public async Task Handle(CreateStoreCommand request, CancellationToken cancellationToken) { // 1. 解析上下文 var context = storeContextService.GetRequiredContext(); // 2. 生成唯一门店编码 var existingStores = await storeRepository.SearchAsync( context.TenantId, context.MerchantId, status: null, auditStatus: null, businessStatus: null, ownershipType: null, keyword: null, includeDeleted: false, cancellationToken: cancellationToken); var generatedCode = GenerateUniqueStoreCode(existingStores); // 3. 组装门店实体 var serviceTypes = request.ServiceTypes?.Count > 0 ? request.ServiceTypes : [ServiceType.Delivery]; var store = new Store { TenantId = context.TenantId, MerchantId = context.MerchantId, Code = generatedCode, Name = request.Name.Trim(), Phone = request.ContactPhone.Trim(), ManagerName = request.ManagerName.Trim(), Address = request.Address.Trim(), CoverImageUrl = request.CoverImage?.Trim(), SignboardImageUrl = request.CoverImage?.Trim(), OwnershipType = StoreOwnershipType.SameEntity, AuditStatus = StoreAuditStatus.Draft, BusinessStatus = request.BusinessStatus ?? StoreBusinessStatus.Open, Status = StoreStatus.Operating }; StoreListMapping.ApplyServiceTypes(store, serviceTypes); // 4. 持久化 await storeRepository.AddStoreAsync(store, cancellationToken); await storeRepository.SaveChangesAsync(cancellationToken); } /// /// 生成当前租户商户内唯一的门店编码。 /// private static string GenerateUniqueStoreCode(IReadOnlyList existingStores) { var existingCodeSet = existingStores .Select(store => store.Code.Trim()) .Where(code => !string.IsNullOrWhiteSpace(code)) .ToHashSet(StringComparer.OrdinalIgnoreCase); const int maxRetryCount = 20; for (var attempt = 0; attempt < maxRetryCount; attempt++) { var candidate = $"ST{DateTime.UtcNow:yyMMddHHmmss}{RandomNumberGenerator.GetInt32(100, 1000)}"; if (existingCodeSet.Add(candidate)) { return candidate; } } throw new BusinessException(ErrorCodes.Conflict, "门店编码生成失败,请稍后重试"); } }