Base Project
This commit is contained in:
427
docs/db/WareHouseDB.md
Normal file
427
docs/db/WareHouseDB.md
Normal file
@@ -0,0 +1,427 @@
|
||||
# Warehouse Management Database Schema
|
||||
|
||||
## ER Diagram Overview
|
||||
|
||||
```
|
||||
Warehouse 1──N Room 1──N Cabinet 1──N Shelf 1──N Container
|
||||
│
|
||||
M──N Component (qua ComponentItem)
|
||||
│
|
||||
ComponentType 1──N Component 1──N ComponentCode
|
||||
│
|
||||
├── ComponentItem (instance tại từng Container)
|
||||
│
|
||||
└── ComponentStatusHistory (lịch sử thay đổi tình trạng)
|
||||
|
||||
InvoiceConfig 1──N InvoiceConfigItem 1──N AlternativeComponent
|
||||
│
|
||||
Invoice 1──N InvoiceItem
|
||||
│
|
||||
└── InvoiceStatusHistory (lịch sử trạng thái hóa đơn)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tables
|
||||
|
||||
### 1. warehouses (Kho)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ------------ | ------------ | -------------------- | ------------------ |
|
||||
| id | BIGINT | PK, AUTO_INCREMENT | |
|
||||
| name | VARCHAR(255) | NOT NULL | Tên kho |
|
||||
| description | TEXT | | Mô tả |
|
||||
| address | VARCHAR(500) | | Địa chỉ |
|
||||
| created_at | DATETIME | DEFAULT NOW() | |
|
||||
| updated_at | DATETIME | DEFAULT NOW() | |
|
||||
|
||||
### 2. rooms (Phòng)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ------------ | ------------ | -------------------- | ------------------ |
|
||||
| id | BIGINT | PK, AUTO_INCREMENT | |
|
||||
| warehouse_id | BIGINT | FK → warehouses(id) | Thuộc kho nào |
|
||||
| name | VARCHAR(255) | NOT NULL | Tên phòng |
|
||||
| description | TEXT | | Mô tả |
|
||||
| created_at | DATETIME | DEFAULT NOW() | |
|
||||
| updated_at | DATETIME | DEFAULT NOW() | |
|
||||
|
||||
### 3. cabinets (Tủ)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ------------ | ------------ | -------------------- | ------------------ |
|
||||
| id | BIGINT | PK, AUTO_INCREMENT | |
|
||||
| room_id | BIGINT | FK → rooms(id) | Thuộc phòng nào |
|
||||
| name | VARCHAR(255) | NOT NULL | Tên tủ |
|
||||
| description | TEXT | | Mô tả |
|
||||
| created_at | DATETIME | DEFAULT NOW() | |
|
||||
| updated_at | DATETIME | DEFAULT NOW() | |
|
||||
|
||||
### 4. shelves (Tầng / Kệ)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ------------ | ------------ | -------------------- | -------------------- |
|
||||
| id | BIGINT | PK, AUTO_INCREMENT | |
|
||||
| cabinet_id | BIGINT | FK → cabinets(id) | Thuộc tủ nào |
|
||||
| name | VARCHAR(255) | NOT NULL | Tên tầng (VD: Tầng 1)|
|
||||
| level_index | INT | NOT NULL | Thứ tự tầng |
|
||||
| description | TEXT | | Mô tả |
|
||||
| created_at | DATETIME | DEFAULT NOW() | |
|
||||
| updated_at | DATETIME | DEFAULT NOW() | |
|
||||
|
||||
### 5. containers (Vật chứa: thùng rỗng, khay, thùng giấy, ...)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ------------ | ------------ | -------------------- | ----------------------------- |
|
||||
| id | BIGINT | PK, AUTO_INCREMENT | |
|
||||
| shelf_id | BIGINT | FK → shelves(id) | Nằm trên tầng nào |
|
||||
| name | VARCHAR(255) | NOT NULL | Tên vật chứa |
|
||||
| container_type | ENUM('empty_box', 'tray', 'paper_box', 'plastic_box', 'bag', 'other') | NOT NULL | Loại vật chứa |
|
||||
| description | TEXT | | Mô tả |
|
||||
| max_capacity | INT | | Sức chứa tối đa (số linh kiện)|
|
||||
| created_at | DATETIME | DEFAULT NOW() | |
|
||||
| updated_at | DATETIME | DEFAULT NOW() | |
|
||||
|
||||
### 6. component_types (Loại linh kiện)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ------------ | ------------ | -------------------- | ---------------------------- |
|
||||
| id | BIGINT | PK, AUTO_INCREMENT | |
|
||||
| name | VARCHAR(255) | NOT NULL, UNIQUE | Tên loại (VD: Resistor, IC) |
|
||||
| description | TEXT | | Mô tả |
|
||||
| created_at | DATETIME | DEFAULT NOW() | |
|
||||
| updated_at | DATETIME | DEFAULT NOW() | |
|
||||
|
||||
### 7. components (Linh kiện)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ----------------- | ------------ | ------------------------- | ----------------------------- |
|
||||
| id | BIGINT | PK, AUTO_INCREMENT | |
|
||||
| component_type_id | BIGINT | FK → component_types(id) | Thuộc loại nào |
|
||||
| name | VARCHAR(255) | NOT NULL | Tên linh kiện |
|
||||
| description | TEXT | | Mô tả chi tiết |
|
||||
| unit | VARCHAR(50) | DEFAULT 'cái' | Đơn vị tính (cái, m, kg, ...) |
|
||||
| total_quantity | INT | DEFAULT 0 | Tổng số lượng (tính tự động) |
|
||||
| min_quantity | INT | DEFAULT 0 | Số lượng tối thiểu (cảnh báo) |
|
||||
| created_at | DATETIME | DEFAULT NOW() | |
|
||||
| updated_at | DATETIME | DEFAULT NOW() | |
|
||||
|
||||
> **Note**: `total_quantity` được tính tổng từ `component_items.quantity` và nên được cache/update qua trigger hoặc application logic.
|
||||
|
||||
### 8. component_codes (Mã linh kiện - 1 linh kiện có thể có nhiều mã)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ------------ | ------------ | ----------------------- | -------------------------------- |
|
||||
| id | BIGINT | PK, AUTO_INCREMENT | |
|
||||
| component_id | BIGINT | FK → components(id) | Thuộc linh kiện nào |
|
||||
| code | VARCHAR(255) | NOT NULL | Mã (VD: ESP32-WROOM-32D) |
|
||||
| code_type | VARCHAR(100) | | Loại mã (VD: SKU, Part Number) |
|
||||
| is_primary | BOOLEAN | DEFAULT FALSE | Mã chính |
|
||||
| created_at | DATETIME | DEFAULT NOW() | |
|
||||
|
||||
```
|
||||
UNIQUE (code, code_type)
|
||||
```
|
||||
|
||||
### 9. component_items (Linh kiện tại từng vị trí - nằm trong container)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ------------ | ------- | ------------------------------------ | ------------------------------ |
|
||||
| id | BIGINT | PK, AUTO_INCREMENT | |
|
||||
| component_id | BIGINT | FK → components(id) | Linh kiện nào |
|
||||
| container_id | BIGINT | FK → containers(id) | Nằm trong vật chứa nào |
|
||||
| quantity | INT | NOT NULL, DEFAULT 0 | Số lượng tại vị trí này |
|
||||
| status | ENUM('normal', 'damaged', 'long_unused', 'expired', 'pending_inspection') | NOT NULL DEFAULT 'normal' | Tình trạng |
|
||||
| created_at | DATETIME| DEFAULT NOW() | |
|
||||
| updated_at | DATETIME| DEFAULT NOW() | |
|
||||
|
||||
```
|
||||
UNIQUE (component_id, container_id, status)
|
||||
```
|
||||
|
||||
> **Note**: Cùng 1 linh kiện ở cùng 1 container nhưng khác status sẽ là các record khác nhau. Điều này cho phép phân biệt linh kiện hỏng và bình thường trong cùng vị trí.
|
||||
|
||||
### 10. component_status_history (Lịch sử thay đổi tình trạng linh kiện)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ---------------- | ---------- | --------------------------------- | ----------------------------------- |
|
||||
| id | BIGINT | PK, AUTO_INCREMENT | |
|
||||
| component_item_id| BIGINT | FK → component_items(id) | Linh kiện item nào |
|
||||
| old_status | ENUM (như component_items.status) | | Tình trạng cũ |
|
||||
| new_status | ENUM (như component_items.status) | | Tình trạng mới |
|
||||
| changed_quantity | INT | | Số lượng bị thay đổi tình trạng |
|
||||
| note | TEXT | | Ghi chú lý do |
|
||||
| changed_by | VARCHAR(255)| | Người thay đổi |
|
||||
| changed_at | DATETIME | DEFAULT NOW() | |
|
||||
|
||||
### 11. invoice_configs (Cấu hình hóa đơn mẫu)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ------------ | ------------ | -------------------- | -------------------------------- |
|
||||
| id | BIGINT | PK, AUTO_INCREMENT | |
|
||||
| name | VARCHAR(255) | NOT NULL | Tên cấu hình (VD: Kit xuất kho A)|
|
||||
| type | ENUM('import', 'export') | NOT NULL | Loại hóa đơn |
|
||||
| description | TEXT | | Mô tả |
|
||||
| is_active | BOOLEAN | DEFAULT TRUE | Đang sử dụng |
|
||||
| created_at | DATETIME | DEFAULT NOW() | |
|
||||
| updated_at | DATETIME | DEFAULT NOW() | |
|
||||
|
||||
### 12. invoice_config_items (Chi tiết cấu hình hóa đơn - linh kiện cần xuất/nhập)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ----------------- | ------------ | ---------------------------------- | ----------------------- |
|
||||
| id | BIGINT | PK, AUTO_INCREMENT | |
|
||||
| invoice_config_id | BIGINT | FK → invoice_configs(id) | Thuộc cấu hình nào |
|
||||
| component_id | BIGINT | FK → components(id) | Linh kiện cần |
|
||||
| required_quantity | INT | NOT NULL | Số lượng yêu cầu |
|
||||
| allow_alternative | BOOLEAN | DEFAULT FALSE | Cho phép dùng linh kiện thay thế |
|
||||
| priority_order | INT | DEFAULT 0 | Thứ tự ưu tiên |
|
||||
| note | TEXT | | Ghi chú |
|
||||
|
||||
```
|
||||
UNIQUE (invoice_config_id, component_id)
|
||||
```
|
||||
|
||||
### 13. alternative_components (Linh kiện thay thế - dùng khi thiếu)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ---------------------- | ------- | ---------------------------------------- | ---------------------------------- |
|
||||
| id | BIGINT | PK, AUTO_INCREMENT | |
|
||||
| invoice_config_item_id | BIGINT | FK → invoice_config_items(id) | Item cấu hình nào |
|
||||
| alternative_component_id | BIGINT | FK → components(id) | Linh kiện thay thế |
|
||||
| conversion_ratio | DECIMAL(10,2) | DEFAULT 1.0 | Tỷ lệ quy đổi (VD: 2 cái nhỏ = 1 cái lớn) |
|
||||
| priority | INT | DEFAULT 0 | Thứ tự ưu tiên khi thay thế |
|
||||
| note | TEXT | | Ghi chú |
|
||||
|
||||
```
|
||||
UNIQUE (invoice_config_item_id, alternative_component_id)
|
||||
```
|
||||
|
||||
> **Ví dụ**: Nếu cấu hình yêu cầu "IC WiFi ESP32" nhưng hết, có thể dùng "IC WiFi ESP32-C3" thay thế với `conversion_ratio = 1.0`.
|
||||
|
||||
### 14. invoices (Hóa đơn nhập / xuất)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ----------------- | ------------ | ---------------------------- | ---------------------------------------- |
|
||||
| id | BIGINT | PK, AUTO_INCREMENT | |
|
||||
| invoice_code | VARCHAR(100) | NOT NULL, UNIQUE | Mã hóa đơn (tự generate) |
|
||||
| type | ENUM('import', 'export') | NOT NULL | Loại hóa đơn |
|
||||
| status | ENUM('draft', 'pending', 'approved', 'completed', 'cancelled') | NOT NULL DEFAULT 'draft' | Trạng thái |
|
||||
| invoice_config_id | BIGINT | FK → invoice_configs(id), NULL | Cấu hình áp dụng (có thể null) |
|
||||
| total_items | INT | DEFAULT 0 | Tổng số loại linh kiện |
|
||||
| note | TEXT | | Ghi chú |
|
||||
| created_by | VARCHAR(255) | | Người tạo |
|
||||
| approved_by | VARCHAR(255) | | Người duyệt |
|
||||
| completed_at | DATETIME | | Thời gian hoàn thành |
|
||||
| created_at | DATETIME | DEFAULT NOW() | |
|
||||
| updated_at | DATETIME | DEFAULT NOW() | |
|
||||
|
||||
### 15. invoice_items (Chi tiết hóa đơn - từng linh kiện)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ----------------- | ------------ | ---------------------------------- | -------------------------------- |
|
||||
| id | BIGINT | PK, AUTO_INCREMENT | |
|
||||
| invoice_id | BIGINT | FK → invoices(id) | Thuộc hóa đơn nào |
|
||||
| component_id | BIGINT | FK → components(id) | Linh kiện |
|
||||
| original_component_id | BIGINT | FK → components(id), NULL | Linh kiện gốc (nếu dùng thay thế)|
|
||||
| required_quantity | INT | NOT NULL | Số lượng yêu cầu |
|
||||
| actual_quantity | INT | DEFAULT 0 | Số lượng thực tế |
|
||||
| is_substituted | BOOLEAN | DEFAULT FALSE | Đã dùng linh kiện thay thế |
|
||||
| is_short | BOOLEAN | DEFAULT FALSE | Có thiếu hay không |
|
||||
| shortage_quantity | INT | DEFAULT 0 | Số lượng thiếu |
|
||||
| note | TEXT | | Ghi chú |
|
||||
|
||||
```
|
||||
UNIQUE (invoice_id, component_id)
|
||||
```
|
||||
|
||||
### 16. invoice_item_locations (Chi tiết vị trí xuất/nhập cho từng item)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| --------------- | ------- | --------------------------------- | ------------------------------------ |
|
||||
| id | BIGINT | PK, AUTO_INCREMENT | |
|
||||
| invoice_item_id | BIGINT | FK → invoice_items(id) | Item hóa đơn nào |
|
||||
| container_id | BIGINT | FK → containers(id) | Xuất/nhập từ container nào |
|
||||
| quantity | INT | NOT NULL | Số lượng xuất/nhập tại vị trí này |
|
||||
|
||||
> **Note**: Bảng này cho phép xuất 1 linh kiện từ nhiều vị trí khác nhau, và ghi lại chính xác vị trí.
|
||||
|
||||
### 17. invoice_status_history (Lịch sử trạng thái hóa đơn)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ---------- | ------------ | ---------------------------- | -------------------- |
|
||||
| id | BIGINT | PK, AUTO_INCREMENT | |
|
||||
| invoice_id | BIGINT | FK → invoices(id) | Hóa đơn nào |
|
||||
| old_status | VARCHAR(50) | | Trạng thái cũ |
|
||||
| new_status | VARCHAR(50) | NOT NULL | Trạng thái mới |
|
||||
| changed_by | VARCHAR(255) | | Người thay đổi |
|
||||
| note | TEXT | | Ghi chú |
|
||||
| changed_at | DATETIME | DEFAULT NOW() | |
|
||||
|
||||
### 18. stock_transactions (Lịch sử nhập xuất kho - phục vụ thống kê)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ----------------- | ------------ | ---------------------------------- | ----------------------------- |
|
||||
| id | BIGINT | PK, AUTO_INCREMENT | |
|
||||
| invoice_id | BIGINT | FK → invoices(id) | Hóa đơn liên quan |
|
||||
| component_id | BIGINT | FK → components(id) | Linh kiện |
|
||||
| container_id | BIGINT | FK → containers(id) | Vị trí |
|
||||
| transaction_type | ENUM('import', 'export', 'adjustment', 'transfer') | NOT NULL | Loại giao dịch |
|
||||
| quantity | INT | NOT NULL | Số lượng (âm = xuất, dương = nhập) |
|
||||
| balance_after | INT | | Số dư sau giao dịch |
|
||||
| note | TEXT | | Ghi chú |
|
||||
| created_by | VARCHAR(255) | | Người thực hiện |
|
||||
| created_at | DATETIME | DEFAULT NOW() | |
|
||||
|
||||
> **Note**: Bảng này là bảng tổng hợp để phục vụ thống kê nhanh, tránh phải tính toán từ invoice_items mỗi lần.
|
||||
|
||||
---
|
||||
|
||||
## Indexes (Quan trọng)
|
||||
|
||||
```sql
|
||||
-- Tìm linh kiện theo vị trí
|
||||
CREATE INDEX idx_component_items_component ON component_items(component_id);
|
||||
CREATE INDEX idx_component_items_container ON component_items(container_id);
|
||||
CREATE INDEX idx_component_items_status ON component_items(status);
|
||||
|
||||
-- Tìm mã linh kiện
|
||||
CREATE INDEX idx_component_codes_code ON component_codes(code);
|
||||
CREATE INDEX idx_component_codes_component ON component_codes(component_id);
|
||||
|
||||
-- Thống kê hóa đơn theo thời gian
|
||||
CREATE INDEX idx_invoices_type_status ON invoices(type, status);
|
||||
CREATE INDEX idx_invoices_created_at ON invoices(created_at);
|
||||
CREATE INDEX idx_invoices_type_created ON invoices(type, created_at);
|
||||
|
||||
-- Thống kê giao dịch kho
|
||||
CREATE INDEX idx_stock_transactions_component_date ON stock_transactions(component_id, created_at);
|
||||
CREATE INDEX idx_stock_transactions_type_date ON stock_transactions(transaction_type, created_at);
|
||||
CREATE INDEX idx_stock_transactions_invoice ON stock_transactions(invoice_id);
|
||||
|
||||
-- Tìm vị trí container
|
||||
CREATE INDEX idx_containers_shelf ON containers(shelf_id);
|
||||
CREATE INDEX idx_shelves_cabinet ON shelves(cabinet_id);
|
||||
CREATE INDEX idx_cabinets_room ON cabinets(room_id);
|
||||
CREATE INDEX idx_rooms_warehouse ON rooms(warehouse_id);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ví dụ truy vấn phổ biến
|
||||
|
||||
### 1. Tìm vị trí của 1 linh kiện
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
c.name AS component_name,
|
||||
ct.name AS type_name,
|
||||
ci.quantity,
|
||||
ci.status,
|
||||
cn.name AS container_name,
|
||||
cn.container_type,
|
||||
s.name AS shelf_name,
|
||||
cb.name AS cabinet_name,
|
||||
r.name AS room_name,
|
||||
w.name AS warehouse_name
|
||||
FROM component_items ci
|
||||
JOIN components c ON ci.component_id = c.id
|
||||
JOIN component_types ct ON c.component_type_id = ct.id
|
||||
JOIN containers cn ON ci.container_id = cn.id
|
||||
JOIN shelves s ON cn.shelf_id = s.id
|
||||
JOIN cabinets cb ON s.cabinet_id = cb.id
|
||||
JOIN rooms r ON cb.room_id = r.id
|
||||
JOIN warehouses w ON r.warehouse_id = w.id
|
||||
WHERE ci.component_id = :componentId AND ci.quantity > 0;
|
||||
```
|
||||
|
||||
### 2. Kiểm tra thiếu hụt khi xuất hóa đơn
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
ici.component_id,
|
||||
c.name AS component_name,
|
||||
ici.required_quantity,
|
||||
COALESCE(SUM(ci.quantity), 0) AS available_quantity,
|
||||
CASE
|
||||
WHEN COALESCE(SUM(ci.quantity), 0) < ici.required_quantity THEN TRUE
|
||||
ELSE FALSE
|
||||
END AS is_short,
|
||||
ici.allow_alternative
|
||||
FROM invoice_config_items ici
|
||||
JOIN components c ON ici.component_id = c.id
|
||||
LEFT JOIN component_items ci ON ci.component_id = ici.component_id
|
||||
AND ci.status = 'normal'
|
||||
WHERE ici.invoice_config_id = :configId
|
||||
GROUP BY ici.component_id, c.name, ici.required_quantity, ici.allow_alternative;
|
||||
```
|
||||
|
||||
### 3. Thống kê nhập xuất theo thời gian
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
st.transaction_type,
|
||||
c.name AS component_name,
|
||||
SUM(ABS(st.quantity)) AS total_quantity,
|
||||
DATE(st.created_at) AS transaction_date
|
||||
FROM stock_transactions st
|
||||
JOIN components c ON st.component_id = c.id
|
||||
WHERE st.created_at BETWEEN :startDate AND :endDate
|
||||
GROUP BY st.transaction_type, c.id, DATE(st.created_at)
|
||||
ORDER BY transaction_date DESC;
|
||||
```
|
||||
|
||||
### 4. Linh kiện sắp hết (dưới mức tối thiểu)
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
c.id,
|
||||
c.name,
|
||||
c.total_quantity,
|
||||
c.min_quantity,
|
||||
ct.name AS type_name
|
||||
FROM components c
|
||||
JOIN component_types ct ON c.component_type_id = ct.id
|
||||
WHERE c.total_quantity <= c.min_quantity AND c.min_quantity > 0;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Flow xử lý hóa đơn xuất
|
||||
|
||||
```
|
||||
1. Tạo Invoice (status: draft)
|
||||
↓
|
||||
2. Chọn InvoiceConfig hoặc thêm items thủ công
|
||||
↓
|
||||
3. Kiểm tra tồn kho:
|
||||
- Query component_items WHERE status = 'normal'
|
||||
- So sánh available vs required
|
||||
- Nếu thiếu:
|
||||
a. Cảnh báo item thiếu
|
||||
b. Kiểm tra allow_alternative
|
||||
c. Query alternative_components
|
||||
d. Đề xuất linh kiện thay thế
|
||||
↓
|
||||
4. Chấp nhận / Điều chỉnh (status: approved)
|
||||
↓
|
||||
5. Thực hiện xuất kho:
|
||||
- Trừ quantity trong component_items
|
||||
- Tạo stock_transactions
|
||||
- Cập nhật total_quantity trong components
|
||||
- Tạo invoice_item_locations
|
||||
↓
|
||||
6. Hoàn thành (status: completed)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Enum Values Reference
|
||||
|
||||
| Enum Field | Values |
|
||||
| ---------------- | --------------------------------------------------- |
|
||||
| container_type | `empty_box`, `tray`, `paper_box`, `plastic_box`, `bag`, `other` |
|
||||
| status (item) | `normal`, `damaged`, `long_unused`, `expired`, `pending_inspection` |
|
||||
| invoice type | `import`, `export` |
|
||||
| invoice status | `draft`, `pending`, `approved`, `completed`, `cancelled` |
|
||||
| transaction_type | `import`, `export`, `adjustment`, `transfer` |
|
||||
170
docs/sqlc/config.md
Normal file
170
docs/sqlc/config.md
Normal file
@@ -0,0 +1,170 @@
|
||||
# sqlc.yaml — Configuration Reference (Version 2)
|
||||
|
||||
sqlc sử dụng **Configuration Version 2** khi khai báo `version: "2"` ở đầu file. Dưới đây là bộ quy tắc và danh sách đầy đủ các key.
|
||||
|
||||
---
|
||||
|
||||
## Cấu trúc tổng thể
|
||||
|
||||
```yaml
|
||||
version: "2"
|
||||
|
||||
sql:
|
||||
- engine: "<engine>"
|
||||
queries: "<path>"
|
||||
schema: "<path>"
|
||||
gen:
|
||||
go:
|
||||
package: "<name>"
|
||||
out: "<path>"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Các key cấp gốc (root-level)
|
||||
|
||||
| Key | Kiểu | Bắt buộc | Mô tả |
|
||||
| ----------- | ----- | -------- | ------------------------------------------------------------------------ |
|
||||
| `version` | `"2"` | ✅ | Khai báo phiên bản cấu hình. Giá trị `"2"` cho Version 2. |
|
||||
| `sql` | list | ✅ | Danh sách các block cấu hình, mỗi block sinh code cho một ngôn ngữ đích. |
|
||||
| `overrides` | map | ❌ | (Deprecated v1) — Không dùng trong v2, thay bằng `gen.<lang>.overrides`. |
|
||||
| `rename` | map | ❌ | (Deprecated v1) — Không dùng trong v2, thay bằng `gen.<lang>.rename`. |
|
||||
|
||||
---
|
||||
|
||||
## Các key bên trong mỗi phần tử của `sql[]`
|
||||
|
||||
| Key | Kiểu | Bắt buộc | Mô tả |
|
||||
| ------------------------ | ----------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `engine` | string | ✅ | CSDL mục tiêu. Giá trị: `"postgresql"`, `"mysql"`, `"sqlite"`. |
|
||||
| `queries` | string / string[] | ✅ | Đường dẫn tới thư mục/file chứa các query `.sql`. Có thể là mảng nhiều path. |
|
||||
| `schema` | string / string[] | ✅ | Đường dẫn tới thư mục/file chứa schema DDL (`.sql`). |
|
||||
| `strict_function_checks` | bool | ❌ | Nếu `true`, sqlc sẽ báo lỗi khi gọi function không tồn tại trong schema. Mặc định `false`. |
|
||||
| `strict_order_by` | bool | ❌ | Nếu `true`, yêu cầu tất cả column trong `ORDER BY` phải tồn tại. Mặc định `false` (chỉ cho PostgreSQL). |
|
||||
| `query_parameter_limit` | int | ❌ | Giới hạn số lượng parameter trong một query. Mặc định `1` (nếu > 1 thì sqlc ưu tiên sinh `sql.NamedArg`). Đặt `0` để bỏ giới hạn. |
|
||||
| `codegen` | list | ❌ | Danh sách cấu hình cho **plugin codegen** bên ngoài. Mỗi item có `out`, `plugin`, `options`. |
|
||||
| `gen` | map | ✅ (ít nhất 1) | Map các ngôn ngữ sinh code. Các key con: `go`, `kotlin`, `python`, `json`, `typescript`, `java`, `swift`, `rust`, `csharp`. |
|
||||
|
||||
---
|
||||
|
||||
## Các key bên trong `gen.go`
|
||||
|
||||
| Key | Kiểu | Bắt buộc | Mô tả |
|
||||
| -------------------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------- |
|
||||
| `out` | string | ✅ | Thư mục output cho Go code sinh ra. |
|
||||
| `package` | string | ✅ | Tên Go package. |
|
||||
| `sql_package` | string | ❌ | Package SQL driver. Giá trị: `"database/sql"`, `"pgx/v4"`, `"pgx/v5"`, `"lib/pq"`. Mặc định `"database/sql"`. |
|
||||
| `sql_driver` | string | ❌ | Tên driver cụ thể, dùng để sinh import đúng. VD: `"github.com/jackc/pgx/v5/stdlib"`. |
|
||||
| `emit_json_tags` | bool | ❌ | Nếu `true`, sinh `json:"column_name"` tag cho struct field. Mặc định `false`. |
|
||||
| `emit_db_tags` | bool | ❌ | Nếu `true`, sinh `db:"column_name"` tag. Mặc định `false`. |
|
||||
| `emit_prepared_queries` | bool | ❌ | Nếu `true`, sinh method `Prepare` cho mỗi query. Mặc định `false`. |
|
||||
| `emit_interface` | bool | ❌ | Nếu `true`, sinh interface ` Querier` thay vì chỉ struct. Mặc định `false`. |
|
||||
| `emit_empty_slices` | bool | ❌ | Nếu `true`, trả về `[]T` rỗng thay vì `nil` khi không có row. Mặc định `false`. |
|
||||
| `emit_result_struct_pointers` | bool | ❌ | Sinh con trỏ `*T` cho result struct. Mặc định `false`. |
|
||||
| `emit_params_struct_pointers` | bool | ❌ | Sinh con trỏ `*T` cho params struct. Mặc định `false`. |
|
||||
| `emit_method_with_db_argument` | bool | ❌ | Nếu `true`, mỗi method nhận thêm `DB` argument, cho phép dùng transaction dễ hơn. Mặc định `false`. |
|
||||
| `emit_pointers_for_null_types` | bool | ❌ | Nếu `true`, dùng con trỏ cho null type thay vì `sql.Null*`. Mặc định `false`. |
|
||||
| `emit_enum_valid_method` | bool | ❌ | Sinh method `Valid()` cho enum type. Mặc định `false`. |
|
||||
| `emit_all_enum_values` | bool | ❌ | Sinh constant cho tất cả giá trị enum. Mặc định `false`. |
|
||||
| `emit_build_tags` | string | ❌ | Thêm Go build tag vào file sinh ra. VD: `"//go:build linux"`. |
|
||||
| `json_tags_case_style` | string | ❌ | Style cho JSON tag. Giá trị: `"camel"`, `"pascal"`, `"snake"`, `"none"`. Mặc định phụ thuộc vào `emit_json_tags`. |
|
||||
| `output_db_file_name` | string | ❌ | Tên file chứa `DB` struct. Mặc định `"db.go"`. |
|
||||
| `output_models_file_name` | string | ❌ | Tên file chứa model struct. Mặc định `"models.go"`. |
|
||||
| `output_querier_file_name` | string | ❌ | Tên file chứa interface. Mặc định `"querier.go"`. |
|
||||
| `output_files_suffix` | string | ❌ | Hậu tố cho file query. Mặc định `""`. VD: `"_sql"` → `user_sql.go`. |
|
||||
| `inflection_exclude_table_names` | list | ❌ | Danh sách tên table không áp dụng quy tắc số nhiều. VD: `["user"]`. |
|
||||
| `overrides` | list | ❌ | Ghi đè kiểu dữ liệu cho column cụ thể hoặc cho kiểu Go toàn cục (xem chi tiết bên dưới). |
|
||||
| `rename` | map | ❌ | Map đổi tên. Key = tên cần đổi, Value = tên mới. Dùng để rename struct field. |
|
||||
| `import` | string | ❌ | Import path của Go module dùng trong generated code. |
|
||||
|
||||
---
|
||||
|
||||
## Cấu trúc của `overrides[]` (bên trong `gen.go`)
|
||||
|
||||
```yaml
|
||||
overrides:
|
||||
- db_type: "uuid"
|
||||
go_type: "github.com/google/uuid.UUID"
|
||||
- db_type: "timestamptz"
|
||||
go_type: "time.Time"
|
||||
- column: "users.status"
|
||||
go_type: "UserStatus"
|
||||
go_struct_tag:
|
||||
tags:
|
||||
json: "status,omitempty"
|
||||
- db_type: "text"
|
||||
go_type:
|
||||
import: "github.com/lib/pq"
|
||||
type: "StringArray"
|
||||
nullable: true
|
||||
```
|
||||
|
||||
| Key | Mô tả |
|
||||
| --------------- | ------------------------------------------------------------------------- |
|
||||
| `db_type` | Kiểu dữ liệu SQL cần ghi đè. Dùng cùng với `go_type`. |
|
||||
| `column` | Đường dẫn `"table.column"` cụ thể. Ưu tiên cao hơn `db_type`. |
|
||||
| `go_type` | Kiểu Go thay thế. Có thể là string hoặc object `{import, type, pointer}`. |
|
||||
| `nullable` | bool — Nếu `true`, áp dụng cho phiên bản nullable của kiểu. |
|
||||
| `go_struct_tag` | Custom struct tag cho field. |
|
||||
|
||||
---
|
||||
|
||||
## `gen.json`
|
||||
|
||||
| Key | Kiểu | Bắt buộc | Mô tả |
|
||||
| ---------- | ------ | -------- | --------------------------------------------------- |
|
||||
| `out` | string | ✅ | Thư mục output file JSON. |
|
||||
| `indent` | string | ❌ | Ký tự indent. Mặc định `" "`. |
|
||||
| `filename` | string | ❌ | Tên file output. Mặc định `"codegen_request.json"`. |
|
||||
|
||||
---
|
||||
|
||||
## `gen.typescript`
|
||||
|
||||
| Key | Kiểu | Bắt buộc | Mô tả |
|
||||
| ------------------- | ------ | -------- | ------------------------------------------------------ |
|
||||
| `out` | string | ✅ | Thư mục output. |
|
||||
| `plugin` | string | ❌ | Tên plugin (nếu dùng plugin ngoài). |
|
||||
| `runtime` | string | ❌ | Runtime cho generated code: `"node"` hoặc `"browser"`. |
|
||||
| `driver` | string | ❌ | Driver: `"pg"` hoặc `"pg-query-stream"`. |
|
||||
| `emit_json_tags` | bool | ❌ | Sinh JSON tag cho property. |
|
||||
| `emit_result_types` | bool | ❌ | Sinh interface cho result. |
|
||||
|
||||
---
|
||||
|
||||
## Ví dụ hoàn chỉnh
|
||||
|
||||
```yaml
|
||||
version: "2"
|
||||
|
||||
sql:
|
||||
- engine: "postgresql"
|
||||
queries: "query/"
|
||||
schema: "schema/"
|
||||
gen:
|
||||
go:
|
||||
package: "db"
|
||||
out: "db"
|
||||
sql_package: "pgx/v5"
|
||||
emit_json_tags: true
|
||||
emit_interface: true
|
||||
emit_empty_slices: true
|
||||
emit_prepared_queries: false
|
||||
json_tags_case_style: "camel"
|
||||
overrides:
|
||||
- db_type: "uuid"
|
||||
go_type: "github.com/google/uuid.UUID"
|
||||
- db_type: "timestamptz"
|
||||
go_type: "time.Time"
|
||||
- column: "orders.status"
|
||||
go_type:
|
||||
import: "warehouse-management/types"
|
||||
type: "OrderStatus"
|
||||
pointer: true
|
||||
inflection_exclude_table_names:
|
||||
- "status"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Tóm lại**: Key bắt buộc tối thiểu cho một config sqlc v2 hoạt động là `version`, `sql[].engine`, `sql[].queries`, `sql[].schema`, và ít nhất một block `gen.<lang>` với `out` + `package` (hoặc tương đương cho ngôn ngữ khác).
|
||||
400
docs/sqlc/query.md
Normal file
400
docs/sqlc/query.md
Normal file
@@ -0,0 +1,400 @@
|
||||
# Bộ quy tắc viết file query trong sqlc
|
||||
|
||||
---
|
||||
|
||||
## 1. Cấu trúc cơ bản
|
||||
|
||||
Mỗi file query là một file `.sql` thông thường, nhưng chứa **sqlc annotation** ở dạng SQL comment để sqlc phân tích metadata.
|
||||
|
||||
```sql
|
||||
-- name: <TênMethod> <:command>
|
||||
-- <comment mô tả (tuỳ chọn)>
|
||||
SELECT * FROM users WHERE id = $1;
|
||||
```
|
||||
|
||||
**Cú pháp annotation:**
|
||||
|
||||
```
|
||||
-- name: <MethodName> <:command>
|
||||
```
|
||||
|
||||
- `MethodName` → Tên method sẽ được sinh trong Go code (phải là identifier hợp lệ, khuyến nghị dùng **camelCase** hoặc **PascalCase**).
|
||||
- `:command` → Loại thao tác, quyết định kiểu trả về.
|
||||
|
||||
---
|
||||
|
||||
## 2. Các loại `:command`
|
||||
|
||||
| Command | Kiểu trả về (Go) | Dùng khi |
|
||||
| ------------- | --------------------- | ------------------------------------------------------------------------------------- |
|
||||
| `:one` | `(T, error)` | Trả về **đúng 1 row**. Nếu không tìm thấy → `sql.ErrNoRows`. |
|
||||
| `:many` | `([]T, error)` | Trả về **danh sách rows**. Có thể rỗng. |
|
||||
| `:exec` | `(sql.Result, error)` | Thực thi **không trả về row** (INSERT/UPDATE/DELETE không cần data trả về). |
|
||||
| `:execrows` | `(int64, error)` | Giống `:exec` nhưng trả về **số rows affected**. |
|
||||
| `:execresult` | `(sql.Result, error)` | Giống `:exec`, trả về `sql.Result` đầy đủ (có `.LastInsertId()` + `.RowsAffected()`). |
|
||||
| `:copyfrom` | `(int64, error)` | Dùng cho PostgreSQL `COPY FROM`, truyền slice struct. |
|
||||
|
||||
---
|
||||
|
||||
## 3. Quy tắc viết annotation
|
||||
|
||||
### 3.1. Annotation phải nằm **ngay trên** câu query
|
||||
|
||||
```sql
|
||||
-- ✅ ĐÚNG: annotation ngay trên query
|
||||
-- name: GetUser :one
|
||||
SELECT * FROM users WHERE id = $1;
|
||||
|
||||
-- ❌ SAI: có dòng trống giữa annotation và query
|
||||
-- name: GetUser :one
|
||||
|
||||
SELECT * FROM users WHERE id = $1;
|
||||
```
|
||||
|
||||
### 3.2. Mỗi query phải kết thúc bằng dấu `;`
|
||||
|
||||
```sql
|
||||
-- ✅ ĐÚNG
|
||||
-- name: GetUser :one
|
||||
SELECT * FROM users WHERE id = $1;
|
||||
|
||||
-- ❌ SAI: thiếu dấu ;
|
||||
-- name: GetUser :one
|
||||
SELECT * FROM users WHERE id = $1
|
||||
```
|
||||
|
||||
### 3.3. Tên method **không được trùng** trong cùng một file (hoặc cùng package queries)
|
||||
|
||||
```sql
|
||||
-- ❌ SAI: trùng tên
|
||||
-- name: GetUser :one
|
||||
SELECT * FROM users WHERE id = $1;
|
||||
|
||||
-- name: GetUser :many
|
||||
SELECT * FROM users WHERE role = $1;
|
||||
```
|
||||
|
||||
### 3.4. Có thể viết nhiều query trong 1 file
|
||||
|
||||
```sql
|
||||
-- name: GetUser :one
|
||||
SELECT * FROM users WHERE id = $1;
|
||||
|
||||
-- name: ListUsers :many
|
||||
SELECT * FROM users ORDER BY created_at DESC;
|
||||
|
||||
-- name: CreateUser :one
|
||||
INSERT INTO users (id, username, email)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteUser :exec
|
||||
DELETE FROM users WHERE id = $1;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Tham số (Parameters)
|
||||
|
||||
### 4.1. PostgreSQL — dùng positional params `$1, $2, ...`
|
||||
|
||||
```sql
|
||||
-- name: CreateUser :one
|
||||
INSERT INTO users (username, email, password_hash)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING *;
|
||||
```
|
||||
|
||||
### 4.2. Sử dụng `sqlc.arg()` — **khuyến nghị dùng** vì rõ ràng hơn
|
||||
|
||||
```sql
|
||||
-- name: CreateUser :one
|
||||
INSERT INTO users (username, email, password_hash)
|
||||
VALUES (sqlc.arg(username), sqlc.arg(email), sqlc.arg(password_hash))
|
||||
RETURNING *;
|
||||
```
|
||||
|
||||
> Khi dùng `sqlc.arg()`, sqlc sinh **tên param có ý nghĩa** trong Go struct thay vì `ID`/`Column2` không rõ ràng.
|
||||
|
||||
### 4.3. `sqlc.narg()` — nullable parameter
|
||||
|
||||
Dùng khi tham số có thể là `NULL`:
|
||||
|
||||
```sql
|
||||
-- name: SearchUsers :many
|
||||
SELECT * FROM users
|
||||
WHERE (
|
||||
sqlc.narg(username) IS NULL OR username = sqlc.narg(username)
|
||||
)
|
||||
AND (
|
||||
sqlc.narg(email) IS NULL OR email = sqlc.narg(email)
|
||||
);
|
||||
```
|
||||
|
||||
sqlc sẽ sinh kiểu `NullString` hoặc con trỏ `*string` (tuỳ config) cho các param này.
|
||||
|
||||
### 4.4. Truyền struct/array — `sqlc.arg()` với nhiều field
|
||||
|
||||
```sql
|
||||
-- name: UpdateUser :one
|
||||
UPDATE users
|
||||
SET
|
||||
username = COALESCE(sqlc.arg(username), username),
|
||||
email = COALESCE(sqlc.arg(email), email)
|
||||
WHERE id = sqlc.arg(id)
|
||||
RETURNING *;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. `RETURNING *` — Khuyến nghị dùng với `:one` / `:many`
|
||||
|
||||
Khi `INSERT`, `UPDATE`, `DELETE` mà bạn muốn nhận lại dữ liệu, hãy dùng `RETURNING *`:
|
||||
|
||||
```sql
|
||||
-- name: CreateUser :one
|
||||
INSERT INTO users (username, email)
|
||||
VALUES ($1, $2)
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateUser :one
|
||||
UPDATE users SET email = $2 WHERE id = $1
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteUser :one
|
||||
DELETE FROM users WHERE id = $1
|
||||
RETURNING *;
|
||||
```
|
||||
|
||||
Nếu không có `RETURNING *`, phải dùng `:exec` hoặc `:execrows`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Quy tắc cho `SELECT *`
|
||||
|
||||
sqlc sẽ phân tích schema và thay `*` bằng danh sách cột cụ thể trong code sinh ra. Tuy nhiên:
|
||||
|
||||
```sql
|
||||
-- ✅ Khuyến nghị: chỉ định cột rõ ràng
|
||||
-- name: GetUser :one
|
||||
SELECT id, username, email, created_at FROM users WHERE id = $1;
|
||||
|
||||
-- ✅ Cũng hợp lệ: dùng *
|
||||
-- name: GetUser :one
|
||||
SELECT * FROM users WHERE id = $1;
|
||||
```
|
||||
|
||||
> **Khi JOIN nhiều bảng**, KHÔNG nên dùng `SELECT *` vì có thể trùng tên cột. Nên alias rõ ràng.
|
||||
|
||||
---
|
||||
|
||||
## 7. JOIN và Alias
|
||||
|
||||
```sql
|
||||
-- name: GetUserWithRole :one
|
||||
SELECT
|
||||
u.id,
|
||||
u.username,
|
||||
u.email,
|
||||
r.name AS role_name
|
||||
FROM users u
|
||||
JOIN user_roles ur ON u.id = ur.user_id
|
||||
JOIN roles r ON ur.role_id = r.id
|
||||
WHERE u.id = $1;
|
||||
```
|
||||
|
||||
sqlc sẽ sinh struct với các field `ID`, `Username`, `Email`, `RoleName`.
|
||||
|
||||
---
|
||||
|
||||
## 8. Sử dụng `sqlc.embed()`
|
||||
|
||||
Dùng khi muốn **nhúng toàn bộ một struct** vào kết quả (từ sqlc v1.18+):
|
||||
|
||||
```sql
|
||||
-- name: GetUserWithRole :one
|
||||
SELECT
|
||||
sqlc.embed(u),
|
||||
r.name AS role_name
|
||||
FROM users u
|
||||
JOIN user_roles ur ON u.id = ur.user_id
|
||||
JOIN roles r ON ur.role_id = r.id
|
||||
WHERE u.id = $1;
|
||||
```
|
||||
|
||||
→ Go struct sinh ra sẽ có dạng:
|
||||
|
||||
```go
|
||||
type GetUserWithRoleRow struct {
|
||||
User // embedded struct từ bảng users
|
||||
RoleName string
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Sử dụng `sqlc.slice()` — IN clause
|
||||
|
||||
Dùng cho `WHERE id IN ($1, $2, ...)` với số lượng phần tử động:
|
||||
|
||||
```sql
|
||||
-- name: GetUsersByIDs :many
|
||||
SELECT * FROM users
|
||||
WHERE id = ANY(sqlc.slice(ids));
|
||||
```
|
||||
|
||||
hoặc với PostgreSQL:
|
||||
|
||||
```sql
|
||||
-- name: GetUsersByIDs :many
|
||||
SELECT * FROM users
|
||||
WHERE id = ANY($1::uuid[]);
|
||||
```
|
||||
|
||||
Nhưng `sqlc.slice()` được khuyến nghị hơn vì sqlc sẽ tự xử lý kiểu.
|
||||
|
||||
---
|
||||
|
||||
## 10. Sub-query và CTE
|
||||
|
||||
sqlc hỗ trợ đầy đủ:
|
||||
|
||||
```sql
|
||||
-- name: GetUserStats :one
|
||||
WITH user_orders AS (
|
||||
SELECT user_id, COUNT(*) AS order_count
|
||||
FROM orders
|
||||
GROUP BY user_id
|
||||
)
|
||||
SELECT u.*, COALESCE(uo.order_count, 0) AS order_count
|
||||
FROM users u
|
||||
LEFT JOIN user_orders uo ON u.id = uo.user_id
|
||||
WHERE u.id = $1;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. CASE expression
|
||||
|
||||
```sql
|
||||
-- name: ListUsersWithStatus :many
|
||||
SELECT
|
||||
id,
|
||||
username,
|
||||
CASE
|
||||
WHEN deleted_at IS NOT NULL THEN 'deleted'
|
||||
WHEN last_login_at > NOW() - INTERVAL '30 days' THEN 'active'
|
||||
ELSE 'inactive'
|
||||
END AS status
|
||||
FROM users;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Transaction — không viết trong file query
|
||||
|
||||
sqlc **không quản lý transaction** trong file `.sql`. Transaction được xử lý ở tầng ứng dụng Go:
|
||||
|
||||
```go
|
||||
// Trong Go code (không phải file .sql)
|
||||
tx, _ := db.BeginTx(ctx, nil)
|
||||
q := New(tx) // tạo Queries instance với tx
|
||||
q.CreateUser(ctx, ...)
|
||||
q.CreateUserRole(ctx, ...)
|
||||
tx.Commit()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. Comment thường vs sqlc annotation
|
||||
|
||||
```sql
|
||||
-- Đây là comment thường, sqlc bỏ qua
|
||||
-- name: GetUser :one ← Đây là sqlc annotation
|
||||
SELECT * FROM users WHERE id = $1;
|
||||
|
||||
/*
|
||||
Multi-line comment cũng được
|
||||
sqlc bỏ qua
|
||||
*/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. Quy tắc đặt tên file
|
||||
|
||||
| Quy ước | Ví dụ |
|
||||
| ------------------------------------------------------------------ | ----------------------------------------- |
|
||||
| 1 file = 1 bảng chính | `users.sql`, `orders.sql`, `products.sql` |
|
||||
| Đặt tên theo **feature/domain** | `auth.sql`, `inventory.sql` |
|
||||
| File nằm trong thư mục được chỉ định ở `queries` trong `sqlc.yaml` | `./db/queries/` |
|
||||
|
||||
---
|
||||
|
||||
## 15. Toàn bộ mẫu tham khảo
|
||||
|
||||
```sql
|
||||
-- name: GetUser :one
|
||||
SELECT * FROM users WHERE id = sqlc.arg(id);
|
||||
|
||||
-- name: GetUserByEmail :one
|
||||
SELECT * FROM users WHERE email = sqlc.arg(email);
|
||||
|
||||
-- name: ListUsers :many
|
||||
SELECT * FROM users
|
||||
ORDER BY created_at DESC
|
||||
LIMIT sqlc.arg(limit) OFFSET sqlc.arg(offset);
|
||||
|
||||
-- name: SearchUsers :many
|
||||
SELECT * FROM users
|
||||
WHERE (
|
||||
sqlc.narg(username) IS NULL OR username ILIKE '%' || sqlc.narg(username) || '%'
|
||||
)
|
||||
AND (
|
||||
sqlc.narg(email) IS NULL OR email ILIKE '%' || sqlc.narg(email) || '%'
|
||||
);
|
||||
|
||||
-- name: CreateUser :one
|
||||
INSERT INTO users (id, username, email, password_hash)
|
||||
VALUES (
|
||||
sqlc.arg(id),
|
||||
sqlc.arg(username),
|
||||
sqlc.arg(email),
|
||||
sqlc.arg(password_hash)
|
||||
)
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateUser :one
|
||||
UPDATE users
|
||||
SET
|
||||
username = COALESCE(sqlc.arg(username), username),
|
||||
email = COALESCE(sqlc.arg(email), email)
|
||||
WHERE id = sqlc.arg(id)
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteUser :execrows
|
||||
DELETE FROM users WHERE id = sqlc.arg(id);
|
||||
|
||||
-- name: GetUsersByIDs :many
|
||||
SELECT * FROM users WHERE id = ANY(sqlc.slice(ids));
|
||||
|
||||
-- name: CountUsers :one
|
||||
SELECT COUNT(*) AS count FROM users;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tóm tắt nhanh
|
||||
|
||||
| Quy tắc | Mô tả |
|
||||
| -------------------------- | ----------------------------------------------------- |
|
||||
| Annotation format | `-- name: MethodName :command` |
|
||||
| Phải có `;` kết thúc | Mỗi query kết thúc bằng dấu chấm phẩy |
|
||||
| Annotation ngay trên query | Không có dòng trống ở giữa |
|
||||
| Tên method không trùng | Trong cùng package queries |
|
||||
| Dùng `sqlc.arg()` | Khuyến nghị thay vì `$1` để sinh tên param có ý nghĩa |
|
||||
| Dùng `sqlc.narg()` | Cho nullable parameter |
|
||||
| Dùng `sqlc.slice()` | Cho `IN (...)` dynamic |
|
||||
| Dùng `sqlc.embed()` | Để nhúng struct khi JOIN |
|
||||
| `RETURNING *` | Khi cần dữ liệu trả về với INSERT/UPDATE/DELETE |
|
||||
| Transaction | Không viết trong `.sql`, xử lý ở Go code |
|
||||
526
docs/swagger/docs.go
Normal file
526
docs/swagger/docs.go
Normal file
@@ -0,0 +1,526 @@
|
||||
// Package swagger Code generated by swaggo/swag. DO NOT EDIT
|
||||
package swagger
|
||||
|
||||
import "github.com/swaggo/swag"
|
||||
|
||||
const docTemplate = `{
|
||||
"schemes": {{ marshal .Schemes }},
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "{{escape .Description}}",
|
||||
"title": "{{.Title}}",
|
||||
"contact": {},
|
||||
"version": "{{.Version}}"
|
||||
},
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {
|
||||
"/auth/register": {
|
||||
"post": {
|
||||
"description": "Register with email, username and password",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"auth"
|
||||
],
|
||||
"summary": "Register a new user",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Register request",
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/requests.BodyRegisterRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/response.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/responses.BodyRegisterResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"409": {
|
||||
"description": "Conflict",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ping": {
|
||||
"get": {
|
||||
"description": "Check server is running",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"health"
|
||||
],
|
||||
"summary": "Health check",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/warehouses": {
|
||||
"get": {
|
||||
"description": "Retrieve a list of all warehouses ordered by creation date",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"warehouse"
|
||||
],
|
||||
"summary": "List all warehouses",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/response.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/models.Warehouse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Create a new warehouse with the provided details",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"warehouse"
|
||||
],
|
||||
"summary": "Create a new warehouse",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Warehouse request body",
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/requests.CUWarehouseRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/response.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/responses.CreateWarehouseResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/warehouses/{id}": {
|
||||
"get": {
|
||||
"description": "Retrieve a single warehouse using its unique identifier",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"warehouse"
|
||||
],
|
||||
"summary": "Get warehouse by ID",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Warehouse ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/response.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/models.Warehouse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"description": "Update an existing warehouse by its ID with the provided details",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"warehouse"
|
||||
],
|
||||
"summary": "Update warehouse",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Warehouse ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Warehouse request body",
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/requests.CUWarehouseRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/response.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/responses.UpdateWarehouseResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"description": "Delete a warehouse by its unique identifier",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"warehouse"
|
||||
],
|
||||
"summary": "Delete warehouse",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Warehouse ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.SuccessResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"models.Warehouse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {
|
||||
"type": "string"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requests.BodyRegisterRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"email",
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"fullName": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"minLength": 8
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requests.CUWarehouseRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"address",
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"response.ErrorResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"now": {
|
||||
"type": "integer"
|
||||
},
|
||||
"status": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"response.SuccessResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"option": {},
|
||||
"reason_status_code": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses.BodyRegisterResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses.CreateWarehouseResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses.UpdateWarehouseResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
||||
var SwaggerInfo = &swag.Spec{
|
||||
Version: "1.0",
|
||||
Host: "localhost:3000",
|
||||
BasePath: "/api/v1",
|
||||
Schemes: []string{},
|
||||
Title: "Warehouse Management API",
|
||||
Description: "This is the Warehouse Management API server.",
|
||||
InfoInstanceName: "swagger",
|
||||
SwaggerTemplate: docTemplate,
|
||||
LeftDelim: "{{",
|
||||
RightDelim: "}}",
|
||||
}
|
||||
|
||||
func init() {
|
||||
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
|
||||
}
|
||||
502
docs/swagger/swagger.json
Normal file
502
docs/swagger/swagger.json
Normal file
@@ -0,0 +1,502 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "This is the Warehouse Management API server.",
|
||||
"title": "Warehouse Management API",
|
||||
"contact": {},
|
||||
"version": "1.0"
|
||||
},
|
||||
"host": "localhost:3000",
|
||||
"basePath": "/api/v1",
|
||||
"paths": {
|
||||
"/auth/register": {
|
||||
"post": {
|
||||
"description": "Register with email, username and password",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"auth"
|
||||
],
|
||||
"summary": "Register a new user",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Register request",
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/requests.BodyRegisterRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/response.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/responses.BodyRegisterResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"409": {
|
||||
"description": "Conflict",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ping": {
|
||||
"get": {
|
||||
"description": "Check server is running",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"health"
|
||||
],
|
||||
"summary": "Health check",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/warehouses": {
|
||||
"get": {
|
||||
"description": "Retrieve a list of all warehouses ordered by creation date",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"warehouse"
|
||||
],
|
||||
"summary": "List all warehouses",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/response.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/models.Warehouse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Create a new warehouse with the provided details",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"warehouse"
|
||||
],
|
||||
"summary": "Create a new warehouse",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Warehouse request body",
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/requests.CUWarehouseRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/response.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/responses.CreateWarehouseResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/warehouses/{id}": {
|
||||
"get": {
|
||||
"description": "Retrieve a single warehouse using its unique identifier",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"warehouse"
|
||||
],
|
||||
"summary": "Get warehouse by ID",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Warehouse ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/response.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/models.Warehouse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"description": "Update an existing warehouse by its ID with the provided details",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"warehouse"
|
||||
],
|
||||
"summary": "Update warehouse",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Warehouse ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Warehouse request body",
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/requests.CUWarehouseRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/response.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/responses.UpdateWarehouseResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"description": "Delete a warehouse by its unique identifier",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"warehouse"
|
||||
],
|
||||
"summary": "Delete warehouse",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Warehouse ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.SuccessResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"models.Warehouse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {
|
||||
"type": "string"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requests.BodyRegisterRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"email",
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"fullName": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"minLength": 8
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requests.CUWarehouseRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"address",
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"response.ErrorResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"now": {
|
||||
"type": "integer"
|
||||
},
|
||||
"status": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"response.SuccessResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"option": {},
|
||||
"reason_status_code": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses.BodyRegisterResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses.CreateWarehouseResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses.UpdateWarehouseResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
317
docs/swagger/swagger.yaml
Normal file
317
docs/swagger/swagger.yaml
Normal file
@@ -0,0 +1,317 @@
|
||||
basePath: /api/v1
|
||||
definitions:
|
||||
models.Warehouse:
|
||||
properties:
|
||||
address:
|
||||
type: string
|
||||
createdAt:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
name:
|
||||
type: string
|
||||
updatedAt:
|
||||
type: string
|
||||
type: object
|
||||
requests.BodyRegisterRequest:
|
||||
properties:
|
||||
email:
|
||||
type: string
|
||||
fullName:
|
||||
type: string
|
||||
password:
|
||||
minLength: 8
|
||||
type: string
|
||||
username:
|
||||
type: string
|
||||
required:
|
||||
- email
|
||||
- password
|
||||
- username
|
||||
type: object
|
||||
requests.CUWarehouseRequest:
|
||||
properties:
|
||||
address:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
required:
|
||||
- address
|
||||
- name
|
||||
type: object
|
||||
response.ErrorResponse:
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
message:
|
||||
type: string
|
||||
now:
|
||||
type: integer
|
||||
status:
|
||||
type: integer
|
||||
type: object
|
||||
response.SuccessResponse:
|
||||
properties:
|
||||
data: {}
|
||||
message:
|
||||
type: string
|
||||
option: {}
|
||||
reason_status_code:
|
||||
type: string
|
||||
status:
|
||||
type: integer
|
||||
type: object
|
||||
responses.BodyRegisterResponse:
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
type: object
|
||||
responses.CreateWarehouseResponse:
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
type: object
|
||||
responses.UpdateWarehouseResponse:
|
||||
properties:
|
||||
address:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
name:
|
||||
type: string
|
||||
type: object
|
||||
host: localhost:3000
|
||||
info:
|
||||
contact: {}
|
||||
description: This is the Warehouse Management API server.
|
||||
title: Warehouse Management API
|
||||
version: "1.0"
|
||||
paths:
|
||||
/auth/register:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Register with email, username and password
|
||||
parameters:
|
||||
- description: Register request
|
||||
in: body
|
||||
name: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/requests.BodyRegisterRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"201":
|
||||
description: Created
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/response.SuccessResponse'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/responses.BodyRegisterResponse'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
"409":
|
||||
description: Conflict
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
summary: Register a new user
|
||||
tags:
|
||||
- auth
|
||||
/ping:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Check server is running
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
summary: Health check
|
||||
tags:
|
||||
- health
|
||||
/v1/warehouses:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Retrieve a list of all warehouses ordered by creation date
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/response.SuccessResponse'
|
||||
- properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/models.Warehouse'
|
||||
type: array
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
summary: List all warehouses
|
||||
tags:
|
||||
- warehouse
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Create a new warehouse with the provided details
|
||||
parameters:
|
||||
- description: Warehouse request body
|
||||
in: body
|
||||
name: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/requests.CUWarehouseRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"201":
|
||||
description: Created
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/response.SuccessResponse'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/responses.CreateWarehouseResponse'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
summary: Create a new warehouse
|
||||
tags:
|
||||
- warehouse
|
||||
/v1/warehouses/{id}:
|
||||
delete:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Delete a warehouse by its unique identifier
|
||||
parameters:
|
||||
- description: Warehouse ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/response.SuccessResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
summary: Delete warehouse
|
||||
tags:
|
||||
- warehouse
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Retrieve a single warehouse using its unique identifier
|
||||
parameters:
|
||||
- description: Warehouse ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/response.SuccessResponse'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/models.Warehouse'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
summary: Get warehouse by ID
|
||||
tags:
|
||||
- warehouse
|
||||
put:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Update an existing warehouse by its ID with the provided details
|
||||
parameters:
|
||||
- description: Warehouse ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
- description: Warehouse request body
|
||||
in: body
|
||||
name: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/requests.CUWarehouseRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/response.SuccessResponse'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/responses.UpdateWarehouseResponse'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
summary: Update warehouse
|
||||
tags:
|
||||
- warehouse
|
||||
swagger: "2.0"
|
||||
Reference in New Issue
Block a user