admin/src/Oms.Application/Services/OpenApi/WarehouseOpenApiService.cs
zzh b7be742046
All checks were successful
Build and Deploy / deploy (push) Successful in 4m34s
feat: 店铺订单支持仓储代发(wms)建单,放宽创建状态限制
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-14 15:39:31 +08:00

202 lines
8.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Fake.Application;
using Fake.Rbac.Application.Jwt;
using Fake.Rbac.Domain.Managers;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Oms.Application.Dtos.OpenApi;
using Oms.Application.Dtos.Operations;
using Oms.Application.Rbac;
using Oms.Application.Services.Operations;
using Oms.Application.Services.Orders;
using Oms.Application.Shared;
using Oms.Domain.OrdersAggregate.Consts;
using Oms.Domain.OrdersAggregate.Repositories;
namespace Oms.Application.Services.OpenApi;
/// <summary>
/// 仓配对外开放 API(Swagger 文档分组:warehouseOpenApi)。
/// 鉴权:Authorization: {token}(账号密码换取,有效期 48 小时;无需 Bearer 前缀)。
/// 与客户 HMAC 分组 openApi 相互独立。
/// </summary>
[ApiExplorerSettings(GroupName = "warehouseOpenApi")]
public class WarehouseOpenApiService : ApplicationService
{
private AccountManager AccountManager =>
LazyServiceProvider.GetRequiredService<AccountManager>();
private IJwtService JwtService =>
LazyServiceProvider.GetRequiredService<IJwtService>();
private OpenApiTokenIssuer TokenIssuer =>
LazyServiceProvider.GetRequiredService<OpenApiTokenIssuer>();
private IOrderRepository OrderRepository =>
LazyServiceProvider.GetRequiredService<IOrderRepository>();
private OrderFileManager OrderFileManager =>
LazyServiceProvider.GetRequiredService<OrderFileManager>();
private InboundRecordService InboundRecordService =>
LazyServiceProvider.GetRequiredService<InboundRecordService>();
private OutboundRecordService OutboundRecordService =>
LazyServiceProvider.GetRequiredService<OutboundRecordService>();
/// <summary>
/// 获取 Token(有效期 48 小时)。后续请求 Header:Authorization: {token}(无需 Bearer)。
/// </summary>
[AllowAnonymous]
[HttpPost("/openapi/warehouse/token")]
[Consumes("application/json")]
[Produces("application/json")]
public async Task<string> GetTokenAsync(
[FromBody] OpenApiGetTokenInput input,
CancellationToken cancellationToken = default)
{
if (input == null
|| string.IsNullOrWhiteSpace(input.Username)
|| string.IsNullOrWhiteSpace(input.Password))
{
throw new BusinessException("用户名或密码不能为空");
}
var user = await AccountManager.ValidateCredentialsAsync(
input.Username.Trim(),
input.Password,
cancellationToken);
var profileRepository = LazyServiceProvider.GetRequiredService<UserProfileRepository>();
await profileRepository.EnsureEnabledOrThrowAsync(user.Id, cancellationToken);
var claims = await JwtService.GenerateClaimsByUserIdAsync(user.Id, cancellationToken);
return TokenIssuer.Issue(claims);
}
/// <summary>
/// 入库上传件重尺。与操作台「入库扫描」页「入库」按钮同源(POST /operation/inbound/create)。
/// 写入收货件重尺、入库记录,并触发入库计费/自动触发。重量默认 kg,长度默认 cm。
/// Header:Authorization: {token}(无需 Bearer)。
/// </summary>
[Authorize]
[HttpPost("/openapi/warehouse/inbound")]
[Consumes("application/json")]
[Produces("application/json")]
public Task<OpenApiSimpleResult> InboundAsync([FromBody] OpenApiUploadWeightInput input) =>
UploadWeightCoreAsync(input, forceMode: 0);
/// <summary>
/// 上传件重尺。mode:0=入库(同「入库」按钮),1=出库(同「出库」按钮)。
/// Header:Authorization: {token}(无需 Bearer)。
/// </summary>
[Authorize]
[HttpPost("/openapi/warehouse/weight/upload")]
[Consumes("application/json")]
[Produces("application/json")]
public Task<OpenApiSimpleResult> UploadWeightAsync([FromBody] OpenApiUploadWeightInput input) =>
UploadWeightCoreAsync(input, forceMode: null);
private async Task<OpenApiSimpleResult> UploadWeightCoreAsync(
OpenApiUploadWeightInput? input,
int? forceMode)
{
try
{
if (input == null || string.IsNullOrWhiteSpace(input.OrderNo))
{
return OpenApiSimpleResult.Fail("订单号不能为空");
}
var mode = forceMode ?? input.Mode;
var weightKg = OpenApiMeasureUnitConverter.ToKilograms(input.Weight, input.WeightUnit);
var lengthCm = OpenApiMeasureUnitConverter.ToCentimeters(input.Length, input.LengthUnit);
var widthCm = OpenApiMeasureUnitConverter.ToCentimeters(input.Width, input.LengthUnit);
var heightCm = OpenApiMeasureUnitConverter.ToCentimeters(input.Height, input.LengthUnit);
var orderNo = input.OrderNo.Trim();
if (weightKg <= 0 || lengthCm <= 0 || widthCm <= 0 || heightCm <= 0)
{
return OpenApiSimpleResult.Fail("实重与长宽高须大于 0");
}
if (mode == 1)
{
await OutboundRecordService.CreateAsync(new CreateOutboundRecordInput
{
WaybillNo = orderNo,
Weight = weightKg,
Length = lengthCm,
Width = widthCm,
Height = heightCm
});
return OpenApiSimpleResult.Ok("出库成功");
}
if (mode == 0)
{
// 与入库扫描页「入库」按钮同一实现:InboundRecordService.CreateAsync
var result = await InboundRecordService.CreateAsync(new CreateInboundRecordInput
{
WaybillNo = orderNo,
Weight = weightKg,
Length = lengthCm,
Width = widthCm,
Height = heightCm
});
var statusName = string.IsNullOrWhiteSpace(result.OrderStatusName)
? "入库成功"
: $"入库成功({result.OrderStatusName})";
return OpenApiSimpleResult.Ok(statusName);
}
return OpenApiSimpleResult.Fail("mode 仅支持 0(入库)或 1(出库)");
}
catch (BusinessException ex)
{
return OpenApiSimpleResult.Fail(ex.Message);
}
catch (Exception ex)
{
return OpenApiSimpleResult.Fail(
string.IsNullOrWhiteSpace(ex.Message) ? "系统异常" : ex.Message);
}
}
/// <summary>
/// 上传红外图片。Base64 解码后转存 COS,写入订单附件 fileType=infraredImage(Source=upload),
/// 与订单详情「附件」Tab →「红外图片」→「点击上传」(POST /order/files/upload)同源展示。
/// 成功无响应体。Header:Authorization: {token}(无需 Bearer)。
/// </summary>
[Authorize]
[HttpPost("/openapi/warehouse/image/upload")]
[Consumes("application/json")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<IActionResult> UploadImageAsync(
[FromBody] OpenApiUploadImageInput input,
CancellationToken cancellationToken = default)
{
if (input == null || string.IsNullOrWhiteSpace(input.OrderNo))
{
throw new BusinessException("订单号不能为空");
}
if (string.IsNullOrWhiteSpace(input.ImageBase64))
{
throw new BusinessException("图片内容不能为空");
}
var order = await OrderRepository.GetByWaybillNoAsync(input.OrderNo.Trim(), cancellationToken)
?? throw new BusinessException("未找到对应订单,请检查单号");
var operatorUserId = CurrentUser.IsAuthenticated ? CurrentUser.GetId<int>() : 0;
// 固定红外图片类型,路径 uploads/order-files/{orderId}/infraredImage/...
await OrderFileManager.AddFromBase64Async(
order.Id,
OrderFileTypeCodes.InfraredImage,
input.ImageBase64,
operatorUserId,
cancellationToken);
return new NoContentResult();
}
}