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
};
}