- 新增 batch-create-logistics / batch-create-fulfill:按平台行建物流单并回写 LinkedOrderId,代发支持绑定冻结与失败回滚 - ParcelBuilder 支持无 SKU 映射的平台行申报;批量创建可指定 OrderSource=platform - 库存绑定候选接口支持 orderId=0 草稿选仓 - 店铺订单增加结构化收件人字段(db.sql + 迁移脚本),拉单回填;补充 customer9 店铺/揽收种子与 ConcurrencyStamp 修复脚本 Co-authored-by: Cursor <cursoragent@cursor.com>
102 lines
3.7 KiB
C#
102 lines
3.7 KiB
C#
using Fake.Application;
|
|
using Newtonsoft.Json;
|
|
using Oms.Application.Dtos.Orders;
|
|
using Oms.Application.Messaging;
|
|
using Oms.Application.Services.Orders.Batch;
|
|
using Oms.Application.Services.Orders.Templates;
|
|
using Oms.Domain.OrdersAggregate.Consts;
|
|
|
|
namespace Oms.Application.Services.Orders;
|
|
|
|
public partial class OrderService
|
|
{
|
|
private IOrderBatchJobPublisher BatchJobPublisher =>
|
|
LazyServiceProvider.GetRequiredService<IOrderBatchJobPublisher>();
|
|
|
|
/// <summary>
|
|
/// 批量 Worker 导入单行 Excel 数据。
|
|
/// </summary>
|
|
public async Task<OrderParcelCreateOutput> ImportRowForBatchAsync(
|
|
OrderBatchImportRowPayload payload,
|
|
int operatorUserId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var template = await OrderTemplateRepository.FirstOrDefaultAsync(x => x.Id == payload.TemplateId, cancellationToken)
|
|
?? throw new BusinessException("模板不存在");
|
|
|
|
OrderTemplateImportMapping.EnsureTemplateReadyForImport(template);
|
|
var mapping = OrderTemplateImportMapping.Resolve(template);
|
|
var sourceValues = JsonConvert.DeserializeObject<Dictionary<string, object>>(payload.RowDataJson)
|
|
?? throw new BusinessException("行数据无效");
|
|
|
|
var input = OrderTemplateRowMapper.MapToParcelCreateInput(sourceValues, mapping, payload.CustomerId);
|
|
return await CreateParcelForBatchAsync(input, operatorUserId, cancellationToken);
|
|
}
|
|
|
|
public async Task<int?> TryResolveImportOrderIdAsync(
|
|
OrderBatchImportRowPayload payload,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
try
|
|
{
|
|
var template = await OrderTemplateRepository.FirstOrDefaultAsync(x => x.Id == payload.TemplateId, cancellationToken);
|
|
if (template == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var mapping = OrderTemplateImportMapping.Resolve(template);
|
|
var sourceValues = JsonConvert.DeserializeObject<Dictionary<string, object>>(payload.RowDataJson);
|
|
if (sourceValues == null || sourceValues.Count == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var input = OrderTemplateRowMapper.MapToParcelCreateInput(sourceValues, mapping, payload.CustomerId);
|
|
if (string.IsNullOrWhiteSpace(input.ClientNo))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var order = await OrderRepository.GetByCustomerAndClientNoAsync(
|
|
payload.CustomerId,
|
|
input.ClientNo.Trim(),
|
|
cancellationToken);
|
|
return order?.Id;
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 批量 Worker 创建小包订单。
|
|
/// </summary>
|
|
public async Task<OrderParcelCreateOutput> CreateParcelForBatchAsync(
|
|
OrderParcelCreateInput input,
|
|
int operatorUserId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
return await CreateParcelForBatchAsync(input, operatorUserId, orderSource: null, cancellationToken);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 批量 Worker 创建小包订单(可指定 <paramref name="orderSource"/>)。
|
|
/// </summary>
|
|
public async Task<OrderParcelCreateOutput> CreateParcelForBatchAsync(
|
|
OrderParcelCreateInput input,
|
|
int operatorUserId,
|
|
string? orderSource,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
ParcelCreateContext? createContext = null;
|
|
if (!string.IsNullOrWhiteSpace(orderSource))
|
|
{
|
|
createContext = new ParcelCreateContext { OrderSource = orderSource.Trim() };
|
|
}
|
|
|
|
return await CreateParcelCoreAsync(input, operatorUserId, createContext);
|
|
}
|
|
}
|