feat(esppOrderLoader): изменено журналирвоание

This commit is contained in:
Mikhail Kuznetsov
2026-02-09 12:00:46 +10:00
parent 25e7fe4b90
commit 7dd40d1701
16 changed files with 434 additions and 252 deletions

View File

@@ -12,7 +12,6 @@ namespace PARR.DAL.Contracts
{
// При добавлении свойств, добавлять в миграцию: PARR.DAL.Context.DataContext
/// <summary>
/// Инициатор регламентной работы, указывается при создании шаблона в ЕСПП.
/// </summary>
@@ -51,8 +50,9 @@ namespace PARR.DAL.Contracts
{
return JsonSerializer.Deserialize<List<TemplateNameConstantPart>>(TemplateNameConstantParts) ?? new List<TemplateNameConstantPart>();
}
catch
catch (Exception ex)
{
// В будущем можно добавить логирование ошибки десериализации
return new List<TemplateNameConstantPart>();
}
}
@@ -66,7 +66,6 @@ namespace PARR.DAL.Contracts
}
}
/// <summary>
/// Количество попыток выполнения задания роботом
/// </summary>
@@ -77,21 +76,6 @@ namespace PARR.DAL.Contracts
/// </summary>
public TimeSpan RobotWaitTime { get; set; }
///// <summary>
///// Расписание регламентной работы - В каком часовом поясе
///// </summary>
//public string ScheduleTimezone { get; set; } = string.Empty;
///// <summary>
///// Расписание регламентной работы - Тип исключения
///// </summary>
//public string ScheduleExcludeType { get; set; } = string.Empty;
///// <summary>
///// Расписание регламентной работы - Календарь
///// </summary>
//public string ScheduleExcludeCalendar { get; set; } = string.Empty;
/// <summary>
/// Расписание регламентной работы - Диапазон повторов
/// </summary>
@@ -126,7 +110,5 @@ namespace PARR.DAL.Contracts
/// Зона ответственности для получения тайм зоны по умолчанию для расписаний
/// </summary>
public string DefaultResponseAreaToTimeOffset { get; set; } = string.Empty;
}
}
}

View File

@@ -14,7 +14,7 @@ namespace PARR.DAL.DomainServices.UnitFilterService;
internal class UnitFilterService : IUnitFilterService
{
#if DEBUG
private readonly Guid targetUnitId = Guid.Parse("87fc4c36-1ea8-4163-983f-1605fee1de99");
private readonly Guid targetUnitId = Guid.Parse("ca9b4a9f-df74-48e1-95dc-588e36e3d5aa");
#endif
private readonly ILogger<UnitFilterService> logger;

View File

@@ -25,29 +25,38 @@ namespace PARR.DAL.Services.Abstracts
public virtual async Task<bool> AddRangeAsync(List<T> objs)
{
logger.LogDebug("Начинаю добавление диапазона объектов типа {EntityType}, количество: {Count}",
typeof(T).Name, objs.Count);
objs.ForEach(item => item.DateCreated = DateTimeOffset.UtcNow);
try
{
await EntitySet.AddRangeAsync(objs);
logger.LogDebug("Успешно добавлено {Count} объектов типа {EntityType}",
objs.Count, typeof(T).Name);
return true;
}
catch (Exception ex)
{
logger.LogError(ex, "Ошибка при добавлении диапазона в БД.");
logger.LogError(ex, "Ошибка при добавлении диапазона объектов типа {EntityType}", typeof(T).Name);
return false;
}
}
public async Task<bool> CommitAsync(IHistoryInitiator? initiator = null)
{
logger.LogDebug("Начинаю сохранение изменений в БД для объектов типа {EntityType}", typeof(T).Name);
#region Изменения
var modifiedEntrities = EntitiContext.ChangeTracker.Entries()
.Where(t => t.State == EntityState.Modified/* || t.State == EntityState.Deleted*/);
logger.LogDebug("Найдено {Count} измененных сущностей для обработки истории", modifiedEntrities.Count());
foreach (var obj in modifiedEntrities)
{
// Если нунжо, обновляем дату изменения
// Если нужно, обновляем дату изменения
DateModifiedResolver(obj);
//Если нужно, пишем историю
@@ -59,12 +68,14 @@ namespace PARR.DAL.Services.Abstracts
try
{
await EntitiContext.SaveChangesAsync();
var changedCount = await EntitiContext.SaveChangesAsync();
logger.LogInformation("Успешно сохранено {ChangedCount} изменений в БД для объектов типа {EntityType}",
changedCount, typeof(T).Name);
return true;
}
catch (Exception ex)
{
logger.LogError(ex, "Ошибка при Commit");
logger.LogError(ex, "Ошибка при сохранении изменений в БД для объектов типа {EntityType}", typeof(T).Name);
return false;
}
}
@@ -76,10 +87,15 @@ namespace PARR.DAL.Services.Abstracts
/// <param name="initiator"></param>
private void SetInitiator(IHistoryInitiator? initiator)
{
logger.LogDebug("Устанавливаю инициатора для изменений");
// Задаем инициатора только для новых и измененных записей
var entrities = EntitiContext.ChangeTracker.Entries()
.Where(t => t.State == EntityState.Modified || t.State == EntityState.Added);
var entityCount = entrities.Count();
logger.LogDebug("Найдено {Count} сущностей для установки инициатора", entityCount);
// смотрим есть ли у объекта интерфейс IHistoryInitiator, если есть, задаём значения
foreach (var obj in entrities)
{
@@ -89,6 +105,8 @@ namespace PARR.DAL.Services.Abstracts
(obj.Entity as IHistoryInitiator)!.InitiatorIp = initiator?.InitiatorIp ?? null;
(obj.Entity as IHistoryInitiator)!.InitiatorParrComponentId = initiator?.InitiatorParrComponentId ?? null;
(obj.Entity as IHistoryInitiator)!.InitiatorComment = initiator?.InitiatorComment ?? null;
logger.LogDebug("Установлен инициатор для сущности типа {EntityType}", obj.Entity.GetType().Name);
}
}
}
@@ -100,11 +118,11 @@ namespace PARR.DAL.Services.Abstracts
/// <param name="obj"></param>
private void DateModifiedResolver(EntityEntry obj)
{
//if (obj.Entity is IBase)
// (obj.Entity as IBase)!.DateModified = DateTimeOffset.UtcNow;
if (obj.Entity is IBaseDateModified)
{
logger.LogDebug("Обновляю DateModified для сущности типа {EntityType}", obj.Entity.GetType().Name);
(obj.Entity as IBaseDateModified)!.DateModified = DateTimeOffset.UtcNow;
}
}
/// <summary>
@@ -113,6 +131,8 @@ namespace PARR.DAL.Services.Abstracts
/// <param name="obj"></param>
private void TableHistoryResolver(EntityEntry obj)
{
logger.LogDebug("Проверяю необходимость создания истории для сущности типа {EntityType}", obj.Entity.GetType().Name);
var myHistoryInterface = obj.Entity.GetType().GetInterfaces()
.Where(t => t.IsGenericType)
.Where(t => t.GetGenericTypeDefinition() == typeof(IMyHistory<>))
@@ -120,7 +140,10 @@ namespace PARR.DAL.Services.Abstracts
// у этого объекта нет интерфейса IMyHistory<>. Не ведем историю
if (myHistoryInterface == null)
{
logger.LogDebug("Сущность типа {EntityType} не требует ведения истории", obj.Entity.GetType().Name);
return;
}
// !!! Эта таблица хочет хранить историю !!!
@@ -128,10 +151,13 @@ namespace PARR.DAL.Services.Abstracts
var historyType = myHistoryInterface.GetGenericArguments().First();
var historyProps = historyType.GetProperties(/*BindingFlags.DeclaredOnly | */ /*BindingFlags.Public*/).ToList();
logger.LogDebug("Создаю историю для сущности типа {EntityType}, тип истории: {HistoryType}",
obj.Entity.GetType().Name, historyType.Name);
var historyInstance = Activator.CreateInstance(historyType);
if (historyInstance == null)
{
logger.LogError($"Не смог создать инстанс для ведения истории {historyType.Name}");
logger.LogError("Не смог создать инстанс для ведения истории {HistoryType}", historyType.Name);
return;
}
@@ -147,10 +173,11 @@ namespace PARR.DAL.Services.Abstracts
try
{
EntitiContext.Add(historyInstance);
logger.LogDebug("История добавлена для сущности типа {EntityType}", obj.Entity.GetType().Name);
}
catch (Exception ex)
{
logger.LogError(ex, $"Ошибка при добавлении объекта в историю {historyType.Name}");
logger.LogError(ex, "Ошибка при добавлении объекта в историю {HistoryType}", historyType.Name);
}
}
@@ -163,6 +190,8 @@ namespace PARR.DAL.Services.Abstracts
/// <param name="propsList"></param>
private void FillHistoryProps(EntityEntry originalObj, ref object historyInstance, List<PropertyInfo> propsList)
{
logger.LogDebug("Заполняю историю для сущности типа {EntityType}", originalObj.Entity.GetType().Name);
foreach (var prop in propsList)
{
if (originalObj.OriginalValues.Properties.FirstOrDefault(t => t.Name == prop.Name) == null)
@@ -185,6 +214,8 @@ namespace PARR.DAL.Services.Abstracts
var origValues = originalObj.Property(prop.Name).OriginalValue;
histProp.SetValue(historyInstance, origValues);
}
logger.LogDebug("Завершено заполнение истории для сущности типа {EntityType}", originalObj.Entity.GetType().Name);
}
@@ -200,14 +231,15 @@ namespace PARR.DAL.Services.Abstracts
var histProp = instanceObj.GetType().GetProperty(propName);
if (histProp == null)
{
logger.LogError($"При изменении объекта для БД, не найдено свойство ${propName}");
logger.LogError("При изменении объекта для БД, не найдено свойство {PropertyName}", propName);
return;
}
// сравним типы
if (histProp.PropertyType != typeof(TValue))
{
logger.LogError($"При изменении объекта для БД, не совпадают типы у свойства {propName}, {histProp.PropertyType.Name}!={typeof(TValue).Name}");
logger.LogError("При изменении объекта для БД, не совпадают типы у свойства {PropertyName}, {PropertyType}!={ValueType}",
propName, histProp.PropertyType.Name, typeof(TValue).Name);
return;
}
@@ -223,7 +255,7 @@ namespace PARR.DAL.Services.Abstracts
// return;
// var template = (Template)obj.Entity;
// // https://stackoverflow.com/questions/15012621/how-to-get-original-entity-from-changetracker
// // https://stackoverflow.com/questions/15012621/how-to-get-original-entity-from-changetracker
// var history = new TemplateHistory();
// // {
// history.Id = Guid.NewGuid();
@@ -273,74 +305,85 @@ namespace PARR.DAL.Services.Abstracts
public virtual async Task<bool> CreateAsync(T obj)
{
logger.LogDebug("Начинаю создание объекта типа {EntityType}", typeof(T).Name);
obj.DateCreated = DateTimeOffset.UtcNow;
try
{
await EntitySet.AddAsync(obj);
logger.LogDebug("Объект типа {EntityType} добавлен в контекст", typeof(T).Name);
return true;
}
catch (Exception ex)
{
logger.LogError(ex, "Ошибка при добавлении в БД.");
logger.LogError(ex, "Ошибка при добавлении объекта типа {EntityType} в БД", typeof(T).Name);
return false;
}
}
public virtual bool Delete(T obj)
{
logger.LogDebug("Начинаю удаление объекта типа {EntityType}", obj.GetType().Name);
try
{
EntitySet.Remove(obj);
logger.LogDebug("Объект типа {EntityType} удален из контекста", obj.GetType().Name);
return true;
}
catch (Exception ex)
{
logger.LogError(ex, "Ошибка при удалении из БД.");
logger.LogError(ex, "Ошибка при удалении объекта типа {EntityType} из БД", obj.GetType().Name);
return false;
}
}
public virtual async Task<bool> DeleteAsync(Guid id)
{
logger.LogDebug("Начинаю удаление объекта типа {EntityType} по ID: {Id}", typeof(T).Name, id);
try
{
var exist = await GetAsync(id);
if (exist == null)
{
logger.LogError($"Ошибка при удалении из БД. Не найдена запись в БД с id: {id}");
logger.LogError("Ошибка при удалении из БД. Не найдена запись в БД типа {EntityType} с id: {Id}",
typeof(T).Name, id);
return false;
}
EntitySet.Remove(exist);
logger.LogDebug("Объект типа {EntityType} с ID {Id} удален из контекста", typeof(T).Name, id);
return true;
}
catch (Exception ex)
{
logger.LogError(ex, "Ошибка при удалении из БД.");
logger.LogError(ex, "Ошибка при удалении объекта типа {EntityType} из БД по ID: {Id}", typeof(T).Name, id);
return false;
}
}
public virtual IQueryable<T> Get()
{
logger.LogDebug("Получаю набор объектов типа {EntityType}", typeof(T).Name);
return EntitySet;
}
public virtual async Task<T?> GetAsync(Guid id)
{
logger.LogDebug("Получаю объект типа {EntityType} по ID: {Id}", typeof(T).Name, id);
return await EntitySet.FirstOrDefaultAsync(t => t.Id == id);
}
public virtual IQueryable<T> GetPage(IQueryable<T> query, PaginationFilter paginationFilter)
{
//if (paginationFilter.IsDisabledPagination)
// return query;
logger.LogDebug("Получаю страницу объектов типа {EntityType}, страница: {PageNumber}, размер: {PageSize}",
typeof(T).Name, paginationFilter.PageNumber, paginationFilter.PageSize);
int skip = (paginationFilter.PageNumber - 1) * paginationFilter.PageSize;
return query.Skip(skip).Take(paginationFilter.PageSize);
}
}
}
}

View File

@@ -20,4 +20,4 @@ namespace PARR.DAL.Services.Implementations
protected override DataContext EntitiContext => dataContext;
}
}
}

View File

@@ -19,28 +19,38 @@ namespace PARR.DAL.Services.Implementations
public IQueryable<OrderStatus> Get()
{
logger.LogDebug("Получаю набор статусов нарядов");
return dataContext.OrderStatuses;
}
public OrderStatusEnum GetOrderStatusByName(string statusName)
{
logger.LogDebug("Поиск статуса наряда по имени: {StatusName}", statusName);
switch (statusName.Trim())
{
case "1-Направлен в группу":
logger.LogDebug("Найден статус: {StatusEnum}", OrderStatusEnum.New);
return OrderStatusEnum.New;
case "2-В работе":
logger.LogDebug("Найден статус: {StatusEnum}", OrderStatusEnum.InWork);
return OrderStatusEnum.InWork;
case "3-Приостановлен":
logger.LogDebug("Найден статус: {StatusEnum}", OrderStatusEnum.Stop);
return OrderStatusEnum.Stop;
case "4-Выполнен":
logger.LogDebug("Найден статус: {StatusEnum}", OrderStatusEnum.Complete);
return OrderStatusEnum.Complete;
case "5-Закрыт":
logger.LogDebug("Найден статус: {StatusEnum}", OrderStatusEnum.Closed);
return OrderStatusEnum.Closed;
default:
logger.LogWarning($"Не смог распознать статус наряда, вернул значение по умполчанию: {OrderStatusEnum.New.ToString()}, входящее значение: {statusName}");
logger.LogWarning(
"Не смог распознать статус наряда, вернул значение по умолчанию: {DefaultStatus}, входящее значение: {InputStatus}",
OrderStatusEnum.New, statusName);
return OrderStatusEnum.New;
}
}
}
}
}

View File

@@ -28,13 +28,28 @@ namespace PARR.DAL.Services.Implementations
public async Task<Template?> GetTemplateByNameAsync(string name)
{
return await GetWithIncludes()
logger.LogDebug("Поиск шаблона по имени: {TemplateName}", name);
var template = await GetWithIncludes()
.Include(t => t.RobotConfigurations)
.FirstOrDefaultAsync(t => t.Name == name);
if (template != null)
{
logger.LogDebug("Шаблон найден: {TemplateId}, имя: {TemplateName}", template.Id, template.Name);
}
else
{
logger.LogDebug("Шаблон с именем {TemplateName} не найден", name);
}
return template;
}
public IQueryable<Template> GetWithIncludes()
{
logger.LogDebug("Получаю шаблоны с include связями");
return Get()
.Include(h => h.Unit)
.ThenInclude(t => t!.UnitValues)
@@ -57,6 +72,8 @@ namespace PARR.DAL.Services.Implementations
public override Task<bool> CreateAsync(Template obj)
{
logger.LogDebug("Создание шаблона: {TemplateName}", obj.Name);
// добавление роботов для шаблона
obj.RobotConfigurations = new List<RobotConfiguration>
{
@@ -86,12 +103,16 @@ namespace PARR.DAL.Services.Implementations
}
};
logger.LogDebug("Добавлены роботы для шаблона {TemplateName}", obj.Name);
return base.CreateAsync(obj);
}
public async Task<Guid?> ReserveUnusedTemplateAsync(Guid newUnitId, HistoryInitiator initiator)
{
logger.LogDebug("Резервирую неиспользуемый шаблон для UnitId: {UnitId}", newUnitId);
var sql = @"
UPDATE ""Templates""
SET ""StatusTypeId"" = @NewStatus,
@@ -111,23 +132,43 @@ namespace PARR.DAL.Services.Implementations
var parameters = new[]
{
new NpgsqlParameter("@NewStatus", (int)TemplateStatusTypeEnum.Updating),
new NpgsqlParameter("@DateModified", DateTimeOffset.UtcNow),
new NpgsqlParameter("@InitiatorIp", initiator.InitiatorIp ?? (object)DBNull.Value),
new NpgsqlParameter("@InitiatorComponent",
initiator.InitiatorParrComponentId.HasValue
? (object)(int)initiator.InitiatorParrComponentId.Value
: DBNull.Value),
new NpgsqlParameter("@InitiatorComment", initiator.InitiatorComment ?? (object)DBNull.Value),
new NpgsqlParameter("@OldStatus", (int)TemplateStatusTypeEnum.Unused),
new NpgsqlParameter("@NewUnitId", newUnitId)
};
new NpgsqlParameter("@NewStatus", (int)TemplateStatusTypeEnum.Updating),
new NpgsqlParameter("@DateModified", DateTimeOffset.UtcNow),
new NpgsqlParameter("@InitiatorIp", initiator.InitiatorIp ?? (object)DBNull.Value),
new NpgsqlParameter("@InitiatorComponent",
initiator.InitiatorParrComponentId.HasValue
? (object)(int)initiator.InitiatorParrComponentId.Value
: DBNull.Value),
new NpgsqlParameter("@InitiatorComment", initiator.InitiatorComment ?? (object)DBNull.Value),
new NpgsqlParameter("@OldStatus", (int)TemplateStatusTypeEnum.Unused),
new NpgsqlParameter("@NewUnitId", newUnitId)
};
var result = await dataContext.Database
.SqlQueryRaw<Guid>(sql, parameters)
.ToListAsync();
try
{
var result = await dataContext.Database
.SqlQueryRaw<Guid>(sql, parameters)
.ToListAsync();
return result.FirstOrDefault();
var reservedTemplateId = result.FirstOrDefault();
if (reservedTemplateId != default(Guid))
{
logger.LogInformation("Успешно зарезервирован шаблон с ID: {TemplateId} для UnitId: {UnitId}",
reservedTemplateId, newUnitId);
}
else
{
logger.LogDebug("Не удалось зарезервировать шаблон для UnitId: {UnitId}", newUnitId);
}
return reservedTemplateId;
}
catch (Exception ex)
{
logger.LogError(ex, "Ошибка при резервировании шаблона для UnitId: {UnitId}", newUnitId);
throw;
}
}
}
}
}