blogbuituantuBùi Tuấn Tú

Chức năng Giấy Chứng Nhận

17 tháng 11, 2025

Tổng quan công nghệ


1. Quy trình tạo Template

BướcNội dung chính
1Org gọi POST /api/certificate/samples (multipart) với TenMau, MoTa, MaSuKien, IsDefault và file nền (ảnh/PDF). Ảnh lưu tại wwwroot/uploads.
2Sau khi có MaMau, FE editor (certificate-template-editor) upload ảnh mới qua POST /api/upload, rồi lưu layout bằng POST /api/certificate/samples/{maMau}/config.
3TemplateConfig dạng JSON fields[], chứa tọa độ, font, màu, key map sang dữ liệu thật (TenTNV, TenSuKien, …).

TemplateConfig mẫu

json
{
  "fields": [
    {
      "key": "TenTNV",
      "label": "Tên TNV",
      "x": 600,
      "y": 360,
      "fontSize": 48,
      "fontFamily": "Times New Roman",
      "color": "#1d1d1f",
      "align": "center",
      "fontWeight": "bold"
    },
    {
      "key": "TenSuKien",
      "label": "Tên sự kiện",
      "x": 600,
      "y": 460,
      "fontSize": 28,
      "fontFamily": "Roboto",
      "color": "#444444",
      "align": "center",
      "fontWeight": "normal"
    },
    {
      "key": "ThoiGian",
      "label": "Thời gian",
      "x": 600,
      "y": 520,
      "fontSize": 22,
      "fontFamily": "Roboto",
      "color": "#666666",
      "align": "center",
      "fontWeight": "normal"
    }
  ]
}

Editor FE làm gì?

Code: components/certificate-template-editor/certificate-template-editor.ts.


2. Cấp giấy & snapshot dữ liệu

Hành độngAPINghiệp vụ chính
Cấp đơn lẻPOST /api/certificate với IssueCertificateDto { maTNV, maSuKien, maMau }CertificateService.IssueCertificateAsync kiểm tra TNV, sự kiện, mẫu; tạo bản ghi GiayChungNhan, dựng CertificateData từ TemplateConfig + dữ liệu thật (tên TNV, thời gian, số giờ, mã chứng nhận, …).
Cấp hàng loạtPOST /api/certificate/events/{maSuKien}/issue-bulk/{maMau}Lặp qua DonDangKy đã duyệt, bỏ qua TNV đã có chứng nhận. Sau khi insert, gửi thông báo qua NotificationService.
Preview/Tải vềGET /api/certificate/{id}/preview hoặc /download?format=image|pdfCertificateGeneratorService đọc CertificateData, load BackgroundImage, render bằng ImageSharp, export PDF qua QuestPDF.

Thông tin lưu cùng chứng nhận:


3. API trả về & mapping field

GET /api/certificate/samples/{maMau}

json
{
  "data": {
    "maMau": 12,
    "tenMau": "Mùa Hè Xanh 2024",
    "backgroundImage": "a8f5-2024.png",
    "templateConfig": "{...}",
    "width": 1920,
    "height": 1080,
    "isDefault": false
  }
}

POST /api/certificate

json
{
  "maGiayChungNhan": 345,
  "maMau": 12,
  "maTNV": 77,
  "tenTNV": "Nguyễn Văn A",
  "maSuKien": 55,
  "tenSuKien": "Chiến dịch Mùa Hè Xanh",
  "tenToChuc": "HSV Việt Nam",
  "ngayCap": "2024-12-01T09:12:00"
}

GET /api/certificate/{id}

json
{
  "data": {
    "maGiayChungNhan": 345,
    "backgroundImage": "a8f5-2024.png",
    "certificateData": "{\"fields\":[{\"key\":\"TenTNV\",\"value\":\"Nguyễn Văn A\",\"x\":600,...}]}",
    "width": 1920,
    "height": 1080
  }
}

Các key khả dụng: TenTNV, TenSuKien, TenToChuc, NgayCap, ThoiGian, DiaChi, MaChungNhan, NgayBatDau, NgayKetThuc.


4. Hiển thị & export FE

CertificateViewerComponent đọc certificateId từ route, gọi GET /api/certificate/{id} rồi vẽ Canvas:

  1. Parse certificateDatafields.
  2. Load ảnh nền (${baseUrl}/uploads/<file>), set canvasWidth/Height.
  3. Vẽ từng field bằng ctx.fillText.
  4. Cho phép tải PNG (canvas.toBlob) và PDF (jsPDF + chuyển px → mm).
tsx
drawFields(ctx: CanvasRenderingContext2D, fields: TemplateField[]): void {
  fields.forEach(field => {
    if (!field.value) return;
    ctx.font = `${field.fontWeight === 'bold' ? 'bold' : 'normal'} ${field.fontSize}px ${field.fontFamily}`;
    ctx.fillStyle = field.color;
    ctx.textAlign = field.align as CanvasTextAlign;
    ctx.textBaseline = 'top';
    ctx.fillText(field.value, field.x, field.y);
  });
}

Code thực tế: components/certificate-viewer/certificate-viewer.ts (hàm loadCertificate, drawCanvas, downloadImage, downloadPDF).


5. Luồng end-to-end

  1. Tạo mẫu: upload ảnh + lưu TemplateConfig → MauGiayChungNhan.
  2. Cấp phát: chọn TNV/sự kiện → IssueCertificate/bulk → lưu snapshot CertificateData, ảnh nền, kích thước.
  3. Thông báo: gửi push/notification cho TNV.
  4. Xem & tải: FE gọi GET /api/certificate/{id} → vẽ Canvas → user tải PNG/PDF hoặc share link.

6. Database

Cấu trúc bảng MauGiayChungNhan Cấu trúc bảng GiayChungNhan

Khi một giấy chứng nhận phát hành, snapshot (CertificateData + BackgroundImage) được lưu cùng record. Việc chỉnh sửa template sau đó không ảnh hưởng các chứng nhận đã cấp.