---
description: Hướng dẫn tạo chức năng xuất Excel cho Backend
---

# Skill: Xuất Excel Backend

Skill này hướng dẫn cách phát triển chức năng xuất Excel chuẩn cho Backend, sử dụng EPPlus và IExcelFileService.

## 1. Cấu trúc thư mục

```
Ladaer.BackEnd/
├── Infrastructures/
│   ├── DTO/
│   │   └── Results/
│   │       └── [Module]/
│   │           └── [Feature]ExcelResult.cs    # DTO kết quả xuất Excel
│   └── Services/
│       └── [Module]/
│           └── [Feature]ExcelService.cs       # Service xuất Excel
└── Controllers/
    └── [Module]/
        └── [Feature]Controller.cs             # Controller với endpoint export
```

## 2. Tạo DTO Result

**Vị trí**: `Infrastructures/DTO/Results/[Module]/[Feature]ExcelResult.cs`

```csharp
using Lada.Framework.DTO;

namespace Ladaer.BackEnd.Infrastructures.DTO.Results.[Module]
{
    public class [Feature]ExcelResult : BaseResult
    {
        public string? FilePath { get; set; }
        public string? FileName { get; set; }
        public long FileSize { get; set; }
        public int TotalRecords { get; set; }
        public string? DownloadUrl { get; set; }
        public DateTimeOffset ProcessStartTime { get; set; }
        public DateTimeOffset ProcessEndTime { get; set; }
        
        public long ProcessingTimeMs => (ProcessEndTime - ProcessStartTime).TotalMilliseconds > 0 
            ? (long)(ProcessEndTime - ProcessStartTime).TotalMilliseconds 
            : 0;
    }
}
```

## 3. Tạo Excel Service

**Vị trí**: `Infrastructures/Services/[Module]/[Feature]ExcelService.cs`

### 3.1 Interface và Class

```csharp
using System.Drawing;
using Lada.Framework.DTO;
using Lada.Framework.DTO.SmartTable;
using Lada.Framework.Infrastructures.Services;
using OfficeOpenXml;
using OfficeOpenXml.Style;
using System.Data;

namespace Ladaer.BackEnd.Infrastructures.Services.[Module]
{
    public interface I[Feature]ExcelService
    {
        Task<[Feature]ExcelResult> ExportAsync(SmartTableParam param);
    }

    public class [Feature]ExcelService : I[Feature]ExcelService
    {
        private readonly IExcelFileService _excelFileService;
        // Thêm các dependency khác (repository, dbConnection...)

        public [Feature]ExcelService(IExcelFileService excelFileService)
        {
            _excelFileService = excelFileService;
        }

        public async Task<[Feature]ExcelResult> ExportAsync(SmartTableParam param)
        {
            var result = new [Feature]ExcelResult
            {
                ProcessStartTime = DateTimeOffset.Now
            };

            try
            {
                // 1. Lấy dữ liệu
                var data = await GetDataAsync(param);

                // 2. Tạo file Excel
                var excelPackage = CreateExcelPackage(data);

                // 3. Lưu file sử dụng ExcelFileService
                var saveResult = await _excelFileService.SaveExcelFileAsync(
                    excelPackage,
                    "TenFile",           // Prefix tên file
                    "subfolder",         // Thư mục con trong exports
                    null);               // User ID (optional)

                if (saveResult.Result == Result.Success)
                {
                    result.FilePath = saveResult.FilePath;
                    result.FileName = saveResult.SavedFileName;
                    result.DownloadUrl = saveResult.FileUrl;
                    result.FileSize = saveResult.FileSize;
                    result.TotalRecords = data.Count;
                    result.Result = Result.Success;
                    result.Message = "Xuất file Excel thành công";
                }
                else
                {
                    result.Result = saveResult.Result;
                    result.Message = saveResult.Message;
                }
            }
            catch (Exception ex)
            {
                result.Result = Result.SystemError;
                result.Message = $"Lỗi khi xuất file Excel: {ex.Message}";
            }

            result.ProcessEndTime = DateTimeOffset.Now;
            return result;
        }
    }
}
```

### 3.2 Method tạo ExcelPackage

```csharp
private ExcelPackage CreateExcelPackage(List<DataItem> items)
{
    var package = new ExcelPackage();
    var worksheet = package.Workbook.Worksheets.Add("Tên Sheet");

    // Thiết lập header
    SetupHeaders(worksheet);

    // Điền dữ liệu
    FillData(worksheet, items);

    // Định dạng worksheet
    FormatWorksheet(worksheet, items.Count);

    return package;
}
```

### 3.3 Method thiết lập header

```csharp
private void SetupHeaders(ExcelWorksheet worksheet)
{
    var headers = new[]
    {
        "STT",
        "Cột 1",
        "Cột 2",
        // ...
    };

    for (int i = 0; i < headers.Length; i++)
    {
        worksheet.Cells[1, i + 1].Value = headers[i];
    }
}
```

### 3.4 Method điền dữ liệu

```csharp
private void FillData(ExcelWorksheet worksheet, List<DataItem> items)
{
    int row = 2;

    foreach (var item in items)
    {
        worksheet.Cells[row, 1].Value = row - 1; // STT
        worksheet.Cells[row, 2].Value = item.Property1;
        
        // Định dạng số
        worksheet.Cells[row, 3].Value = item.Amount;
        worksheet.Cells[row, 3].Style.Numberformat.Format = "#,##0";
        
        // Định dạng số thập phân
        worksheet.Cells[row, 4].Value = (double)item.Decimal;
        worksheet.Cells[row, 4].Style.Numberformat.Format = "#,##0.00";
        
        // Định dạng ngày
        worksheet.Cells[row, 5].Value = item.Date.ToString("dd/MM/yyyy");
        
        // Màu có điều kiện
        if (item.Value > 0)
            worksheet.Cells[row, 6].Style.Font.Color.SetColor(Color.Green);
        else if (item.Value < 0)
            worksheet.Cells[row, 6].Style.Font.Color.SetColor(Color.Red);

        row++;
    }
}
```

### 3.5 Method định dạng worksheet

```csharp
private void FormatWorksheet(ExcelWorksheet worksheet, int dataRowCount)
{
    int columnCount = 6; // Số cột

    // Header formatting
    var headerRange = worksheet.Cells[1, 1, 1, columnCount];
    headerRange.Style.Font.Bold = true;
    headerRange.Style.Fill.PatternType = ExcelFillStyle.Solid;
    headerRange.Style.Fill.BackgroundColor.SetColor(Color.LightBlue);
    headerRange.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
    headerRange.Style.VerticalAlignment = ExcelVerticalAlignment.Center;

    // Auto-fit columns
    worksheet.Cells.AutoFitColumns();

    // Border for all data
    if (dataRowCount > 0)
    {
        var dataRange = worksheet.Cells[1, 1, dataRowCount + 1, columnCount];
        dataRange.Style.Border.Top.Style = ExcelBorderStyle.Thin;
        dataRange.Style.Border.Left.Style = ExcelBorderStyle.Thin;
        dataRange.Style.Border.Right.Style = ExcelBorderStyle.Thin;
        dataRange.Style.Border.Bottom.Style = ExcelBorderStyle.Thin;
    }
}
```

## 4. Đăng ký Dependency Injection

**Vị trí**: `Infrastructures/Startup/DependencyRegister.cs`

```csharp
services.AddTransient<I[Feature]ExcelService, [Feature]ExcelService>();
```

## 5. Tạo Controller Endpoint

**Vị trí**: `Controllers/[Module]/[Feature]Controller.cs`

```csharp
private readonly I[Feature]ExcelService _excelService;

// Inject trong constructor
public [Feature]Controller(I[Feature]ExcelService excelService)
{
    _excelService = excelService;
}

/// <summary>
/// Xuất Excel [Feature]
/// </summary>
[HttpPost("Export")]
[Authorize(Roles = "[Feature].Export")]
public async Task<ActionResult<[Feature]ExcelResult>> Export([FromBody] SmartTableParam param)
{
    try
    {
        var result = await _excelService.ExportAsync(param);

        if (result.Result == Result.Success)
        {
            return Ok(result);
        }
        else
        {
            return BadRequest(result);
        }
    }
    catch (Exception ex)
    {
        return StatusCode(500, new [Feature]ExcelResult
        {
            Result = Result.SystemError,
            Message = $"Lỗi hệ thống: {ex.Message}"
        });
    }
}
```

## 6. Lưu ý quan trọng

### 6.1 Điều kiện lọc

> [!IMPORTANT]
> Điều kiện lọc của API Export phải **giống hệt** với API List tương ứng để đảm bảo tính nhất quán

### 6.2 Response

API Export trả về:
- `DownloadUrl`: URL để download file
- `FileName`: Tên file đã lưu
- `TotalRecords`: Số bản ghi đã xuất
- `ProcessingTimeMs`: Thời gian xử lý (ms)

### 6.3 Color coding

Sử dụng màu để làm nổi bật dữ liệu:
- **Xanh (Green)**: Giá trị dương, trạng thái tốt
- **Đỏ (Red)**: Giá trị âm, trạng thái xấu
- **Vàng (Yellow)**: Cảnh báo

### 6.4 Number Format

| Loại | Format |
|------|--------|
| Số nguyên | `#,##0` |
| Số thập phân | `#,##0.00` |
| Tiền VND | `#,##0` |
| Phần trăm | `0.00%` |

## 7. Ví dụ tham khảo

- [FuelRefillExcelService.cs](file:///e:/Projects/LADA/SourceCode/V23/Ladaer.BackEnd/Ladaer.BackEnd/Infrastructures/Services/FuelCards/FuelRefillExcelService.cs)
- [FuelRefillReportExcelService.cs](file:///e:/Projects/LADA/SourceCode/V23/Ladaer.BackEnd/Ladaer.BackEnd/Infrastructures/Services/Report/FuelCards/FuelRefillReportExcelService.cs)
- [OrderExportController.cs](file:///e:/Projects/LADA/SourceCode/V23/Ladaer.BackEnd/Ladaer.BackEnd/Controllers/Orders/OrderExportController.cs)
