feat: 9610 支持从已有运单批量创建汇总申报单
All checks were successful
Build and Deploy / deploy (push) Successful in 1m2s

- 新增 POST /order/customs-9610/create-from-orders:运单设为 9610Summary 并绑定出口资质备案(空则客户默认),按申报明细生成待申报单
- 逐单返回结果:已删除、已有 9610 单、无申报明细、无可用备案记为失败,不影响其它运单
This commit is contained in:
zzh 2026-10-19 16:24:02 +08:00
parent 314c5ac02f
commit e931606a75
2 changed files with 178 additions and 0 deletions

View File

@ -319,3 +319,68 @@ public class Customs9610DeclareBatchOutput
public List<Customs9610DeclareResult> Items { get; set; } = [];
}
/// <summary>
/// 从已有运单批量创建 9610 汇总申报单入参。
/// </summary>
public class Customs9610CreateFromOrdersInput
{
/// <summary>
/// 运单 Id 列表(t_order.Id)。
/// </summary>
[Required]
[MinLength(1)]
public List<int> OrderIds { get; set; } = [];
/// <summary>
/// 出口资质备案 Id;为空时按各运单客户的默认备案。
/// </summary>
public int? ExportQualificationId { get; set; }
/// <summary>
/// 电商平台编码(e-commerce_platform 字典),最长 16。
/// </summary>
[Required]
[StringLength(16)]
public string PlatformCode { get; set; } = string.Empty;
}
/// <summary>
/// 单个运单创建 9610 申报单结果。
/// </summary>
public class Customs9610CreateFromOrderResult
{
/// <summary>
/// 运单 Id。
/// </summary>
public int OrderId { get; set; }
/// <summary>
/// 运单号。
/// </summary>
public string? ServiceNo { get; set; }
public bool Success { get; set; }
/// <summary>
/// 失败原因;成功为空。
/// </summary>
public string? Message { get; set; }
/// <summary>
/// 生成的 9610 申报单 Id。
/// </summary>
public int? Customs9610OrderId { get; set; }
}
/// <summary>
/// 批量创建 9610 申报单结果。
/// </summary>
public class Customs9610CreateFromOrdersOutput
{
public int SuccessCount { get; set; }
public int FailCount { get; set; }
public List<Customs9610CreateFromOrderResult> Items { get; set; } = [];
}

View File

@ -41,6 +41,12 @@ public class Customs9610OrderService : ApplicationService
private IOptions<ItfxGatewayOptions> ItfxOptions =>
LazyServiceProvider.GetRequiredService<IOptions<ItfxGatewayOptions>>();
private ICustomerExportQualificationRepository ExportQualificationRepository =>
LazyServiceProvider.GetRequiredService<ICustomerExportQualificationRepository>();
private Customs9610OrderWriter Customs9610Writer =>
LazyServiceProvider.GetRequiredService<Customs9610OrderWriter>();
[HttpPost("/order/customs-9610/list")]
public async Task<OmsPageResult<Customs9610OrderOutput>> ListAsync([FromBody] Customs9610ListInput input)
{
@ -209,6 +215,113 @@ public class Customs9610OrderService : ApplicationService
};
}
/// <summary>
/// 从已有运单批量创建 9610 汇总申报单:运单改为 9610Summary 并绑定出口资质备案,按运单申报明细生成待申报单。
/// </summary>
/// <param name="input">运单 Id 列表、出口资质备案 Id(空则用各客户默认备案)、电商平台编码。</param>
/// <returns>逐单结果;已删除、已有 9610 单、无申报明细、无可用备案的运单记为失败,不影响其它运单。</returns>
[HttpPost("/order/customs-9610/create-from-orders")]
public async Task<Customs9610CreateFromOrdersOutput> CreateFromOrdersAsync(
[FromBody] Customs9610CreateFromOrdersInput input)
{
var platformCode = input.PlatformCode?.Trim() ?? string.Empty;
if (string.IsNullOrWhiteSpace(platformCode))
{
throw new BusinessException("请选择电商平台");
}
if (platformCode.Length > 16)
{
throw new BusinessException("电商平台编码不能超过 16 个字符");
}
var orderIds = input.OrderIds.Where(x => x > 0).Distinct().ToList();
if (orderIds.Count == 0)
{
throw new BusinessException("请选择运单");
}
var sessionCustomerId = await WarehouseCustomerHelper.ResolveListCustomerIdAsync(
CustomerRepository,
CurrentUser,
null);
var orders = (await OrderRepository.GetListAsync(x => orderIds.Contains(x.Id)))
.ToDictionary(x => x.Id);
var declarations = await OrderRepository.GetDeclarationsByOrderIdsAsync(orderIds);
var ordersWithItems = declarations.Select(x => x.OrderId).ToHashSet();
var results = new List<Customs9610CreateFromOrderResult>();
foreach (var orderId in orderIds)
{
var result = new Customs9610CreateFromOrderResult { OrderId = orderId };
results.Add(result);
try
{
if (!orders.TryGetValue(orderId, out var order)
|| (sessionCustomerId is > 0 && order.CustomerId != sessionCustomerId.Value))
{
throw new BusinessException("运单不存在或无权访问");
}
result.ServiceNo = order.ServiceNo;
if (string.Equals(order.OrderStatus, OrderStatusCodes.Deleted, StringComparison.OrdinalIgnoreCase))
{
throw new BusinessException("运单已删除");
}
if (order.CustomerId is not > 0)
{
throw new BusinessException("运单未关联客户");
}
if (await HeaderRepository.GetByOrderIdAsync(order.Id) != null)
{
throw new BusinessException("该运单已存在 9610 申报单");
}
if (!ordersWithItems.Contains(order.Id))
{
throw new BusinessException("运单没有申报明细");
}
var qualification = input.ExportQualificationId is > 0
? await ExportQualificationRepository.GetOwnedAsync(
input.ExportQualificationId.Value,
order.CustomerId.Value)
: await ExportQualificationRepository.GetDefaultAsync(order.CustomerId.Value);
if (qualification == null)
{
throw new BusinessException(input.ExportQualificationId is > 0
? "所选出口资质备案不属于该运单客户"
: "运单客户未维护默认出口资质备案");
}
order.ExportCustomsMode = ExportCustomsModeCodes.Mode9610Summary;
order.ExportQualificationId = qualification.Id;
await Customs9610Writer.UpsertFromOrderAsync(order, platformCode);
order.UpdateTime = DateTime.Now;
await OrderRepository.UpdateAsync(order);
var header = await HeaderRepository.GetByOrderIdAsync(order.Id);
result.Customs9610OrderId = header?.Id;
result.Success = header != null;
result.Message = header == null ? "9610 申报单生成失败" : null;
}
catch (Exception ex)
{
result.Success = false;
result.Message = string.IsNullOrWhiteSpace(ex.Message) ? "创建失败" : ex.Message;
}
}
return new Customs9610CreateFromOrdersOutput
{
SuccessCount = results.Count(x => x.Success),
FailCount = results.Count(x => !x.Success),
Items = results,
};
}
/// <summary>
/// 批量获取清单数据(调 API 中心上游,标准码暂定),成功后回写本地字段。
/// </summary>