admin/src/Oms.Application/Services/Orders/OrderService.BatchCreate.cs
zzh 188aed1ee6 feat: 店铺订单创建物流/代发,并补齐收件人字段与种子数据
- 新增 batch-create-logistics / batch-create-fulfill:按平台行建物流单并回写 LinkedOrderId,代发支持绑定冻结与失败回滚
- ParcelBuilder 支持无 SKU 映射的平台行申报;批量创建可指定 OrderSource=platform
- 库存绑定候选接口支持 orderId=0 草稿选仓
- 店铺订单增加结构化收件人字段(db.sql + 迁移脚本),拉单回填;补充 customer9 店铺/揽收种子与 ConcurrencyStamp 修复脚本

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-19 22:33:55 +08:00

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