using CoreModels.Enums.Department; using DepartmentBusinessLogic.HelperModels; using DepartmentContract.BindingModels; using DepartmentContract.Logics.IGenericEntityLogic; using DepartmentContract.Services.IGenericEntityService; using DepartmentContract.ViewModels; using SecurityContract.BindingModels; using SecurityContract.Logics.IGenericEntityLogic; using SecurityContract.ViewModels; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Text.Json; using System.Threading.Tasks; using ToolsModule.ManagmentDependency; using ToolsModule.ManagmentEntity; using ToolsModule.ManagmentExtension; using ToolsModule.ManagmentSecurity; namespace DepartmentBusinessLogic.BusinessLogics.GenericBusinessLogic { /// /// Логика работы с историями синхронизации приказов /// public class OrderSyncHistoryBusinessLogic : GenericBusinessLogic, IOrderSyncHistoryLogic { private IOrderSyncHistoryRecordLogic _recordLogic; private IEnviromentSettingLogic _enviromentSettingLogic; private IAcademicPlanLogic _academicPlanLogic; private IStudentGroupLogic _groupsLogic; private IStudentLogic _studentLogic; private IUserLogic _userLogic; private IUserRoleLogic _userRoleLogic; private IRoleLogic _roleLogic; private IOrderLogic _orderLogic; private IOrderStudentRecordLogic _orderStudentRecordLogic; private OrderSyncHistoryViewModel _history; private StudentGroupListViewModel _groups; public OrderSyncHistoryBusinessLogic(IOrderSyncHistoryService service) : base(service, "Синхронизация Приказов", AccessOperation.СинхронизацияПриказов) { } /// /// Синхронизация приказов по всем студентам /// /// public async Task SyncOrders() { InitLogics(); // создание логов по операции синхронизации приказов if (!CreateHistory()) { return false; } // получение адреса сервера с приказами var address = GetAddress(); if (address == null) { return false; } // подключение клиента для отправки запросов к серверу var client = GetClinet(address); if (client == null) { return false; } // получение списка студентов var response = await client.GetAsync($"{address.Value}/univer/hs/Ulstu_StudentsInfo/v1/GetCurrentStudentsOfDepartment"); if (!response.IsSuccessStatusCode) { SaveLog("Не удалось получить список студентов с сервера"); return false; } var studentFromServer = JsonSerializer.Deserialize(await response.Content.ReadAsStringAsync()); if (studentFromServer == null || studentFromServer.CurrentStudentsList == null) { SaveLog("Не удалось распознать список приказов по студенту"); return false; } if (studentFromServer.CurrentStudentsList.Count == 0) { SaveLog("Полученный список студентов пустой"); return false; } _groups = GetStudentGroups(); var students = _studentLogic.GetList(new StudentGetBindingModel()); if (students == null || students.List == null) { SaveErrors(_studentLogic.Errors, "Ошибка получения данных", "Не удалось получить список студентов с базы"); return false; } foreach (var student in students.List) { var studentSync = studentFromServer.CurrentStudentsList.FirstOrDefault(x => x.recordBookName == student.NumberOfBook); // студент не найден, значит он ушел с кафедры, выясняем почему ИЛИ не совпадение групп if (studentSync == null || student.StudentGroupName != studentSync.groupName) { await SyncStudentOrders(student, client, address.Value); } studentFromServer.CurrentStudentsList.Remove(studentSync); } // новые студенты и восстановленцы foreach (var student in studentFromServer.CurrentStudentsList) { // восстановленцы var deletedStudent = _studentLogic.GetElement(new StudentGetBindingModel { NumberOfBook = student.recordBookName, IgnoreDeleted = true }); if (deletedStudent != null) { await SyncStudentOrders(deletedStudent, client, address.Value); continue; } // новые студенты var newStudent = CreateNewStudent(student); if (newStudent != null) { SaveLog($"Добавлен студент {newStudent}"); await SyncStudentOrders(newStudent, client, address.Value); } } foreach(var group in _groups.List) { var grStudents = _studentLogic.GetList(new StudentGetBindingModel { StudentGroupId = group.Id }); if (grStudents?.List?.Count == 0) { _groupsLogic.Delete(new StudentGroupGetBindingModel { Id = group.Id }); } } return true; } /// /// Синхронизация приказов по студенту /// /// /// public async Task SyncStudentOrders(Guid studentId) { InitLogics(); if (!CreateHistory()) { return false; } SaveLog("Синхронизация приказов по студенту"); var address = GetAddress(); if (address == null) { return false; } var client = GetClinet(address); if (client == null) { return false; } _groups = GetStudentGroups(); var student = _studentLogic.GetElement(new StudentGetBindingModel { Id = studentId }); if (student == null) { SaveErrors(_studentLogic.Errors, "Ошибка получения данных", "Не удалось получить студента с базы"); return false; } await SyncStudentOrders(student, client, address.Value); return true; } /// /// Инициализаия всех логик для получения от них данных и сохранения новых /// private void InitLogics() { _recordLogic = DependencyManager.Instance.Resolve(); _enviromentSettingLogic = DependencyManager.Instance.Resolve(); _academicPlanLogic = DependencyManager.Instance.Resolve(); _groupsLogic = DependencyManager.Instance.Resolve(); _studentLogic = DependencyManager.Instance.Resolve(); _orderLogic = DependencyManager.Instance.Resolve(); _orderStudentRecordLogic = DependencyManager.Instance.Resolve(); _userLogic = DependencyManager.Instance.Resolve(); _userRoleLogic = DependencyManager.Instance.Resolve(); _roleLogic = DependencyManager.Instance.Resolve(); } /// /// Создаение логера действий при синхронизации приказов /// /// private bool CreateHistory() { _history = Create(new OrderSyncHistorySetBindingModel { SyncDate = DateTime.Now }); if (_history == null) { Errors.Add(("Ошибка создание истории", "Не удалось создать историю")); return false; } return true; } /// /// Получение адреса сервера /// /// private EnviromentSettingViewModel GetAddress() { var address = _enviromentSettingLogic.GetList(new EnviromentSettingGetBindingModel { Key = "SyncStudentOrderIpAddress" })?.List?.FirstOrDefault(); if (address == null || address.Value.IsEmpty()) { SaveErrors(_enviromentSettingLogic.Errors, "Ошибка получения данных", "Не удалось получить адрес сервера для получения приказов по студентам"); return null; } return address; } /// /// Получение клиента для работы с сервисом приказов /// /// /// private HttpClient GetClinet(EnviromentSettingViewModel address) { var username = _enviromentSettingLogic.GetList(new EnviromentSettingGetBindingModel { Key = "SyncStudentOrderUserName" })?.List?.FirstOrDefault(); if (username == null || username.Value.IsEmpty()) { SaveErrors(_enviromentSettingLogic.Errors, "Ошибка получения данных", "Не удалось получить имя пользователя для получения приказов по студентам"); return null; } var password = _enviromentSettingLogic.GetList(new EnviromentSettingGetBindingModel { Key = "SyncStudentOrderPassword" })?.List?.FirstOrDefault(); if (password == null || password.Value.IsEmpty()) { SaveErrors(_enviromentSettingLogic.Errors, "Ошибка получения данных", "Не удалось получить пароль для получения приказов по студентам"); return null; } var client = new HttpClient { BaseAddress = new Uri(address.Value) }; client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username.Value}:{password.Value}"))); return client; } /// /// Получение списка групп /// /// private StudentGroupListViewModel GetStudentGroups() { var sgs = _groupsLogic.GetList(new StudentGroupGetBindingModel()); if (sgs == null || sgs.List == null) { SaveErrors(_groupsLogic.Errors, "Ошибка получения данных", "Не удалось получить список групп с базы"); return null; } return sgs; } /// /// Получение пользователя-студента /// /// /// private UserViewModel WorkWithUser(StudentSyncModel student) { var userName = $"{student.lastName}{(student.firstName.IsNotEmpty() ? $" {student.firstName[0]}." : string.Empty)}{(student.patronymicName.IsNotEmpty() ? $"{student.patronymicName[0]}." : string.Empty)}"; var user = _userLogic.GetOrCreateUser(new UserGetBindingModel { Login = student.recordBookName }, userName); if (user == null) { if (_userLogic.Errors.Count > 0) { SaveErrors(_userLogic.Errors, "Ошибка получения пользователя под студента", $"Не удалось получить пользователя под студента {student.lastName} {student.firstName} {student.patronymicName}"); return null; } } var role = _roleLogic.GetElement(new RoleGetBindingModel { RoleName = "Студент" }); if (role == null) { if (_roleLogic.Errors.Count > 0) { SaveErrors(_roleLogic.Errors, "Ошибка получения роли студента", $"Не удалось получить роль под студента {student.lastName} {student.firstName} {student.patronymicName}"); return null; } } var link = _userRoleLogic.GetElement(new UserRoleGetBindingModel { RoleId = role.Id, UserId = user.Id }); if (link == null) { var manager = DependencyManager.Instance.Resolve(); _userRoleLogic.Create(new UserRoleSetBindingModel { RoleId = role.Id, UserId = user.Id, UserIdForAccess = manager?.User }); if (_userRoleLogic.Errors.Count > 0) { SaveErrors(_userRoleLogic.Errors, "Ошибка создания привязки студента к роли", $"Не удалось привязать студента {student.lastName} {student.firstName} {student.patronymicName} к роли"); return null; } } return user; } /// /// Создание нового студента /// /// /// private StudentViewModel CreateNewStudent(StudentSyncModel student) { var user = WorkWithUser(student); if (user == null) { return null; } var newStudent = _studentLogic.Create(new StudentSetBindingModel { Iduniv = student.iduniv, NumberOfBook = student.recordBookName, UserId = user.Id, FirstName = student.firstName, LastName = student.lastName, Patronymic = student.patronymicName, StudentState = StudentState.Неопределен, Description = string.Empty }); if (newStudent == null) { SaveErrors(_studentLogic.Errors, "Ошибка добавления студента", $"Не удалось добавить студента {student.lastName} {student.firstName} {student.patronymicName}"); return null; } return newStudent; } /// /// Синхронизация приказов по студенту /// /// /// /// /// /// /// private async Task SyncStudentOrders(StudentViewModel student, HttpClient client, string address) { var response = await client.GetAsync($"{address}/univer/hs/Ulstu_StudentsInfo/v1/GetStudentOrdersByIdAndRecordBook?iduniv={student.Iduniv}&recordBookName={student.NumberOfBook}&allOrders=sppd"); if (!response.IsSuccessStatusCode) { SaveLog("Не удалось получить список приказов по студенту"); return; } var syncOrders = JsonSerializer.Deserialize(await response.Content.ReadAsStringAsync()); if (syncOrders == null || syncOrders.StudentOrders == null) { SaveLog("Не удалось распознать список приказов по студенту"); return; } if (student.LastName == "Костенко" || student.LastName == "Киселева") { int c = 10; } foreach (var syncOrder in syncOrders.StudentOrders) { if (syncOrder.orderTypeName == "Утверждение тем курсовых работ" || syncOrder.orderTypeName == "Утверждение тем ВКР" || syncOrder.orderTypeName == "Назначение стипендии") { continue; } if (syncOrder.markOfApprove.ToLower() != "true") { SaveLog($"Приказ {syncOrder.clericNumber} от {syncOrder.clericDate} ({syncOrder.orderTypeName}) по студенту {student} не утврежден"); continue; } if (syncOrder.clericNumber.IsEmpty()) { SaveLog($"Приказ без номера от {syncOrder.clericDate} ({student})"); continue; } if (syncOrder.clericDate.IsEmpty()) { SaveLog($"Приказ {syncOrder.clericNumber} - неизвестная дата ({student})"); continue; } var orderType = GetOrderType(syncOrder.orderTypeName); // пропускаем приказы, которые нас не интересуют if (orderType == OrderType.Игнорировать) { continue; } if (orderType == OrderType.Неопределено) { if (syncOrder.orderTypeName != "Корректировка" && syncOrder.orderTypeName != "Смена ФИО") { SaveLog($"Приказ {syncOrder.clericNumber} необрабатываемого типа {syncOrder.orderTypeName} по студенту {student}"); } continue; } // пытаемся найти приказ var order = _orderLogic.GetElement(new OrderGetBindingModel { OrderNumber = syncOrder.clericNumber, OrderDate = Convert.ToDateTime(syncOrder.clericDate).Date, OrderType = orderType }); if (order == null) { // если не нашли - пытаемся создать order = _orderLogic.Create(new OrderSetBindingModel { OrderNumber = syncOrder.clericNumber, OrderDate = Convert.ToDateTime(syncOrder.clericDate).Date, OrderType = orderType }); if (order == null) { SaveErrors(_orderLogic.Errors, "Ошибка добавления приказа", $"Не удалось добавить приказ {syncOrder.clericNumber} {syncOrder.clericDate}"); continue; } } var studentGroupFrom = GetStudentGroup(syncOrder.groupNameBefore, order, false); var studentGroupTo = GetStudentGroup(syncOrder.groupNameAfter, order, syncOrder.orderSubTypeName == "Распределение по группам"); // игнорируем приказы, не связанные с нашими группами if (studentGroupFrom == null && studentGroupTo == null && order.OrderType != OrderType.ЗачислениеСтудентов && syncOrder.reason != "на другой факультет") { continue; } var orderStudentMoveType = GetOrderStudentMoveType(student, syncOrder, studentGroupFrom, studentGroupTo, out string info); if (orderStudentMoveType == OrderStudentMoveType.Неопределено) { SaveLog($"Неизветсный приказ {syncOrder.orderTypeName} ({syncOrder.reason}) по студенту {student}"); continue; } // ищем в приказе запись по студенту var exsistStudentOrder = _orderStudentRecordLogic.GetElement(new OrderStudentRecordGetBindingModel { OrderId = order.Id, StudentId = student.Id, OrderStudentMoveType = orderStudentMoveType }); // если такой приказ по студенту уже есть, просто пропускаем if (exsistStudentOrder != null) { continue; } // создаем, если не нашли var studentOrder = _orderStudentRecordLogic.Create(new OrderStudentRecordSetBindingModel { OrderId = order.Id, StudentId = student.Id, Info = info, OrderStudentMoveType = orderStudentMoveType, StudentGroupFromId = studentGroupFrom?.Id, StudentGroupToId = studentGroupTo?.Id, StudentModel = student }); if (studentOrder == null) { SaveErrors(_orderStudentRecordLogic.Errors, "Ошибка добавления записи приказа по студенту", $"Не удалось добавить запись приказа {syncOrder.orderSubTypeName} по студенту {student}"); continue; } SaveLog($"Добавили запись к приказу {order.OrderNumber} по студенту {student} с формулировкой {info}"); } } /// /// Определение типа приказа /// /// /// private static OrderType GetOrderType(string orderTitle) => orderTitle switch { "Зачисление в вуз вне приемной кампании" => OrderType.ЗачислениеСтудентов, "Зачисление в вуз" => OrderType.ЗачислениеСтудентов, "Перевод из другого вуза" => OrderType.ЗачислениеСтудентов, "Перевод" => OrderType.Перевод, "Перевод на следующий курс" => OrderType.ДвижениеСтудентов, "Завершение обучения" => OrderType.Отчисление, "Уход в академический отпуск" => OrderType.ДвижениеСтудентов, "Продление академического отпуска" => OrderType.ДвижениеСтудентов, "Восстановление из академического отпуска" => OrderType.ДвижениеСтудентов, "Отчисление" => OrderType.Отчисление, "Восстановление" => OrderType.Перевод, "Выпуск" => OrderType.ДвижениеСтудентов, "Утверждение тем ВКР" => OrderType.Игнорировать, _ => OrderType.Неопределено, }; /// /// Поиск группы по названию из приказа /// /// /// /// /// private StudentGroupViewModel GetStudentGroup(string studetnGroupName, OrderViewModel order, bool isGrouping) { if (string.IsNullOrEmpty(studetnGroupName)) { return null; } studetnGroupName = studetnGroupName.Replace("пбд", "бд");// когда-то были группы-прикладники int orderStudyYear = order.OrderDate.Year + (order.OrderDate.Month / 7) - 1; // учебный год var group = _groups.List.FirstOrDefault(x => GetGroupName(x, orderStudyYear) == studetnGroupName); if (group == null && isGrouping) { var academicPlan = _academicPlanLogic.GetElement(new AcademicPlanGetBindingModel { EducationDirectionShortName = studetnGroupName.Split('-')[0], Year = orderStudyYear }); if (academicPlan == null) { SaveLog($"Не найден учебный план для группы {studetnGroupName} за {orderStudyYear} год"); return null; } int yearFinish = academicPlan.EducationDirectionQualification == EducationDirectionQualification.Бакалавриат ? orderStudyYear + 4 : academicPlan.EducationDirectionQualification == EducationDirectionQualification.Магистратура ? orderStudyYear + 2 : 0; var newGroup = _groupsLogic.Create(new StudentGroupSetBindingModel { AcademicPlanId = academicPlan.Id, GroupNumber = Convert.ToInt32(studetnGroupName[^1].ToString()), LecturerId = null, YearEntrance = orderStudyYear, YearFinish = yearFinish }); if (newGroup == null) { SaveErrors(_groupsLogic.Errors, "Ошибка созданий группы", "Не удалось создать группу"); return null; } _groups = GetStudentGroups(); group = _groups.List.FirstOrDefault(x => GetGroupName(x, orderStudyYear) == studetnGroupName); } return group; } /// /// Получение именни группы в зависимости от нужного года /// /// /// /// private static string GetGroupName(StudentGroupViewModel group, int orderStudyYear) { var course = orderStudyYear > group.YearFinish ? AcademicCourse.Неопределен : (orderStudyYear - group.YearEntrance) switch { 0 => AcademicCourse.Курс_1, 1 => AcademicCourse.Курс_2, 2 => AcademicCourse.Курс_3, 3 => AcademicCourse.Курс_4, 4 => AcademicCourse.Курс_5, _ => AcademicCourse.Неопределен, }; return $"{group.EducationDirectionShortName}-{((int)course)}{group.GroupNumber}"; } /// /// Определение подтипа приказа /// /// /// /// /// /// /// private static OrderStudentMoveType GetOrderStudentMoveType(StudentViewModel student, StudentOrderSyncModel syncOrder, StudentGroupViewModel studentGroupFrom, StudentGroupViewModel studentGroupTo, out string info) { switch (syncOrder.orderTypeName) { case "Перевод": if (syncOrder.orderSubTypeName == "Распределение по группам") { info = $"Распределение студента {student} в группу {studentGroupTo}"; return OrderStudentMoveType.Распределить; } if (studentGroupFrom != null && studentGroupTo != null) { info = $"Перевод студента {student} из группы {studentGroupFrom} в группу {studentGroupTo}"; return OrderStudentMoveType.ПеревестиНаДругоеНаправлениеКафедры; } if (studentGroupFrom == null && studentGroupTo != null) { info = $"Перевод студента {student} c группы {syncOrder.groupNameBefore} другой кафедры в группу {studentGroupTo}"; return OrderStudentMoveType.ПринятьПоПереводу; } if (studentGroupFrom != null && studentGroupTo == null) { info = $"Отчисление студента {student} из группы {studentGroupFrom} в связи с переводом"; return OrderStudentMoveType.ОтчислитьВСвязиСПереводом; } info = string.Empty; return OrderStudentMoveType.Неопределено; case "Зачисление в вуз вне приемной кампании": info = $"Зачисление студента {student} по приказу"; return OrderStudentMoveType.ЗачислитьПоПриказу; case "Зачисление в вуз": info = $"Зачисление студента {student} по приказу"; return OrderStudentMoveType.ЗачислитьПоПриказу; case "Перевод из другого вуза": info = $"Перевод студента {student} с другого вуза"; return OrderStudentMoveType.ПринятьПоПереводусДругогоВуза; case "Перевод на следующий курс": info = $"Перевод студента {student} из группы {studentGroupFrom} на следующий курс в группу {studentGroupTo}"; return OrderStudentMoveType.ПеревестиНаСтаршийКурс; case "Уход в академический отпуск": DateTime? date = null; if (syncOrder.dateEnd.IsNotEmpty()) { date = Convert.ToDateTime(syncOrder.dateEnd); } info = $"Уход в АО студента {student} из группы {studentGroupFrom} до {date?.ToShortDateString() ?? string.Empty}"; return OrderStudentMoveType.ОтправитьВАкадем; case "Продление академического отпуска": info = $"Продление АО студента {student}"; return OrderStudentMoveType.ПродлитьАкадем; case "Восстановление из академического отпуска": info = $"Выход из АО студента {student} в группу {studentGroupTo}"; return OrderStudentMoveType.ВосстановитьИзАкадема; case "Отчисление": if (syncOrder.reason.Contains("за невыполнение учебного плана") || syncOrder.reason.Contains("академическую неуспеваемость") || syncOrder.reason.Contains("в связи с невыполнением обучающимся обязанностей по добросовестному освоению") || syncOrder.reason.Contains("за невыполнение обязанностей по добросовестному освоению")) { info = $"Отчисление студента {student} из группы {studentGroupFrom} за неуспеваемость"; return OrderStudentMoveType.ОтчислитьЗаНеуспевамость; } if (syncOrder.reason.Contains("по собственному желанию")) { info = $"Отчисление студента {student} из группы {studentGroupFrom} по собственному желанию"; return OrderStudentMoveType.ОтчислитьПоСобственному; } if (syncOrder.reason.Contains("в связи с невыходом из академического отпуска")) { info = $"Отчисление студента {student} из группы {studentGroupFrom} в связи с невыходм из академического отпуска"; return OrderStudentMoveType.ОтчислитьЗаНевыходСАкадема; } if (syncOrder.reason.Contains("в связи с переводом в")) { info = $"Отчисление студента {student} из группы {studentGroupFrom} в связи с переводом"; return OrderStudentMoveType.ОтчислитьВСвязиСПереводом; } if (syncOrder.reason.Contains("за невыполнение условий договора")) { info = $"Отчисление студента {student} из группы {studentGroupFrom} в связи с не оплатой обучения"; return OrderStudentMoveType.ОтчислитьЗаНеоплату; } info = string.Empty; return OrderStudentMoveType.Неопределено; case "Восстановление": info = $"Восстановление отчисленного студента {student} в группу {studentGroupTo}"; return OrderStudentMoveType.Восстановить; case "Выпуск": info = $"Завершение обучения студента {student}"; return OrderStudentMoveType.ОтчислитьПоЗавершению; default: info = string.Empty; return OrderStudentMoveType.Неопределено; } } /// /// Сохранение лога с ошибками /// /// /// /// private void SaveErrors(List<(string Title, string Message)> errors, string title, string message) { if (_history == null) { return; } Errors = errors ?? new List<(string Title, string Message)>(); Errors.Add((title, message)); SaveLog(string.Join(Environment.NewLine, Errors.Select(x => x.Message))); } /// /// Сохранение лога /// /// private void SaveLog(string info) { _recordLogic.Create(new OrderSyncHistoryRecordSetBindingModel { OrderSyncHistoryId = _history.Id, Information = info }); } } }