From dd4644e914ed7cc144861e4f10a45eac172cd0c8 Mon Sep 17 00:00:00 2001 From: zzh <1368735060@qq.com> Date: Thu, 24 Sep 2026 15:00:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8F=BD=E6=94=B6=E5=A4=A7=E5=8C=85?= =?UTF-8?q?=E8=BD=A8=E8=BF=B9=E6=89=8B=E5=8A=A8=E5=90=8C=E6=AD=A5=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=EF=BC=8C=E5=B9=B6=E6=94=AF=E6=8C=81=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=91=98=E9=87=8D=E7=BD=AE=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 track-upsert-big-bag 返回三步联调结果;补齐 reset-password API。 Co-authored-by: Cursor --- .../Dtos/Rbac/OmsUserProfileDtos.cs | 11 +++ .../SmallBagTrack/PickupBigBagTrackSyncer.cs | 80 +++++++++++++++++-- .../Services/Orders/PickupOrderService.cs | 10 +++ .../Services/Rbac/OmsUserService.cs | 30 +++++++ 4 files changed, 125 insertions(+), 6 deletions(-) diff --git a/src/Oms.Application/Dtos/Rbac/OmsUserProfileDtos.cs b/src/Oms.Application/Dtos/Rbac/OmsUserProfileDtos.cs index 903a8f2..415831c 100644 --- a/src/Oms.Application/Dtos/Rbac/OmsUserProfileDtos.cs +++ b/src/Oms.Application/Dtos/Rbac/OmsUserProfileDtos.cs @@ -54,3 +54,14 @@ public class OmsUserDetailDto : UserDto public string? WecomKfUrl { get; set; } } + +/// +/// 管理员重置用户密码入参。 +/// +public class ResetUserPasswordInput +{ + /// + /// 新密码。 + /// + public string NewPassword { get; set; } = string.Empty; +} diff --git a/src/Oms.Application/Integrations/SmallBagTrack/PickupBigBagTrackSyncer.cs b/src/Oms.Application/Integrations/SmallBagTrack/PickupBigBagTrackSyncer.cs index e3bca81..fc63b2c 100644 --- a/src/Oms.Application/Integrations/SmallBagTrack/PickupBigBagTrackSyncer.cs +++ b/src/Oms.Application/Integrations/SmallBagTrack/PickupBigBagTrackSyncer.cs @@ -9,11 +9,36 @@ using Oms.Domain.OrdersAggregate.Repositories; namespace Oms.Application.Integrations.SmallBagTrack; /// -/// 揽收单预报下游成功后:按序 UpsertBigBag → Link → Track/Upsert(A0201)。 +/// 揽收大包轨迹同步结果(UpsertBigBag → Link → Track A0201)。 +/// +public class PickupBigBagTrackSyncResult +{ + public string PickupNo { get; set; } = string.Empty; + + public bool Success { get; set; } + + public string? FailStep { get; set; } + + public string? Message { get; set; } + + public SmallBagTrackUpsertResult? UpsertBigBag { get; set; } + + public SmallBagTrackUpsertResult? Link { get; set; } + + public SmallBagTrackUpsertResult? TrackUpsert { get; set; } +} + +/// +/// 揽收单预报下游成功后 / 手动:按序 UpsertBigBag → Link → Track/Upsert(A0201)。 /// public interface IPickupBigBagTrackSyncer { Task SyncAfterDownstreamForwardAsync(PickupOrder pickup, CancellationToken cancellationToken = default); + + /// + /// 同步执行三步并返回逐步请求/响应(手动「创建轨迹订单」联调)。 + /// + Task SyncDebugAsync(PickupOrder pickup, CancellationToken cancellationToken = default); } internal sealed class PickupBigBagTrackSyncer( @@ -29,13 +54,29 @@ internal sealed class PickupBigBagTrackSyncer( PickupOrder pickup, CancellationToken cancellationToken = default) { + await SyncDebugAsync(pickup, cancellationToken); + } + + public async Task SyncDebugAsync( + PickupOrder pickup, + CancellationToken cancellationToken = default) + { + var result = new PickupBigBagTrackSyncResult + { + PickupNo = pickup?.PickupNo?.Trim() ?? string.Empty + }; + if (pickup == null || string.IsNullOrWhiteSpace(pickup.PickupNo)) { + result.Success = false; + result.FailStep = "validate"; + result.Message = "pickup or PickupNo empty"; logger.LogWarning("Skip big-bag track sync: pickup or PickupNo empty"); - return; + return result; } var bagNo = pickup.PickupNo.Trim(); + result.PickupNo = bagNo; var authorization = httpContextAccessor.HttpContext?.Request.Headers.Authorization.ToString(); if (string.IsNullOrWhiteSpace(authorization)) { @@ -62,22 +103,29 @@ internal sealed class PickupBigBagTrackSyncer( Authorization = authorization }, cancellationToken); + result.UpsertBigBag = upsertResult; if (upsertResult.Skipped) { + result.Success = false; + result.FailStep = "UpsertBigBag"; + result.Message = upsertResult.SkipReason ?? "skipped"; logger.LogDebug( "UpsertBigBag skipped for pickup {PickupNo}: {Reason}", bagNo, upsertResult.SkipReason); - return; + return result; } if (!upsertResult.Success) { + result.Success = false; + result.FailStep = "UpsertBigBag"; + result.Message = $"HTTP {upsertResult.StatusCode}"; logger.LogWarning( "Stop big-bag track sync after UpsertBigBag failure for pickup {PickupNo}", bagNo); - return; + return result; } // 2) Link(无明细单号则跳过,继续写轨迹) @@ -98,6 +146,7 @@ internal sealed class PickupBigBagTrackSyncer( SmallBagTrackLifecycleEvents.OrderType, authorization, cancellationToken); + result.Link = linkResult; if (linkResult.Skipped) { @@ -108,10 +157,13 @@ internal sealed class PickupBigBagTrackSyncer( } else if (!linkResult.Success) { + result.Success = false; + result.FailStep = "Link"; + result.Message = $"HTTP {linkResult.StatusCode}"; logger.LogWarning( "Stop big-bag track sync after Link failure for pickup {PickupNo}", bagNo); - return; + return result; } } else @@ -126,28 +178,44 @@ internal sealed class PickupBigBagTrackSyncer( SmallBagTrackLifecycleEvents.BigBagOrderType, authorization, cancellationToken); + result.TrackUpsert = trackResult; if (trackResult.Skipped) { + result.Success = false; + result.FailStep = "TrackUpsert"; + result.Message = trackResult.SkipReason ?? "skipped"; logger.LogDebug( "Track A0201 skipped for pickup {PickupNo}: {Reason}", bagNo, trackResult.SkipReason); - return; + return result; } if (!trackResult.Success) { + result.Success = false; + result.FailStep = "TrackUpsert"; + result.Message = $"HTTP {trackResult.StatusCode}"; logger.LogWarning( "Track A0201 failed for pickup {PickupNo}: {StatusCode} {Body}", bagNo, trackResult.StatusCode, trackResult.ResponseBody); + return result; } + + result.Success = true; + result.Message = "ok"; + return result; } catch (Exception ex) { + result.Success = false; + result.FailStep ??= "exception"; + result.Message = ex.Message; logger.LogWarning(ex, "Big-bag track sync failed for pickup {PickupNo}", bagNo); + return result; } } diff --git a/src/Oms.Application/Services/Orders/PickupOrderService.cs b/src/Oms.Application/Services/Orders/PickupOrderService.cs index bd33958..9020543 100644 --- a/src/Oms.Application/Services/Orders/PickupOrderService.cs +++ b/src/Oms.Application/Services/Orders/PickupOrderService.cs @@ -1005,6 +1005,16 @@ public class PickupOrderService : ApplicationService }; } + /// + /// 手动创建大包轨迹订单:按序 UpsertBigBag → Link → Track/Upsert(A0201),返回逐步请求/响应。 + /// + [HttpPost("/order/pickup-order/track-upsert-big-bag")] + public async Task TrackUpsertBigBagAsync([FromBody] PickupOrderIdInput input) + { + var pickup = await GetOwnedPickupOrThrowAsync(input.Id); + return await PickupBigBagTrackSyncer.SyncDebugAsync(pickup); + } + /// /// 凭证:优先揽收单 CarrierCode 作为 supplierCode;AppKey/Secret 仍按承运服务→渠道分组解析。 /// diff --git a/src/Oms.Application/Services/Rbac/OmsUserService.cs b/src/Oms.Application/Services/Rbac/OmsUserService.cs index d4f4a2e..8abb5fb 100644 --- a/src/Oms.Application/Services/Rbac/OmsUserService.cs +++ b/src/Oms.Application/Services/Rbac/OmsUserService.cs @@ -4,6 +4,7 @@ using Fake.Rbac.Application.Dtos.Common; using Fake.Rbac.Application.Dtos.Role; using Fake.Rbac.Application.Dtos.User; using Fake.Rbac.Application.Services; +using Fake.Rbac.Domain.Managers; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Fake.ObjectStorage; @@ -23,6 +24,7 @@ namespace Oms.Application.Services.Rbac; [ApiExplorerSettings(GroupName = "RBAC-User")] public class OmsUserService( UserService userService, + AccountManager accountManager, UserProfileRepository profileRepository, IEmployeeRepository employeeRepository, IObjectStorage fileStorage) : ApplicationService @@ -167,6 +169,34 @@ public class OmsUserService( CancellationToken cancellationToken = default) => userService.UpdatePasswordAsync(id, input, cancellationToken); + /// + /// 管理员重置用户密码(无需旧密码)。 + /// + [HttpPost("/rbac/user/reset-password")] + public async Task ResetPasswordAsync( + [FromQuery] Guid id, + [FromBody] ResetUserPasswordInput input, + CancellationToken cancellationToken = default) + { + if (id == Guid.Empty) + { + throw new BusinessException("用户 Id 无效"); + } + + var newPassword = input?.NewPassword?.Trim(); + if (string.IsNullOrWhiteSpace(newPassword)) + { + throw new BusinessException("新密码不能为空"); + } + + if (newPassword.Length < 6) + { + throw new BusinessException("密码长度不能少于6位"); + } + + await accountManager.ResetPasswordAsync(id, newPassword, cancellationToken); + } + /// /// 更新用户头像。 ///