using System.Linq.Expressions; using Fake.Application; using Microsoft.AspNetCore.Mvc; using Oms.Application.Dtos.PlatformStore; using Oms.Application.Dtos.Shared; using Oms.Application.Shared; using Oms.Domain.CustomersAggregate.Repositories; using Oms.Domain.OrdersAggregate.Entities; using Oms.Domain.OrdersAggregate.Repositories; using Oms.Domain.WarehouseAggregate.Consts; namespace Oms.Application.Services.Orders; /// /// 店铺授权 API 服务。 /// [ApiExplorerSettings(GroupName = "OMS-Order")] public class PlatformStoreService : ApplicationService { private ICustomerRepository CustomerRepository => LazyServiceProvider.GetRequiredService(); private ISalesStoreRepository StoreRepository => LazyServiceProvider.GetRequiredService(); /// /// 查询店铺授权列表。 /// /// 入参:店铺分页查询条件。 /// 店铺分页结果。 [HttpPost("/order/platform-store/list")] public async Task> GetListAsync(PlatformStorePageInput input) { var customerId = await WarehouseCustomerHelper.ResolveListCustomerIdAsync( CustomerRepository, CurrentUser, input.CustomerId); var predicate = BuildListPredicate(customerId, input); var pageIndex = OmsDocHelper.NormalizePage(input.Page); var pageSize = OmsDocHelper.NormalizePageSize(input.PageSize); var entities = await StoreRepository.GetPagedListAsync( predicate, pageIndex, pageSize, new Dictionary { [nameof(SalesStore.UpdateTime)] = false, [nameof(SalesStore.Id)] = false }); var total = await StoreRepository.CountAsync(predicate); return new OmsPageResult { Items = entities.Select(MapOutput).ToList(), Total = total }; } /// /// 获取店铺 OAuth 授权地址。 /// /// 入参:平台编码与回调地址。 /// 授权跳转 URL。 [HttpPost("/order/platform-store/auth-url")] public async Task GetAuthUrlAsync(PlatformStoreAuthUrlInput input) { await WarehouseCustomerHelper.ResolveRequiredCustomerIdAsync( CustomerRepository, CurrentUser, input.CustomerId); var platform = input.PlatformCode.Trim().ToLowerInvariant(); var redirect = string.IsNullOrWhiteSpace(input.RedirectUri) ? "https://localhost/oauth/callback" : input.RedirectUri.Trim(); return new PlatformStoreAuthUrlOutput { AuthUrl = $"https://oauth.example.com/{platform}?redirect={Uri.EscapeDataString(redirect)}", Message = "OAuth 对接待平台 SDK 接入,当前返回占位地址" }; } /// /// 保存店铺授权(新增或编辑)。 /// /// 入参:店铺保存数据。 /// 保存后的店铺信息。 [HttpPost("/order/platform-store/save")] public async Task SaveAsync(PlatformStoreSaveInput input) { var customerId = await WarehouseCustomerHelper.ResolveRequiredCustomerIdAsync( CustomerRepository, CurrentUser, input.CustomerId); var nickname = input.StoreNickname.Trim(); if (string.IsNullOrWhiteSpace(nickname)) { throw new BusinessException("店铺昵称不能为空"); } SalesStore entity; if (input.Id > 0) { entity = await GetStoreOrThrowAsync(customerId, input.Id); } else { entity = new SalesStore { CustomerId = customerId, PlatformCode = string.IsNullOrWhiteSpace(input.PlatformCode) ? "manual" : input.PlatformCode.Trim(), AuthStatus = SalesStoreAuthStatusCodes.Active, AuthTime = DateTime.Now }; } entity.StoreNickname = nickname; entity.PlatformAccount = string.IsNullOrWhiteSpace(input.PlatformAccount) ? null : input.PlatformAccount.Trim(); entity.SiteCountry = string.IsNullOrWhiteSpace(input.SiteCountry) ? null : input.SiteCountry.Trim(); entity.IsAutoFulfill = input.IsAutoFulfill; entity.Remark = string.IsNullOrWhiteSpace(input.Remark) ? null : input.Remark.Trim(); if (input.Id > 0) { await StoreRepository.UpdateAsync(entity); } else { await StoreRepository.InsertAsync(entity); } return MapOutput(entity); } /// /// 删除店铺授权。 /// /// 入参:店铺 ID 列表。 [HttpPost("/order/platform-store/delete")] public async Task DeleteAsync(PlatformStoreIdsInput input) { if (input.Ids.Count == 0) { throw new BusinessException("请选择要删除的店铺"); } var customerId = await WarehouseCustomerHelper.ResolveListCustomerIdAsync( CustomerRepository, CurrentUser, null); var (hasCustomerFilter, customerIdValue) = WarehouseCustomerHelper.ToCustomerFilter(customerId); var entities = await StoreRepository.GetListAsync(x => (!hasCustomerFilter || x.CustomerId == customerIdValue) && input.Ids.Contains(x.Id)); foreach (var entity in entities) { await StoreRepository.DeleteAsync(entity); } } private async Task GetStoreOrThrowAsync(int? customerId, int id) { var entity = customerId.HasValue ? await StoreRepository.FirstOrDefaultAsync(x => x.Id == id && x.CustomerId == customerId.Value) : await StoreRepository.FirstOrDefaultAsync(x => x.Id == id); if (entity == null) { throw new BusinessException("店铺不存在"); } return entity; } private static Expression> BuildListPredicate(int? customerId, PlatformStorePageInput input) { var (hasCustomerFilter, customerIdValue) = WarehouseCustomerHelper.ToCustomerFilter(customerId); var platformCode = input.PlatformCode?.Trim(); var nickname = input.StoreNickname?.Trim(); var filter = input.ExpirationFilter?.Trim().ToLowerInvariant(); var hasPlatformCode = !string.IsNullOrWhiteSpace(platformCode); var hasNickname = !string.IsNullOrWhiteSpace(nickname); var now = DateTime.Now; var within7Days = now.AddDays(7); // 显式按过期筛选分支构造表达式,避免嵌套三元被 ORM 翻译成非法 IIF。 return filter switch { "expired" => x => (!hasCustomerFilter || x.CustomerId == customerIdValue) && (!hasPlatformCode || x.PlatformCode == platformCode) && (!hasNickname || x.StoreNickname.Contains(nickname!)) && x.TokenExpireTime.HasValue && x.TokenExpireTime < now, "within7days" => x => (!hasCustomerFilter || x.CustomerId == customerIdValue) && (!hasPlatformCode || x.PlatformCode == platformCode) && (!hasNickname || x.StoreNickname.Contains(nickname!)) && x.TokenExpireTime.HasValue && x.TokenExpireTime >= now && x.TokenExpireTime <= within7Days, "normal" => x => (!hasCustomerFilter || x.CustomerId == customerIdValue) && (!hasPlatformCode || x.PlatformCode == platformCode) && (!hasNickname || x.StoreNickname.Contains(nickname!)) && (!x.TokenExpireTime.HasValue || x.TokenExpireTime > within7Days), _ => x => (!hasCustomerFilter || x.CustomerId == customerIdValue) && (!hasPlatformCode || x.PlatformCode == platformCode) && (!hasNickname || x.StoreNickname.Contains(nickname!)) }; } private static PlatformStoreOutput MapOutput(SalesStore entity) => new() { Id = entity.Id, CustomerId = entity.CustomerId, PlatformCode = entity.PlatformCode, PlatformName = ResolvePlatformName(entity.PlatformCode), StoreNickname = entity.StoreNickname, PlatformAccount = entity.PlatformAccount, SiteCountry = entity.SiteCountry, AuthStatus = entity.AuthStatus, AuthTime = entity.AuthTime, TokenExpireTime = entity.TokenExpireTime, IsAutoFulfill = entity.IsAutoFulfill, Remark = entity.Remark, CreateTime = entity.CreateTime }; private static string ResolvePlatformName(string platformCode) => platformCode.ToLowerInvariant() switch { "lazada" => "Lazada", "shopee" => "Shopee", "tiktok" => "TikTok", "temu" => "Temu", "manual" => "手工店铺", _ => platformCode }; }