diff --git a/PARR.EsppApi/Constants/EsppOperationsEnum.cs b/PARR.EsppApi/Constants/EsppOperationsEnum.cs new file mode 100644 index 00000000..aee8b55e --- /dev/null +++ b/PARR.EsppApi/Constants/EsppOperationsEnum.cs @@ -0,0 +1,28 @@ +namespace PARR.EsppApi.Constants +{ + /// + /// Операции в ЕСПП + /// + internal enum EsppOperationsEnum + { + /// + /// Списать трудозатраты (ТНК) + /// + AddMTNK, + + /// + /// Получить список нарядов + /// + GetTaskList, + + /// + /// Получить наряд + /// + GetTask, + + /// + /// Сохранить наряд + /// + SaveTask + } +} diff --git a/PARR.EsppApi/EsppApiService.cs b/PARR.EsppApi/EsppApiService.cs index a332bcd1..03d55f42 100644 --- a/PARR.EsppApi/EsppApiService.cs +++ b/PARR.EsppApi/EsppApiService.cs @@ -1,7 +1,10 @@ using Microsoft.Extensions.Logging; +using PARR.EsppApi.Constants; using PARR.EsppApi.Models; using PARR.EsppApi.Models.Query; +using PARR.EsppApi.Requests; using PARR.EsppApi.Settings; +using System.Text.Json; namespace PARR.EsppApi { @@ -18,27 +21,40 @@ namespace PARR.EsppApi this.esppHttpService = esppHttpService; } + public Task> SetStatusInWorkAsync(string recordId, string resultMsg)//TODO упростить можно без RecordIdQuery { return SetStatusAsync(recordId, "2-В работе", resultMsg); } + public Task> SetStatusIsDoneAsync(string recordId, string resultMsg)//TODO упростить можно без RecordIdQuery { return SetStatusAsync(recordId, "4-Выполнен", resultMsg); } + private async Task> SetStatusAsync(string recordId, string newStatusStr, string resultMsg) { //TODO assignee сразу назначаем принудительно, чтобы было понятно кто менял объект - var query = $"{{" + - $"\"recordid\":\"{recordId}\"," + - $"\"status\":\"{newStatusStr}\"," + - $"\"assignee\":\"{esppOrderSettings.AccountName}\"," + - $"\"resolution\":\"{resultMsg}\"" + - $"}}"; + //var query = $"{{" + + // $"\"recordid\":\"{recordId}\"," + + // $"\"status\":\"{newStatusStr}\"," + + // $"\"assignee\":\"{esppOrderSettings.AccountName}\"," + + // $"\"resolution\":\"{resultMsg}\"" + + // $"}}"; - var result = await esppHttpService.SendAsync(query); + //var result = await esppHttpService.SendAsync(QuerySanitize(query)); + + var request = new SetStatusRequest + { + recordid = recordId, + status = newStatusStr, + assignee = esppOrderSettings.AccountName, + resolution = resultMsg + }; + + var result = await esppHttpService.SendAsync(GenerateRequest(EsppOperationsEnum.SaveTask, request)); if (result.Data != null && result.Data.ErrorCode != "0") return new EsppResultBase(null, false, new Exception(result.Data.Message)); @@ -46,13 +62,21 @@ namespace PARR.EsppApi return result; } + public async Task> FindOrderByRecordIdAsync(string recordId) { - var result = await esppHttpService.SendAsync($"\r\n{{\"recordid\":\"{recordId}\"}}\r\n");//TODO упростить можно без RecordIdQuery + //TODO упростить можно без RecordIdQuery + //var query = $"\r\n{{\"recordid\":\"{recordId}\"}}\r\n"; + + //var result = await esppHttpService.SendAsync(QuerySanitize(query)); + + var request = new GetTaskRequest { recordid = recordId }; + var result = await esppHttpService.SendAsync(GenerateRequest(EsppOperationsEnum.GetTask, request)); return result; } + public async Task>> FindOrdersAsync(FindOrdersQuery query) { DateTime? generateDateStartWOTimeZone = query.GenerateDateStart.HasValue ? query.GenerateDateStart.Value.AddHours(esppOrderSettings.EsppUserTimeZone) : null; @@ -79,23 +103,79 @@ namespace PARR.EsppApi return new EsppResultBase>(null, false); } - var result = await esppHttpService.SendAsync>($"{{\"VIEW_QUERY\":\"{string.Join(" and ", conditions)}\"}}"); + //var request = $"{{\"VIEW_QUERY\":\"{string.Join(" and ", conditions)}\"}}"; + //var result = await esppHttpService.SendAsync>(QuerySanitize(request)); + + var request = new GetTaskListRequest { VIEW_QUERY = string.Join(" and ", conditions) }; + var result = await esppHttpService.SendAsync>(GenerateRequest(EsppOperationsEnum.GetTaskList, request)); return result; } + public async Task> AddMTnkAsync(AddMtnkQuery mtnk) { - var result = await esppHttpService.SendAsync( - $"{{\"recordid\":\"{mtnk.RecordId}\"," + - $"\"joboperation\":\"{mtnk.JobOperation}\",\"time\":\"{mtnk.Time}\",\"workscope\":\"{mtnk.Workspace}\"" + - $"}}"); + //var query = $"{{\"recordid\":\"{mtnk.RecordId}\"," + + // $"\"joboperation\":\"{mtnk.JobOperation}\",\"time\":\"{mtnk.Time}\",\"workscope\":\"{mtnk.Workspace}\"" + + // $"}}"; + + //var result = await esppHttpService.SendAsync(QuerySanitize(query)); + + var request = new AddMTNKRequest + { + recordid = mtnk.RecordId, + joboperation = mtnk.JobOperation, + time = mtnk.Time, + workscope = mtnk.Workspace.ToString() + }; + var result = await esppHttpService.SendAsync(GenerateRequest(EsppOperationsEnum.AddMTNK, request)); if (result.Data != null && result.Data.ErrorCode != "0") return new EsppResultBase(null, false, new Exception(result.Data.Message)); return result; } + + + /// + /// Убирает из запроса запрещенные символы + /// + /// + private string QuerySanitize(string str) + { + //str = str.Replace("\r\n", "
"); + + return str; + } + + + /// + /// Формирует запрос в ЕСПП из строки + /// + /// + /// + /// + private string GenerateRequest(EsppOperationsEnum operation, string query) + { + var baseRequest = $"requestValue"; + + return baseRequest.Replace("operationValue", operation.ToString()).Replace("requestValue", query); + } + + + /// + /// Формирует запрос в ЕСПП из jsonQuery + /// + /// + /// + /// + /// + private string GenerateRequest(EsppOperationsEnum operation, T jsonQuery) + { + var strQuery = JsonSerializer.Serialize(jsonQuery); + + return GenerateRequest(operation, strQuery); + } } } diff --git a/PARR.EsppApi/Requests/AddMTNKRequest.cs b/PARR.EsppApi/Requests/AddMTNKRequest.cs new file mode 100644 index 00000000..73f97c7d --- /dev/null +++ b/PARR.EsppApi/Requests/AddMTNKRequest.cs @@ -0,0 +1,17 @@ +namespace PARR.EsppApi.Requests +{ + /// + /// Списать трудозатраты + /// + internal class AddMTNKRequest + { + public required string recordid { get; set; } + + public required string joboperation { get; set; } + + public required string time { get; set; } + + public required string workscope { get; set; } + + } +} diff --git a/PARR.EsppApi/Requests/GetTaskListRequest.cs b/PARR.EsppApi/Requests/GetTaskListRequest.cs new file mode 100644 index 00000000..ecf49c50 --- /dev/null +++ b/PARR.EsppApi/Requests/GetTaskListRequest.cs @@ -0,0 +1,10 @@ +namespace PARR.EsppApi.Requests +{ + /// + /// Получить список нарядов + /// + internal class GetTaskListRequest + { + public required string VIEW_QUERY { get; set; } + } +} diff --git a/PARR.EsppApi/Requests/GetTaskRequest.cs b/PARR.EsppApi/Requests/GetTaskRequest.cs new file mode 100644 index 00000000..5b9c6ef3 --- /dev/null +++ b/PARR.EsppApi/Requests/GetTaskRequest.cs @@ -0,0 +1,10 @@ +namespace PARR.EsppApi.Requests +{ + /// + /// Получить наряд по номеру + /// + internal class GetTaskRequest + { + public required string recordid { get; set; } + } +} diff --git a/PARR.EsppApi/Requests/SetStatusRequest.cs b/PARR.EsppApi/Requests/SetStatusRequest.cs new file mode 100644 index 00000000..e4418912 --- /dev/null +++ b/PARR.EsppApi/Requests/SetStatusRequest.cs @@ -0,0 +1,16 @@ +namespace PARR.EsppApi.Requests +{ + /// + /// Изменить статус наряда + /// + internal class SetStatusRequest + { + public required string recordid { get; set; } + + public required string status { get; set; } + + public required string assignee { get; set; } + + public required string resolution { get; set; } + } +} diff --git a/PARR.EsppApi/Services/EsppHttpService.cs b/PARR.EsppApi/Services/EsppHttpService.cs index 64404a0c..b412d2ba 100644 --- a/PARR.EsppApi/Services/EsppHttpService.cs +++ b/PARR.EsppApi/Services/EsppHttpService.cs @@ -30,15 +30,12 @@ namespace PARR.EsppApi var responseString = await response.Content.ReadAsStringAsync(); - if (responseString.Contains("ErrorCode") && responseString.Contains("Message")) - { - logger.LogError(new Exception(responseString), $"Ошибка при выполнении запроса в ЕСПП. Тело запроса: {query}"); - - return new EsppResultBase(null, false, new Exception($"Espp error: {responseString}")); - } - logger.LogDebug($"Получил данные, строка: {responseString}"); + var errors = CheckErrors(responseString, query); + if (errors != null) + return errors; + var obj = JsonSerializer.Deserialize(responseString); @@ -51,5 +48,40 @@ namespace PARR.EsppApi return new EsppResultBase(null, false, ex); } } + + + /// + /// Проверка респонса на ошибки. В ЕСПП полный вест кост кастом + /// + /// + /// + private EsppResultBase? CheckErrors(string response, string query) where T : class + { + //Если вернет "ErrorCode":"0" - то все успешно прошло, если вернет другой ErrorCode, то не успешно + //Не всегда возвращается ErrorCode, если получали например наряд, он вернет тело наряда + if (response.Contains("ErrorCode") && response.Contains("Message")) + { + if (!response.Contains("\"ErrorCode\":\"0\"")) + { + logger.LogError(new Exception(response), $"Ошибка при выполнении запроса в ЕСПП. Тело запроса: {query}"); + + return new EsppResultBase(null, false, new Exception($"Espp error: {response}")); + } + + // ахахах! Может быть "ErrorCode":"0", и "Message":"Успешно", но в "MessageESPP":"err - Операция 'Прочее(работы)' не найденаЗапись \"Трудозатраты и Время в пути\" добавлена." + // "Message":"Успешно","ErrorCode":"0","MessageESPP":"err - Операция 'Прочее(работы)' не найденаЗапись \"Трудозатраты и Время в пути\" добавлена." + // т е это ошибка. рука лицо и привет разрабам ЕСПП + // делаем тупо, может стоит переделать + if (response.Contains("\"MessageESPP\":\"err")) + { + logger.LogError(new Exception(response), $"Ошибка при выполнении запроса в ЕСПП. Тело запроса: {query}"); + + return new EsppResultBase(null, false, new Exception($"Espp error: {response}")); + } + } + + return null; + } + } } diff --git a/PARR.EsppOrderManager/EsppOrderManager.cs b/PARR.EsppOrderManager/EsppOrderManager.cs index 0d9f508f..2bb877f2 100644 --- a/PARR.EsppOrderManager/EsppOrderManager.cs +++ b/PARR.EsppOrderManager/EsppOrderManager.cs @@ -47,7 +47,7 @@ namespace PARR.EsppOrderManager // throw new Exception("Ошибка при подключении к RabbitMq"); //test, and remove async in method name - await ManageOrderAsync("{\"OrderId\":\"77eeccfb-4ca0-443e-8cc7-f6aa9dc774b7\"}"); + await ManageOrderAsync("{\"OrderId\":\"dd04a997-e7b5-4326-b6e1-505719cc46fb\"}"); } public void Stop() @@ -149,11 +149,11 @@ namespace PARR.EsppOrderManager .OrderBy(t => t.DateCreated) .Select(t => $"{t.DateCreated} :: {t.AgentHistoryLevel?.Name} :: {t.Message}"); - var baseMsg = order.Template!.ApplicationsInWork!.Solution + "\r\n"; + var baseMsg = order.Template!.ApplicationsInWork!.Solution + "\n"; - var msgAgent = "Работы выполнены средствами автоматизации. \r\n" + - "Журнал работы: \r\n" + - $"{string.Join("\r\n", agentHistory)}"; + var msgAgent = "Работы выполнены средствами автоматизации. \n" + + "Журнал работы: \n" + + $"{string.Join("\n", agentHistory)}"; var msgAgentEmpty = "Работы должны были выполняться средствами автоматизации. " + "От средств автоматизации не поступило сообщений. " + diff --git a/PARR.EsppOrderManager/ParrEsppOrderManagerInstaller.cs b/PARR.EsppOrderManager/ParrEsppOrderManagerInstaller.cs index 66b92e1d..72836bdb 100644 --- a/PARR.EsppOrderManager/ParrEsppOrderManagerInstaller.cs +++ b/PARR.EsppOrderManager/ParrEsppOrderManagerInstaller.cs @@ -20,6 +20,10 @@ namespace PARR.EsppOrderManager configuration.GetSection(nameof(MqSettings)).Bind(mqSettings); services.AddSingleton(mqSettings); + var esppPoligonSettings = new EsppPoligonSettings(); + configuration.GetSection(nameof(EsppPoligonSettings)).Bind(esppPoligonSettings); + services.AddSingleton(esppPoligonSettings); + services.AddTransient(); services.AddTransient(); } diff --git a/PARR.EsppOrderManager/Services/EsppOrderService.cs b/PARR.EsppOrderManager/Services/EsppOrderService.cs index caee6076..bfe64bc5 100644 --- a/PARR.EsppOrderManager/Services/EsppOrderService.cs +++ b/PARR.EsppOrderManager/Services/EsppOrderService.cs @@ -3,6 +3,7 @@ using PARR.Constants; using PARR.DAL.Models; using PARR.EsppApi; using PARR.EsppApi.Models.Query; +using PARR.EsppOrderManager.Settings; namespace PARR.EsppOrderManager.Services { @@ -10,14 +11,17 @@ namespace PARR.EsppOrderManager.Services { private readonly ILogger logger; private readonly IEsppApiService esppApiService; + private readonly EsppPoligonSettings esppPoligonSettings; public EsppOrderService( ILogger logger, - IEsppApiService esppApiService + IEsppApiService esppApiService, + EsppPoligonSettings esppPoligonSettings ) { this.logger = logger; this.esppApiService = esppApiService; + this.esppPoligonSettings = esppPoligonSettings; } @@ -68,9 +72,15 @@ namespace PARR.EsppOrderManager.Services { JobOperation = order!.Template!.ApplicationsInWork!.Work!.Name, RecordId = order.Number, - Time = duration.ToString("HH:mm") + Time = duration.ToString("hh\\:mm") }; + if (esppPoligonSettings.IsEnabled) + { + //для теста, подемеяем значения из БД + tnk.JobOperation = esppPoligonSettings.TnkValue; + } + return await SetCompleteStatusAsync(order.Number, resultMsg, tnk); } else @@ -104,6 +114,8 @@ namespace PARR.EsppOrderManager.Services return false; } + else + logger.LogDebug($"В ЕСПП, для наряда {orderNumber} списаны трудозатраты. Операция: {tnk.JobOperation}, время: {tnk.Time}"); // выполняем наряд var result = await esppApiService.SetStatusIsDoneAsync(orderNumber, resultMsg); diff --git a/PARR.EsppOrderManager/Settings/EsppPoligonSettings.cs b/PARR.EsppOrderManager/Settings/EsppPoligonSettings.cs new file mode 100644 index 00000000..88ec89ef --- /dev/null +++ b/PARR.EsppOrderManager/Settings/EsppPoligonSettings.cs @@ -0,0 +1,19 @@ +namespace PARR.EsppOrderManager.Settings +{ + /// + /// Настройки смежных полигнов (продуктив/дев ЕСПП). Значения на полигонах могут отличаться. + /// Чтобы тестировать, подменяем значения из БД, берем из этих настроек. + /// + internal class EsppPoligonSettings + { + /// + /// Применить все настройки + /// + public bool IsEnabled { get; set; } + + /// + /// Значение ТНК, заменяет значение из БД. + /// + public string TnkValue { get; set; } = string.Empty; + } +} diff --git a/PARR.EsppOrderManagerWorker/appsettings.json b/PARR.EsppOrderManagerWorker/appsettings.json index a907daf7..9d2e92bf 100644 --- a/PARR.EsppOrderManagerWorker/appsettings.json +++ b/PARR.EsppOrderManagerWorker/appsettings.json @@ -38,5 +38,9 @@ "AccountName": "АВТО ТЕХНОЛОГ ПТК-ДВС (AUTO-PTK-DVS)", "Password": "123456789", "EsppUserTimeZone": 10 + }, + "EsppPoligonSettings": { + "IsEnabled": true, + "TnkValue": "Не учтена в перечне ТНК" } } diff --git a/PARR.Test/Worker.cs b/PARR.Test/Worker.cs index e1232f20..7aa69595 100644 --- a/PARR.Test/Worker.cs +++ b/PARR.Test/Worker.cs @@ -24,7 +24,7 @@ namespace PARR.Test private async Task TestEsppApi() { //var result = await esppApiService.FindOrdersAsync(new FindOrdersQuery { DescriptionContains = "Полное", GenerateDateStart = new DateTime(2023, 10, 10) , GenerateDateEnd = new DateTime(2023, 10, 24) }); - var recordId = "НАР23-00049787"; + var recordId = "НАР23-00046650"; var result = await esppApiService.FindOrderByRecordIdAsync(recordId); @@ -32,7 +32,7 @@ namespace PARR.Test { //var orders = result.Data; - var inWorkResult = await esppApiService.SetStatusInWorkAsync(recordId, "Решение..."); + var inWorkResult = await esppApiService.SetStatusInWorkAsync(recordId, "Решение...\nС новой строки"); if (!inWorkResult.IsSuccess) { _logger.LogError(inWorkResult.Exception, $"Не удалось взять в работу наряда{recordId}");