补迁移脚本为 C_* 与 t_sales_* 表增加 ConcurrencyStamp;列表查询改用 hasCustomerFilter 模式避免 ORM 生成非法 SQL。 Co-authored-by: Cursor <cursoragent@cursor.com>
217 lines
8.3 KiB
C#
217 lines
8.3 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// 店铺授权 API 服务。
|
|
/// </summary>
|
|
[ApiExplorerSettings(GroupName = "OMS-Order")]
|
|
public class PlatformStoreService : ApplicationService
|
|
{
|
|
private ICustomerRepository CustomerRepository => LazyServiceProvider.GetRequiredService<ICustomerRepository>();
|
|
private ISalesStoreRepository StoreRepository => LazyServiceProvider.GetRequiredService<ISalesStoreRepository>();
|
|
|
|
/// <summary>
|
|
/// 查询店铺授权列表。
|
|
/// </summary>
|
|
/// <param name="input">入参:店铺分页查询条件。</param>
|
|
/// <returns>店铺分页结果。</returns>
|
|
[HttpPost("/order/platform-store/list")]
|
|
public async Task<OmsPageResult<PlatformStoreOutput>> 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<string, bool> { [nameof(SalesStore.UpdateTime)] = false, [nameof(SalesStore.Id)] = false });
|
|
var total = await StoreRepository.CountAsync(predicate);
|
|
|
|
return new OmsPageResult<PlatformStoreOutput>
|
|
{
|
|
Items = entities.Select(MapOutput).ToList(),
|
|
Total = total
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取店铺 OAuth 授权地址。
|
|
/// </summary>
|
|
/// <param name="input">入参:平台编码与回调地址。</param>
|
|
/// <returns>授权跳转 URL。</returns>
|
|
[HttpPost("/order/platform-store/auth-url")]
|
|
public async Task<PlatformStoreAuthUrlOutput> 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 接入,当前返回占位地址"
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// 保存店铺授权(新增或编辑)。
|
|
/// </summary>
|
|
/// <param name="input">入参:店铺保存数据。</param>
|
|
/// <returns>保存后的店铺信息。</returns>
|
|
[HttpPost("/order/platform-store/save")]
|
|
public async Task<PlatformStoreOutput> 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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 删除店铺授权。
|
|
/// </summary>
|
|
/// <param name="input">入参:店铺 ID 列表。</param>
|
|
[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<SalesStore> 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<Func<SalesStore, bool>> 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 now = DateTime.Now;
|
|
var within7Days = now.AddDays(7);
|
|
|
|
return x =>
|
|
(!hasCustomerFilter || x.CustomerId == customerIdValue)
|
|
&& (string.IsNullOrWhiteSpace(platformCode) || x.PlatformCode == platformCode)
|
|
&& (string.IsNullOrWhiteSpace(nickname) || x.StoreNickname.Contains(nickname))
|
|
&& (string.IsNullOrWhiteSpace(filter) || filter == "normal"
|
|
? string.IsNullOrWhiteSpace(filter) || !x.TokenExpireTime.HasValue || x.TokenExpireTime > within7Days
|
|
: filter == "expired"
|
|
? x.TokenExpireTime.HasValue && x.TokenExpireTime < now
|
|
: filter == "within7days"
|
|
? x.TokenExpireTime.HasValue && x.TokenExpireTime >= now && x.TokenExpireTime <= within7Days
|
|
: true);
|
|
}
|
|
|
|
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
|
|
};
|
|
}
|