using Fake.DependencyInjection; using Fake.Security.Claims; using Fake.Users; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Oms.Application.Dtos.Orders; using Oms.Application.Shared; using Oms.Domain.OrdersAggregate.Consts; using Oms.Domain.OrdersAggregate.Entities; using Oms.Domain.OrdersAggregate.Repositories; namespace Oms.Application.Services.Orders; /// /// 运单操作日志写入器:字段 diff 落库;失败不影响主业务。 /// public class OrderOperationLogWriter : IScopedDependency { private readonly IOrderOperationLogRepository _repository; private readonly ICurrentUser _currentUser; private readonly OmsRbacUserDisplayNameResolver _userDisplayNameResolver; private readonly ILogger _logger; public OrderOperationLogWriter( IOrderOperationLogRepository repository, ICurrentUser currentUser, OmsRbacUserDisplayNameResolver userDisplayNameResolver, ILogger logger) { _repository = repository; _currentUser = currentUser; _userDisplayNameResolver = userDisplayNameResolver; _logger = logger; } public Task TryWriteAsync( Order order, string action, IReadOnlyDictionary before, IReadOnlyDictionary after, string? remark = null) => TryWriteAsync(order, action, before, after, remark, operatorId: null, operatorName: null); public async Task TryWriteAsync( Order order, string action, IReadOnlyDictionary before, IReadOnlyDictionary after, string? remark, int? operatorId, string? operatorName, string operatorType = "staff", string source = "pc") { try { var changes = OrderOperationLogSnapshot.Diff(before, after); var (resolvedId, resolvedName) = await ResolveOperatorAsync(operatorId, operatorName); var entity = new OrderOperationLog { OrderId = order.Id, ServiceNo = order.ServiceNo, Action = action, ActionName = OrderOperationActionCodes.GetActionName(action), OperatorId = resolvedId, OperatorName = resolvedName, OperatorType = string.IsNullOrWhiteSpace(operatorType) ? "staff" : operatorType.Trim(), Source = string.IsNullOrWhiteSpace(source) ? "pc" : source.Trim(), ChangesJson = JsonConvert.SerializeObject( changes.Select(x => new OrderOperationLogChangeItem { Path = x.Path, Label = x.Label, Before = x.Before, After = x.After }).ToList()), Remark = string.IsNullOrWhiteSpace(remark) ? null : remark.Trim(), CreateUserId = resolvedId, CreateTime = DateTime.Now }; await _repository.InsertAsync(entity); } catch (Exception ex) { _logger.LogError( ex, "写入运单操作日志失败 OrderId={OrderId} Action={Action}", order.Id, action); } } /// /// 无字段 diff 时写入叙事日志(打印、下游预报成功等)。 /// public Task TryWriteSimpleAsync( Order order, string action, string? remark, int? operatorId = null, string? operatorName = null, string operatorType = "staff", string source = "pc") => TryWriteAsync( order, action, new Dictionary(StringComparer.OrdinalIgnoreCase), new Dictionary(StringComparer.OrdinalIgnoreCase), remark, operatorId, operatorName, operatorType, source); public async Task ListByOrderIdAsync(int orderId) { var rows = await _repository.GetListAsync(x => x.OrderId == orderId); var items = rows .OrderByDescending(x => x.CreateTime) .ThenByDescending(x => x.Id) .Select(MapOutput) .ToList(); return new OrderOperationLogListOutput { Items = items }; } private async Task<(int OperatorId, string? OperatorName)> ResolveOperatorAsync( int? operatorId, string? operatorName) { if (operatorId is > 0) { var name = string.IsNullOrWhiteSpace(operatorName) ? await _userDisplayNameResolver.ResolveAsync(operatorId.Value) : operatorName.Trim(); return (operatorId.Value, name); } if (_currentUser.IsAuthenticated) { var id = _currentUser.GetId(); var name = string.IsNullOrWhiteSpace(operatorName) ? await _userDisplayNameResolver.ResolveAsync(id) : operatorName.Trim(); return (id, name); } return (0, string.IsNullOrWhiteSpace(operatorName) ? null : operatorName.Trim()); } private static OrderOperationLogOutput MapOutput(OrderOperationLog row) { var changes = new List(); if (!string.IsNullOrWhiteSpace(row.ChangesJson)) { try { changes = JsonConvert.DeserializeObject>(row.ChangesJson) ?? []; } catch { changes = []; } } var views = changes .Select(x => new OrderOperationLogSnapshot.OrderOperationLogChangeView( x.Path, x.Label, x.Before, x.After)) .ToList(); return new OrderOperationLogOutput { Id = row.Id, OrderId = row.OrderId, ServiceNo = row.ServiceNo, Action = row.Action, ActionName = row.ActionName, OperatorId = row.OperatorId, OperatorName = row.OperatorName, OperatorType = row.OperatorType, Source = row.Source, Changes = changes, Remark = row.Remark, Summary = OrderOperationLogSnapshot.BuildSummary(views, row.Remark), CreateTime = row.CreateTime }; } }