using System.Linq.Expressions; using Fake.Application; using Microsoft.AspNetCore.Mvc; using Oms.Application.Dtos.Orders; using Oms.Application.Dtos.Shared; using Oms.Application.Shared; using Oms.Domain.OrdersAggregate.Entities; using Oms.Domain.OrdersAggregate.Repositories; namespace Oms.Application.Services.Orders; public partial class OrderService { private ICustomerSenderAddressRepository CustomerSenderAddressRepository => LazyServiceProvider.GetRequiredService(); /// /// 发件地址分页列表。 /// [HttpPost("/order/sender-address/list")] public async Task> GetSenderAddressListAsync( [FromBody] CustomerSenderAddressPaginatedInput input) { var customer = await OmsSessionHelper.ResolveSessionCustomerAsync(CustomerRepository, CurrentUser); var pageIndex = OmsDocHelper.NormalizePage(input.Page); var pageSize = OmsDocHelper.NormalizePageSize(input.PageSize); var predicate = BuildSenderAddressPredicate(customer.Id, input); var entities = await CustomerSenderAddressRepository.GetPagedListAsync( predicate, pageIndex, pageSize, new Dictionary { [nameof(CustomerSenderAddress.IsDefault)] = false, [nameof(CustomerSenderAddress.Id)] = false }); var total = await CustomerSenderAddressRepository.CountAsync(predicate); var countryMap = await LoadCountryCodeMapAsync(entities.Select(x => x.Country).Where(x => x.HasValue).Select(x => x!.Value)); return new OmsPageResult { Items = entities.Select(x => MapSenderAddressOutput(x, countryMap)).ToList(), Total = total }; } /// /// 发件地址详情。 /// [HttpGet("/order/sender-address/detail")] public async Task GetSenderAddressDetailAsync([FromQuery] int id) { var entity = await GetOwnedSenderAddressOrThrowAsync(id); var countryMap = await LoadCountryCodeMapAsync(entity.Country.HasValue ? [entity.Country.Value] : []); return MapSenderAddressOutput(entity, countryMap); } /// /// 新增发件地址。 /// [HttpPost("/order/sender-address/create")] public async Task CreateSenderAddressAsync([FromBody] CreateCustomerSenderAddressInput input) { var customer = await OmsSessionHelper.ResolveSessionCustomerAsync(CustomerRepository, CurrentUser); ValidateSenderAddressInput(input); var countryId = await ResolveSenderCountryIdAsync(input.CountryCode); if (input.IsDefault) { await CustomerSenderAddressRepository.ClearDefaultAsync(customer.Id); } var entity = new CustomerSenderAddress { CustomerId = customer.Id, AddressName = TrimOrNull(input.AddressName), SenderName = input.SenderName.Trim(), Company = TrimOrNull(input.Company), Country = countryId, Province = TrimOrNull(input.Province), City = TrimOrNull(input.City), ZipCode = TrimOrNull(input.ZipCode), StreetAddress1 = input.StreetAddress1.Trim(), StreetAddress2 = TrimOrNull(input.StreetAddress2), StreetAddress3 = TrimOrNull(input.StreetAddress3), HouseNumber = TrimOrNull(input.HouseNumber), Phone = TrimOrNull(input.Phone), TelePhone = TrimOrNull(input.TelePhone), Email = TrimOrNull(input.Email), IsDefault = input.IsDefault ? (byte)1 : (byte)0 }; entity = await CustomerSenderAddressRepository.InsertAsync(entity); return new { id = entity.Id }; } /// /// 更新发件地址。 /// [HttpPost("/order/sender-address/update")] public async Task UpdateSenderAddressAsync([FromBody] UpdateCustomerSenderAddressInput input) { var entity = await GetOwnedSenderAddressOrThrowAsync(input.Id); ValidateSenderAddressInput(input); var countryId = await ResolveSenderCountryIdAsync(input.CountryCode); if (input.IsDefault) { await CustomerSenderAddressRepository.ClearDefaultAsync(entity.CustomerId, entity.Id); } entity.AddressName = TrimOrNull(input.AddressName); entity.SenderName = input.SenderName.Trim(); entity.Company = TrimOrNull(input.Company); entity.Country = countryId; entity.Province = TrimOrNull(input.Province); entity.City = TrimOrNull(input.City); entity.ZipCode = TrimOrNull(input.ZipCode); entity.StreetAddress1 = input.StreetAddress1.Trim(); entity.StreetAddress2 = TrimOrNull(input.StreetAddress2); entity.StreetAddress3 = TrimOrNull(input.StreetAddress3); entity.HouseNumber = TrimOrNull(input.HouseNumber); entity.Phone = TrimOrNull(input.Phone); entity.TelePhone = TrimOrNull(input.TelePhone); entity.Email = TrimOrNull(input.Email); entity.IsDefault = input.IsDefault ? (byte)1 : (byte)0; await CustomerSenderAddressRepository.UpdateAsync(entity); } /// /// 删除发件地址(软删)。 /// [HttpPost("/order/sender-address/delete")] public async Task DeleteSenderAddressAsync([FromBody] IdInput input) { var entity = await GetOwnedSenderAddressOrThrowAsync(input.Id); await CustomerSenderAddressRepository.DeleteAsync(entity); } /// /// 设为默认发件地址。 /// [HttpPost("/order/sender-address/set-default")] public async Task SetDefaultSenderAddressAsync([FromBody] IdInput input) { var entity = await GetOwnedSenderAddressOrThrowAsync(input.Id); await CustomerSenderAddressRepository.ClearDefaultAsync(entity.CustomerId, entity.Id); entity.IsDefault = 1; await CustomerSenderAddressRepository.UpdateAsync(entity); } private async Task GetOwnedSenderAddressOrThrowAsync(int id) { var customer = await OmsSessionHelper.ResolveSessionCustomerAsync(CustomerRepository, CurrentUser); var entity = await CustomerSenderAddressRepository.GetOwnedAsync(id, customer.Id); if (entity == null) { throw new BusinessException("发件地址不存在"); } return entity; } private static void ValidateSenderAddressInput(CreateCustomerSenderAddressInput input) { if (string.IsNullOrWhiteSpace(input.SenderName)) { throw new BusinessException("发件人姓名不能为空"); } if (string.IsNullOrWhiteSpace(input.StreetAddress1)) { throw new BusinessException("发件地址不能为空"); } } private async Task ResolveSenderCountryIdAsync(string? countryCode) { var code = string.IsNullOrWhiteSpace(countryCode) ? "CN" : countryCode.Trim(); var country = await GetCountryOrThrowAsync(code, "发件国家不存在"); return country.Id; } private static Expression> BuildSenderAddressPredicate( int customerId, CustomerSenderAddressPaginatedInput input) { var keyword = input.Keyword?.Trim(); var onlyDefault = input.OnlyDefault; return x => x.CustomerId == customerId && (!onlyDefault || x.IsDefault == 1) && (string.IsNullOrWhiteSpace(keyword) || (x.AddressName ?? string.Empty).Contains(keyword) || x.SenderName.Contains(keyword) || (x.Phone ?? string.Empty).Contains(keyword) || x.StreetAddress1.Contains(keyword)); } private async Task> LoadCountryCodeMapAsync(IEnumerable countryIds) { var ids = countryIds.Distinct().ToArray(); if (ids.Length == 0) { return new Dictionary(); } var countries = await CountryRepository.GetListAsync(x => ids.Contains(x.Id)); return countries.ToDictionary(x => x.Id, x => x.CountryCode ?? string.Empty); } private static CustomerSenderAddressOutput MapSenderAddressOutput( CustomerSenderAddress entity, IReadOnlyDictionary countryMap) { string? countryCode = null; if (entity.Country.HasValue && countryMap.TryGetValue(entity.Country.Value, out var code)) { countryCode = code; } return new CustomerSenderAddressOutput { Id = entity.Id, CustomerId = entity.CustomerId, AddressName = entity.AddressName, SenderName = entity.SenderName, Company = entity.Company, Country = entity.Country, CountryCode = countryCode, Province = entity.Province, City = entity.City, ZipCode = entity.ZipCode, StreetAddress1 = entity.StreetAddress1, StreetAddress2 = entity.StreetAddress2, StreetAddress3 = entity.StreetAddress3, HouseNumber = entity.HouseNumber, Phone = entity.Phone, TelePhone = entity.TelePhone, Email = entity.Email, IsDefault = entity.IsDefault > 0, CreateTime = entity.CreateTime, UpdateTime = OmsDocHelper.NullIfDefault(entity.UpdateTime) }; } private static string? TrimOrNull(string? value) { return string.IsNullOrWhiteSpace(value) ? null : value.Trim(); } }