From 07fc7fffe7b513e69c843aa432f5bc3b97764a41 Mon Sep 17 00:00:00 2001 From: zzh <1368735060@qq.com> Date: Mon, 29 Jun 2026 19:07:17 +0800 Subject: [PATCH] feat: resolve createBy/updateBy to RBAC user display names Add OmsRbacUserDisplayNameResolver and use it in customer, order, warehouse, and channel APIs so audit fields return names instead of mapIds. Co-authored-by: Cursor --- .../Dtos/Customers/CustomerPaginatedOutput.cs | 10 +++ .../Channels/SalesProductService.Helpers.cs | 15 ++-- .../Services/Channels/SalesProductService.cs | 6 +- .../Channels/ServiceChannelService.Helpers.cs | 9 ++- .../Channels/ServiceChannelService.cs | 6 +- .../Services/Customers/CustomerService.cs | 20 ++++- .../Services/Orders/OrderService.Helpers.cs | 7 +- .../Services/Orders/OrderService.cs | 2 + .../Services/System/WarehouseService.cs | 9 ++- .../Shared/OmsRbacUserDisplayNameResolver.cs | 80 +++++++++++++++++++ 10 files changed, 147 insertions(+), 17 deletions(-) create mode 100644 src/Oms.Application/Shared/OmsRbacUserDisplayNameResolver.cs diff --git a/src/Oms.Application/Dtos/Customers/CustomerPaginatedOutput.cs b/src/Oms.Application/Dtos/Customers/CustomerPaginatedOutput.cs index 290f1d3..6ddc01a 100644 --- a/src/Oms.Application/Dtos/Customers/CustomerPaginatedOutput.cs +++ b/src/Oms.Application/Dtos/Customers/CustomerPaginatedOutput.cs @@ -102,8 +102,18 @@ public class CustomerPaginatedOutput /// public DateTime CreateTime { get; set; } + /// + /// 创建人(展示名称)。 + /// + public string? CreateBy { get; set; } + /// /// 更新时间。 /// public DateTime? UpdateTime { get; set; } + + /// + /// 更新人(展示名称)。 + /// + public string? UpdateBy { get; set; } } diff --git a/src/Oms.Application/Services/Channels/SalesProductService.Helpers.cs b/src/Oms.Application/Services/Channels/SalesProductService.Helpers.cs index 18fcd22..272c38f 100644 --- a/src/Oms.Application/Services/Channels/SalesProductService.Helpers.cs +++ b/src/Oms.Application/Services/Channels/SalesProductService.Helpers.cs @@ -74,6 +74,9 @@ public partial class SalesProductService .GroupBy(x => x.PriceId) .ToDictionary(x => x.Key, x => x.OrderBy(item => item.Id).ToList()); + var auditUserNameMap = await UserDisplayNameResolver.ResolveManyAsync( + prices.SelectMany(x => new[] { x.CreateUserId, x.UpdateUserId })); + return prices.Select(price => new SalesProductPriceListOutput { Id = price.Id, @@ -88,9 +91,9 @@ public partial class SalesProductService Factors = factorMap.TryGetValue(price.Id, out var factors) ? factors.Select(MapPriceFactorOutput).ToList() : [], Details = detailMap.TryGetValue(price.Id, out var details) ? details.Select(MapPriceDetailOutput).ToList() : [], CreateTime = price.CreateTime, - CreateBy = price.CreateUserId <= 0 ? null : price.CreateUserId.ToString(), + CreateBy = OmsRbacUserDisplayNameResolver.FormatUserId(price.CreateUserId, auditUserNameMap), UpdateTime = OmsDocHelper.NullIfDefault(price.UpdateTime), - UpdateBy = price.UpdateUserId <= 0 ? null : price.UpdateUserId.ToString() + UpdateBy = OmsRbacUserDisplayNameResolver.FormatUserId(price.UpdateUserId, auditUserNameMap) }).ToList(); } @@ -342,7 +345,9 @@ public partial class SalesProductService }; } - private static SalesProductDetailOutput MapDetailOutput(SalesProduct entity) + private static SalesProductDetailOutput MapDetailOutput( + SalesProduct entity, + IReadOnlyDictionary auditUserNameMap) { return new SalesProductDetailOutput { @@ -363,9 +368,9 @@ public partial class SalesProductService IsActive = (byte)entity.IsActive, Scheme = entity.Scheme, CreateTime = entity.CreateTime, - CreateBy = entity.CreateUserId <= 0 ? null : entity.CreateUserId.ToString(), + CreateBy = OmsRbacUserDisplayNameResolver.FormatUserId(entity.CreateUserId, auditUserNameMap), UpdateTime = OmsDocHelper.NullIfDefault(entity.UpdateTime), - UpdateBy = entity.UpdateUserId <= 0 ? null : entity.UpdateUserId.ToString() + UpdateBy = OmsRbacUserDisplayNameResolver.FormatUserId(entity.UpdateUserId, auditUserNameMap) }; } diff --git a/src/Oms.Application/Services/Channels/SalesProductService.cs b/src/Oms.Application/Services/Channels/SalesProductService.cs index b6a023b..eb75bac 100644 --- a/src/Oms.Application/Services/Channels/SalesProductService.cs +++ b/src/Oms.Application/Services/Channels/SalesProductService.cs @@ -25,6 +25,8 @@ public partial class SalesProductService : ApplicationService private IWeightRoundingRepository WeightRoundingRepository => LazyServiceProvider.GetRequiredService(); private AllocationProviderSnapshotEnricher AllocationProviderSnapshotEnricher => LazyServiceProvider.GetRequiredService(); + private OmsRbacUserDisplayNameResolver UserDisplayNameResolver => + LazyServiceProvider.GetRequiredService(); /// /// 查询销售产品列表。 @@ -71,7 +73,9 @@ public partial class SalesProductService : ApplicationService public async Task GetDetailAsync([FromQuery] int id) { var entity = await GetProductOrThrowAsync(id); - return MapDetailOutput(entity); + var auditUserNameMap = await UserDisplayNameResolver.ResolveManyAsync( + [entity.CreateUserId, entity.UpdateUserId]); + return MapDetailOutput(entity, auditUserNameMap); } /// diff --git a/src/Oms.Application/Services/Channels/ServiceChannelService.Helpers.cs b/src/Oms.Application/Services/Channels/ServiceChannelService.Helpers.cs index a6c1fab..d8d6500 100644 --- a/src/Oms.Application/Services/Channels/ServiceChannelService.Helpers.cs +++ b/src/Oms.Application/Services/Channels/ServiceChannelService.Helpers.cs @@ -329,7 +329,10 @@ public partial class ServiceChannelService }; } - private static ServiceChannelDetailOutput MapDetailOutput(ServiceChannel entity, IReadOnlyDictionary groupMap) + private static ServiceChannelDetailOutput MapDetailOutput( + ServiceChannel entity, + IReadOnlyDictionary groupMap, + IReadOnlyDictionary auditUserNameMap) { return new ServiceChannelDetailOutput { @@ -346,9 +349,9 @@ public partial class ServiceChannelService PreScript = entity.PreScript, IsActive = (byte)entity.IsActive, CreateTime = entity.CreateTime, - CreateBy = entity.CreateUserId <= 0 ? null : entity.CreateUserId.ToString(), + CreateBy = OmsRbacUserDisplayNameResolver.FormatUserId(entity.CreateUserId, auditUserNameMap), UpdateTime = OmsDocHelper.NullIfDefault(entity.UpdateTime), - UpdateBy = entity.UpdateUserId <= 0 ? null : entity.UpdateUserId.ToString() + UpdateBy = OmsRbacUserDisplayNameResolver.FormatUserId(entity.UpdateUserId, auditUserNameMap) }; } diff --git a/src/Oms.Application/Services/Channels/ServiceChannelService.cs b/src/Oms.Application/Services/Channels/ServiceChannelService.cs index 5738ef1..6fe41cd 100644 --- a/src/Oms.Application/Services/Channels/ServiceChannelService.cs +++ b/src/Oms.Application/Services/Channels/ServiceChannelService.cs @@ -20,6 +20,8 @@ public partial class ServiceChannelService : ApplicationService private IServiceChannelRepository ServiceChannelRepository => LazyServiceProvider.GetRequiredService(); private IServiceChannelGroupRepository ServiceChannelGroupRepository => LazyServiceProvider.GetRequiredService(); + private OmsRbacUserDisplayNameResolver UserDisplayNameResolver => + LazyServiceProvider.GetRequiredService(); /// /// 查询服务渠道列表。 @@ -67,7 +69,9 @@ public partial class ServiceChannelService : ApplicationService public async Task GetDetailAsync([FromQuery] int id) { var entity = await GetEntityOrThrowAsync(id); - return MapDetailOutput(entity, await GetGroupMapAsync()); + var auditUserNameMap = await UserDisplayNameResolver.ResolveManyAsync( + [entity.CreateUserId, entity.UpdateUserId]); + return MapDetailOutput(entity, await GetGroupMapAsync(), auditUserNameMap); } /// diff --git a/src/Oms.Application/Services/Customers/CustomerService.cs b/src/Oms.Application/Services/Customers/CustomerService.cs index ad04976..960df8d 100644 --- a/src/Oms.Application/Services/Customers/CustomerService.cs +++ b/src/Oms.Application/Services/Customers/CustomerService.cs @@ -32,6 +32,9 @@ public class CustomerService : ApplicationService private FakeRbacDbContext RbacDbContext => LazyServiceProvider.GetRequiredService(); + private OmsRbacUserDisplayNameResolver UserDisplayNameResolver => + LazyServiceProvider.GetRequiredService(); + /// /// 查询客户列表。 /// @@ -57,7 +60,16 @@ public class CustomerService : ApplicationService new Dictionary { [nameof(Customer.UpdateTime)] = false, [nameof(Customer.Id)] = false }); var totalCount = await CustomerRepository.CountAsync(predicate); - var items = entities.Select(entity => ObjectMapper.Map(entity)).ToList(); + var auditUserNameMap = await UserDisplayNameResolver.ResolveManyAsync( + entities.SelectMany(x => new[] { x.CreateUserId, x.UpdateUserId })); + var items = entities.Select(entity => + { + var output = ObjectMapper.Map(entity); + output.CreateBy = OmsRbacUserDisplayNameResolver.FormatUserId(entity.CreateUserId, auditUserNameMap); + output.UpdateBy = OmsRbacUserDisplayNameResolver.FormatUserId(entity.UpdateUserId, auditUserNameMap); + output.UpdateTime = OmsDocHelper.NullIfDefault(entity.UpdateTime); + return output; + }).ToList(); return new OmsPageResult { @@ -82,8 +94,10 @@ public class CustomerService : ApplicationService { var entity = await GetCustomerOrThrowAsync(id); var output = ObjectMapper.Map(entity); - output.CreateBy = entity.CreateUserId <= 0 ? null : entity.CreateUserId.ToString(); - output.UpdateBy = entity.UpdateUserId <= 0 ? null : entity.UpdateUserId.ToString(); + var auditUserNameMap = await UserDisplayNameResolver.ResolveManyAsync( + [entity.CreateUserId, entity.UpdateUserId]); + output.CreateBy = OmsRbacUserDisplayNameResolver.FormatUserId(entity.CreateUserId, auditUserNameMap); + output.UpdateBy = OmsRbacUserDisplayNameResolver.FormatUserId(entity.UpdateUserId, auditUserNameMap); output.UpdateTime = OmsDocHelper.NullIfDefault(entity.UpdateTime); return output; } diff --git a/src/Oms.Application/Services/Orders/OrderService.Helpers.cs b/src/Oms.Application/Services/Orders/OrderService.Helpers.cs index 137f2e9..2bad69e 100644 --- a/src/Oms.Application/Services/Orders/OrderService.Helpers.cs +++ b/src/Oms.Application/Services/Orders/OrderService.Helpers.cs @@ -69,6 +69,9 @@ public partial class OrderService : await CountryRepository.GetListAsync(x => countryCodes.Contains(x.CountryCode)); var countryMap = countries.ToDictionary(x => x.CountryCode, StringComparer.OrdinalIgnoreCase); + var auditUserNameMap = await UserDisplayNameResolver.ResolveManyAsync( + orders.SelectMany(x => new[] { x.CreateUserId, x.UpdateUserId })); + return orders.Select(order => { senderMap.TryGetValue(order.Id, out var sender); @@ -127,9 +130,9 @@ public partial class OrderService IsIntercept = order.IsIntercept > 0, IsRemote = order.IsRemote > 0, CreateTime = order.CreateTime, - CreateBy = order.CreateUserId <= 0 ? null : order.CreateUserId.ToString(), + CreateBy = OmsRbacUserDisplayNameResolver.FormatUserId(order.CreateUserId, auditUserNameMap), UpdateTime = OmsDocHelper.NullIfDefault(order.UpdateTime), - UpdateBy = order.UpdateUserId <= 0 ? null : order.UpdateUserId.ToString(), + UpdateBy = OmsRbacUserDisplayNameResolver.FormatUserId(order.UpdateUserId, auditUserNameMap), Sender = MapSender(sender), Receiver = MapReceiver(recipient), Items = (items ?? []).Select(MapDeclaration).ToList(), diff --git a/src/Oms.Application/Services/Orders/OrderService.cs b/src/Oms.Application/Services/Orders/OrderService.cs index ba469fa..9f8d548 100644 --- a/src/Oms.Application/Services/Orders/OrderService.cs +++ b/src/Oms.Application/Services/Orders/OrderService.cs @@ -28,6 +28,8 @@ public partial class OrderService : ApplicationService private IWarehouseAddressRepository WarehouseAddressRepository => LazyServiceProvider.GetRequiredService(); private ISalesProductRepository SalesProductRepository => LazyServiceProvider.GetRequiredService(); private IWebHostEnvironment WebHostEnvironment => LazyServiceProvider.GetRequiredService(); + private OmsRbacUserDisplayNameResolver UserDisplayNameResolver => + LazyServiceProvider.GetRequiredService(); /// /// 查询订单列表。 diff --git a/src/Oms.Application/Services/System/WarehouseService.cs b/src/Oms.Application/Services/System/WarehouseService.cs index eac127a..969501b 100644 --- a/src/Oms.Application/Services/System/WarehouseService.cs +++ b/src/Oms.Application/Services/System/WarehouseService.cs @@ -18,6 +18,9 @@ public class WarehouseService : ApplicationService { private IWarehouseAddressRepository WarehouseAddressRepository => LazyServiceProvider.GetRequiredService(); + private OmsRbacUserDisplayNameResolver UserDisplayNameResolver => + LazyServiceProvider.GetRequiredService(); + /// /// 查询仓库列表。 /// @@ -74,8 +77,10 @@ public class WarehouseService : ApplicationService { var entity = await GetWarehouseOrThrowAsync(id); var output = ObjectMapper.Map(entity); - output.CreateBy = entity.CreateUserId <= 0 ? null : entity.CreateUserId.ToString(); - output.UpdateBy = entity.UpdateUserId <= 0 ? null : entity.UpdateUserId.ToString(); + var auditUserNameMap = await UserDisplayNameResolver.ResolveManyAsync( + [entity.CreateUserId, entity.UpdateUserId]); + output.CreateBy = OmsRbacUserDisplayNameResolver.FormatUserId(entity.CreateUserId, auditUserNameMap); + output.UpdateBy = OmsRbacUserDisplayNameResolver.FormatUserId(entity.UpdateUserId, auditUserNameMap); output.UpdateTime = OmsDocHelper.NullIfDefault(entity.UpdateTime); return output; } diff --git a/src/Oms.Application/Shared/OmsRbacUserDisplayNameResolver.cs b/src/Oms.Application/Shared/OmsRbacUserDisplayNameResolver.cs new file mode 100644 index 0000000..8fe51d0 --- /dev/null +++ b/src/Oms.Application/Shared/OmsRbacUserDisplayNameResolver.cs @@ -0,0 +1,80 @@ +using Fake.DependencyInjection; +using Fake.Rbac.Infrastructure; +using Microsoft.EntityFrameworkCore; + +namespace Oms.Application.Shared; + +/// +/// 将 OMS 审计字段中的 RBAC mapId(user.userid)解析为展示名称。 +/// +public class OmsRbacUserDisplayNameResolver : IScopedDependency +{ + private readonly FakeRbacDbContext _rbacDbContext; + + public OmsRbacUserDisplayNameResolver(FakeRbacDbContext rbacDbContext) + { + _rbacDbContext = rbacDbContext; + } + + public async Task ResolveAsync(int userId, CancellationToken cancellationToken = default) + { + if (userId <= 0) + { + return null; + } + + var map = await ResolveManyAsync([userId], cancellationToken); + return FormatUserId(userId, map); + } + + public async Task> ResolveManyAsync( + IEnumerable userIds, + CancellationToken cancellationToken = default) + { + var ids = userIds.Where(x => x > 0).Distinct().ToList(); + if (ids.Count == 0) + { + return new Dictionary(); + } + + var sql = + $"SELECT `userid` AS `{nameof(RbacUserNameRow.UserId)}`, `Name` AS `{nameof(RbacUserNameRow.DisplayName)}` FROM `user` WHERE `IsDeleted` = 0 AND `userid` IN ({string.Join(",", ids)})"; + var rows = await _rbacDbContext.Database + .SqlQueryRaw(sql) + .ToListAsync(cancellationToken); + + return rows.ToDictionary( + x => x.UserId, + x => NormalizeDisplayName(x.DisplayName, x.UserId)); + } + + public static string? FormatUserId(int userId, IReadOnlyDictionary? nameMap) + { + if (userId <= 0) + { + return null; + } + + if (nameMap != null && + nameMap.TryGetValue(userId, out var name) && + !string.IsNullOrWhiteSpace(name)) + { + return name; + } + + return userId.ToString(); + } + + private static string NormalizeDisplayName(string? name, int userId) + { + var trimmed = name?.Trim(); + return string.IsNullOrWhiteSpace(trimmed) ? userId.ToString() : trimmed; + } + + private sealed class RbacUserNameRow + { + public int UserId { get; set; } + + public string? DisplayName { get; set; } + } +}