using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using ListmonkIntegration.Services; using ListmonkIntegration.Models; using ClosedXML.Excel; namespace ListmonkIntegration.Pages.Subscribers { public class UploadModel : PageModel { private readonly IListmonkService _listmonkService; private readonly ILogger _logger; [BindProperty] public IFormFile? excelFile { get; set; } public List Subscribers { get; set; } = new List(); public List ColumnHeaders { get; set; } = new List(); public string Message { get; set; } = string.Empty; public bool IsSuccess { get; set; } public UploadModel(IListmonkService listmonkService, ILogger logger) { _listmonkService = listmonkService; _logger = logger; } public void OnGet() { } public async Task OnPostAsync(string listName) { try { _logger.LogInformation("=== Upload started ==="); if (excelFile == null || excelFile.Length == 0) { Message = "Файл не выбран"; IsSuccess = false; return Page(); } _logger.LogInformation($"File received: {excelFile.FileName}, Size: {excelFile.Length} bytes"); // Ограничение размера файла (10 MB) if (excelFile.Length > 10 * 1024 * 1024) { Message = "Файл слишком большой (максимум 10 MB)"; IsSuccess = false; return Page(); } // Чтение Excel файла _logger.LogInformation("Reading Excel file..."); Subscribers = ReadExcelFile(excelFile); _logger.LogInformation($"Read {Subscribers.Count} subscribers"); if (!Subscribers.Any()) { Message = "Не найдено подписчиков в файле"; IsSuccess = false; return Page(); } // Проверяем подключение _logger.LogInformation("Testing Listmonk connection..."); if (!await _listmonkService.TestConnectionAsync()) { Message = "Нет подключения к Listmonk"; IsSuccess = false; return Page(); } // Создаем список _logger.LogInformation($"Creating list: {listName}"); int listId = await _listmonkService.CreateListAsync(listName); // Импортируем _logger.LogInformation($"Importing to list {listId}..."); await _listmonkService.ImportSubscribersAsync(new List { listId }, Subscribers); Message = $"Успешно загружено {Subscribers.Count} подписчиков в список \"{listName}\""; IsSuccess = true; _logger.LogInformation("=== Upload completed successfully ==="); } catch (Exception ex) { Message = $"Ошибка: {ex.Message}"; IsSuccess = false; _logger.LogError(ex, "=== Upload failed ==="); } return Page(); } private List ReadExcelFile(IFormFile file) { var subscribers = new List(); try { // Создаём временный файл с правильным расширением .xlsx var tempFilePath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx"); _logger.LogInformation($"Temp file: {tempFilePath}"); try { // Сохраняем загруженный файл во временный файл using (var stream = new FileStream(tempFilePath, FileMode.Create)) { file.CopyTo(stream); } _logger.LogInformation("Opening workbook..."); using (var workbook = new XLWorkbook(tempFilePath)) { var worksheet = workbook.Worksheet(1); var firstRow = worksheet.FirstRowUsed(); if (firstRow == null) { _logger.LogWarning("Empty file"); return subscribers; } // Заголовки var headers = new List(); foreach (var cell in firstRow.Cells()) { var header = cell.Value.ToString().Trim(); headers.Add(header); _logger.LogInformation($"Header: {header}"); } ColumnHeaders = headers; _logger.LogInformation($"Total headers: {headers.Count}"); if (headers.Count < 2) { _logger.LogError("Need at least 2 columns"); return subscribers; } // Данные var lastRow = worksheet.LastRowUsed(); _logger.LogInformation($"Last row: {lastRow?.RowNumber() ?? 0}"); for (int row = 2; row <= lastRow.RowNumber(); row++) { try { var email = worksheet.Cell(row, 1).GetValue().Trim(); if (string.IsNullOrWhiteSpace(email)) { _logger.LogWarning($"Row {row}: empty email, skipping"); continue; } var name = worksheet.Cell(row, 2).GetValue().Trim(); var attributes = new Dictionary(); for (int col = 3; col <= headers.Count; col++) { var value = worksheet.Cell(row, col).GetValue().Trim(); if (!string.IsNullOrWhiteSpace(value)) { attributes[headers[col - 1]] = value; } } subscribers.Add(new Subscriber { Email = email, FullName = name, Attributes = attributes }); _logger.LogInformation($"Row {row}: Added {email}"); } catch (Exception rowEx) { _logger.LogWarning(rowEx, $"Skipping row {row}"); } } } } finally { // Удаляем временный файл if (System.IO.File.Exists(tempFilePath)) { try { System.IO.File.Delete(tempFilePath); _logger.LogInformation($"Deleted temp file: {tempFilePath}"); } catch (Exception ex) { _logger.LogWarning(ex, $"Failed to delete temp file: {tempFilePath}"); } } } } catch (Exception ex) { _logger.LogError(ex, "Error reading Excel"); throw; } return subscribers; } } }