All checks were successful
Build and Deploy / deploy (push) Successful in 1m3s
Co-authored-by: Cursor <cursoragent@cursor.com>
194 lines
6.5 KiB
C#
194 lines
6.5 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// 运单操作日志写入器:字段 diff 落库;失败不影响主业务。
|
|
/// </summary>
|
|
public class OrderOperationLogWriter : IScopedDependency
|
|
{
|
|
private readonly IOrderOperationLogRepository _repository;
|
|
private readonly ICurrentUser _currentUser;
|
|
private readonly OmsRbacUserDisplayNameResolver _userDisplayNameResolver;
|
|
private readonly ILogger<OrderOperationLogWriter> _logger;
|
|
|
|
public OrderOperationLogWriter(
|
|
IOrderOperationLogRepository repository,
|
|
ICurrentUser currentUser,
|
|
OmsRbacUserDisplayNameResolver userDisplayNameResolver,
|
|
ILogger<OrderOperationLogWriter> logger)
|
|
{
|
|
_repository = repository;
|
|
_currentUser = currentUser;
|
|
_userDisplayNameResolver = userDisplayNameResolver;
|
|
_logger = logger;
|
|
}
|
|
|
|
public Task TryWriteAsync(
|
|
Order order,
|
|
string action,
|
|
IReadOnlyDictionary<string, string?> before,
|
|
IReadOnlyDictionary<string, string?> after,
|
|
string? remark = null)
|
|
=> TryWriteAsync(order, action, before, after, remark, operatorId: null, operatorName: null);
|
|
|
|
public async Task TryWriteAsync(
|
|
Order order,
|
|
string action,
|
|
IReadOnlyDictionary<string, string?> before,
|
|
IReadOnlyDictionary<string, string?> 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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 无字段 diff 时写入叙事日志(打印、下游预报成功等)。
|
|
/// </summary>
|
|
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<string, string?>(StringComparer.OrdinalIgnoreCase),
|
|
new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase),
|
|
remark,
|
|
operatorId,
|
|
operatorName,
|
|
operatorType,
|
|
source);
|
|
|
|
public async Task<OrderOperationLogListOutput> 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<int>();
|
|
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<OrderOperationLogChangeItem>();
|
|
if (!string.IsNullOrWhiteSpace(row.ChangesJson))
|
|
{
|
|
try
|
|
{
|
|
changes = JsonConvert.DeserializeObject<List<OrderOperationLogChangeItem>>(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
|
|
};
|
|
}
|
|
}
|