Hệ thống sinh tài liệu từ Word template → fill dữ liệu động → export PDF.
| Tính năng | ChatGPT version | Bản này |
|---|---|---|
| Database | Không có | SQLite async (SQLAlchemy) |
| AI auto-label | Không | ✅ Claude Haiku tự label tiếng Việt |
| Config qua API | Không | ✅ PATCH /config/ |
| Async render | Không | ✅ Background job + poll |
| Retry LibreOffice | Không | ✅ tenacity 3 lần |
| Parallel safety | Không | ✅ Semaphore + unique LO profile |
| Logging | print() | ✅ structlog JSON |
| Word split-run bug | Chưa xử lý | ✅ Merge runs trước khi parse |
cp .env.example .env
# Điền ANTHROPIC_API_KEY vào .envdocker compose up --buildHoặc với Nginx (production):
docker compose --profile production up --buildpip install -r requirements.txt
uvicorn app.main:app --reload --port 8000API docs: http://localhost:8000/docs
curl -X POST http://localhost:8000/templates/ \
-F "file=@template.docx" \
-F "name=Hợp đồng lao động" \
-F "description=Template HĐLĐ chuẩn"Response:
{
"id": "uuid-...",
"name": "Hợp đồng lao động",
"metadata": {
"fields": [
{"key": "ho_ten", "label": "Họ và tên", "type": "text"},
{"key": "ngay_sinh", "label": "Ngày sinh", "type": "date"}
],
"tables": [
{"key": "danh_sach", "columns": ["ten", "so_tien"]}
]
}
}curl -X PATCH http://localhost:8000/templates/{id}/labels \
-H "Content-Type: application/json" \
-d '{"labels": {"ho_ten": "Tên đầy đủ của nhân viên"}}'curl -X POST http://localhost:8000/render/{template_id} \
-H "Content-Type: application/json" \
-d '{
"data": {
"ho_ten": "Nguyễn Văn A",
"ngay_sinh": "01/01/1990",
"danh_sach": [
{"ten": "Sản phẩm A", "so_tien": "1.000.000"},
{"ten": "Sản phẩm B", "so_tien": "2.000.000"}
]
},
"output_format": "pdf"
}' \
--output result.pdf# 1. Tạo job
curl -X POST http://localhost:8000/render/{template_id}/async \
-H "Content-Type: application/json" \
-d '{"data": {...}, "output_format": "pdf"}'
# → {"job_id": "abc123", "status": "pending"}
# 2. Poll status
curl http://localhost:8000/render/jobs/abc123
# → {"status": "done", "download_url": "/render/jobs/abc123/download"}
# 3. Download
curl http://localhost:8000/render/jobs/abc123/download -o result.pdf# Xem config
curl http://localhost:8000/config/
# Tắt AI (tiết kiệm cost khi không cần)
curl -X PATCH http://localhost:8000/config/ \
-H "Content-Type: application/json" \
-d '{"ai_enabled": false}'
# Tăng concurrent renders
curl -X PATCH http://localhost:8000/config/ \
-d '{"max_concurrent_renders": 20}'{{ ho_ten }} ← scalar field
{{ ngay_sinh }} ← date field
{% for item in danh_sach %}
{{ item.ten }} | {{ item.so_tien }}
{% endfor %}
{% if co_phu_luc %}
Phụ lục: {{ ten_phu_luc }}
{% endif %}
Xem ví dụ đầy đủ trong templates/TEMPLATE_GUIDE.txt
Client
↓
Nginx (port 80)
↓
FastAPI (port 8000, 4 workers)
├── POST /templates/ → Parse + AI label → SQLite
├── POST /render/sync → docxtpl → LibreOffice → PDF
├── POST /render/async → BackgroundTask → Job DB
└── PATCH /config/ → Runtime config
SQLite (docgen.db)
├── templates (metadata, labels)
└── render_jobs (status, output_path)
Storage/
├── uploads/ (template .docx)
├── outputs/ (rendered PDF/DOCX)
└── temp/ (temp files + LO profiles)
Khi cần scale lớn hơn:
- Thay SQLite → PostgreSQL (asyncpg)
- Thay BackgroundTasks → Celery + Redis worker
- Thay local storage → S3/MinIO
- Tách PDF Worker thành service riêng (LibreOffice nặng)
- Kubernetes autoscale PDF workers theo queue depth