Загрузка данных
Кратко, понятно и по делу.
1) Роли — что и как работает
- Модель:
- Две роли: Admin и User. Хранятся в таблице Users в поле Role (NVARCHAR).
- Авторизация и передача роли:
- При логине LoginForm получает роль из БД и передаёт её в MainForm:
```csharp
string query = "SELECT Role FROM Users WHERE Login = @login AND Password = @password";
DataTable result = DatabaseHelper.ExecuteQuery(query, parameters);
if (result.Rows.Count > 0) {
string role = "User";
if (result.Columns.Contains("Role") && result.Rows[0]["Role"] != DBNull.Value)
role = result.Rows[0]["Role"].ToString();
var mainForm = new MainForm(role);
mainForm.ShowDialog();
}
```
- Применение роли в UI:
- MainForm имеет перегрузку конструктора MainForm(string role) и метод ApplyRole(), который включает/отключает элементы интерфейса в зависимости от роли.
- В текущей реализации:
- Для Admin: все пункты меню и кнопки доступны.
- Для User: отключены пункты управления справочниками и некоторые действия:
- citizensToolStripMenuItem.Enabled = isAdmin;
- employeesToolStripMenuItem.Enabled = isAdmin;
- servicesToolStripMenuItem.Enabled = isAdmin;
- employeeReportToolStripMenuItem.Enabled = isAdmin;
- servicesReportToolStripMenuItem.Enabled = isAdmin;
- btnDeleteRequest.Enabled = isAdmin;
- btnChangeStatus.Enabled = isAdmin;
- Экспорт доступен всем.
- Файлы с изменениями: VODSTROY\LoginForm.cs и VODSTROY\MainForm.cs.
- Практика администрирования:
- Управление ролями делаете напрямую в БД (UPDATE Users SET Role = 'Admin' WHERE Login = 'user1').
- Для демонстрации на защите: войти под admin (показать все функции) и под user (показать ограничения).
2) Два сложных запроса из раздела «Отчёты» — что и как работает (код + объяснение)
A. Отчёт по сотруднику (EmployeeReportForm.GenerateReport)
- Параметры: @employeeId, @dateFrom, @dateTo (дату подбирают в форме; dateTo привязан как до конца дня: AddDays(1).AddSeconds(-1)).
- SQL (в коде):
```sql
SELECT
r.RequestID as 'ID заявки',
s.ServiceName as 'Услуга',
c.FullName as 'Гражданин',
r.Address as 'Адрес',
CONVERT(VARCHAR, r.RequestDate, 104) as 'Дата заявки',
CONVERT(VARCHAR, r.DeadlineDate, 104) as 'Крайний срок',
r.Status as 'Статус'
FROM Requests r
JOIN Citizens c ON r.CitizenID = c.CitizenID
JOIN Services s ON r.ServiceID = s.ServiceID
WHERE r.EmployeeID = @employeeId
AND r.RequestDate BETWEEN @dateFrom AND @dateTo
ORDER BY r.RequestDate DESC
```
- Что делает:
- Выбирает все заявки, назначенные конкретному сотруднику в заданном периоде.
- Приводит даты к строковому формату dd.MM.yyyy (CONVERT VARCHAR, 104) для отображения в DataGridView.
- Результат заполняет dgvReport, обновляет lblTotalRequests = количество строк.
- Затем применяется цветовое кодирование строк по значению столбца "Статус":
- "Принята" → LightBlue
- "В работе" → LightYellow
- "Выполнена" → LightGreen
- "Отклонена" → LightCoral
- Экспорт:
- При экспорте в Excel (ClosedXML) заголовки, параметры отчёта и сами строки записываются в лист; для столбца «Статус» применяется такое же цветовое кодирование.
B. Отчёт по услугам (ServicesReportForm.GenerateReport)
- Параметры: @dateFrom, @dateTo и необязательный @status (если выбран фильтр статуса в форме).
- Ключевой SQL (упрощённо):
```sql
SELECT
s.ServiceName as 'Услуга',
COUNT(*) as 'Количество заявок',
COUNT(CASE WHEN r.Status = 'Выполнена' THEN 1 END) as 'Выполнено',
COUNT(CASE WHEN r.Status = 'В работе' THEN 1 END) as 'В работе',
COUNT(CASE WHEN r.Status = 'Принята' THEN 1 END) as 'Принято',
COUNT(CASE WHEN r.Status = 'Отклонена' THEN 1 END) as 'Отклонено',
CAST(
CASE WHEN COUNT(*) > 0
THEN (COUNT(CASE WHEN r.Status = 'Выполнена' THEN 1 END) * 100.0 / COUNT(*))
ELSE 0 END
AS DECIMAL(5,2)
) as '% выполнения'
FROM Services s
LEFT JOIN Requests r ON s.ServiceID = r.ServiceID
AND r.RequestDate BETWEEN @dateFrom AND @dateTo
-- если выбран статус, в запрос добавляется: AND r.Status = @status
GROUP BY s.ServiceID, s.ServiceName
HAVING COUNT(*) > 0
ORDER BY COUNT(*) DESC
```
- Объяснение:
- LEFT JOIN с фильтром по дате в условии JOIN: это позволяет для каждой услуги посчитать связанные заявки только за период; если у услуги нет заявок в периоде, по HAVING она будет отфильтрована (HAVING COUNT(*) > 0 — оставляет только услуги с хотя бы одной заявкой).
- COUNT(CASE WHEN … THEN 1 END) — стандартный приём для подсчёта количества заявок по статусам.
- '% выполнения' вычисляется как доля выполненных заявок: (выполнено * 100.0 / общее) и приводится к DECIMAL(5,2) — формат с двумя знаками после запятой.
- Если выбран фильтр статуса, в запрос добавляется условие AND r.Status = @status (параметризировано).
- Постобработка в UI:
- После получения DataTable подсчитываются итоги (общее количество заявок, число услуг).
- Визуальное форматирование:
- Колонка '% выполнения' получает формат "0.00".
- Цвет строк в зависимости от процента:
- >= 90% → LightGreen
- >= 70% → LightBlue
- < 50% → LightCoral
- Экспорт:
- При экспорте процент преобразуется в double и форматируется в Excel как 0.00; цветовое кодирование применяется аналогично.
Замечания по реализации и проверке
- Параметризация запросов — везде используется SqlParameter, поэтому риск SQL‑инъекции минимален.
- Даты: в коде форма использует dtpFrom.Value.Date и dtpTo.Value.Date.AddDays(1).AddSeconds(-1) для включения всего дня в конец периода.