Base Project

This commit is contained in:
Tran Anh Tuan
2026-05-08 14:32:24 +07:00
parent 5a9249c9ea
commit 6a4a96e0ca
74 changed files with 6749 additions and 0 deletions

427
docs/db/WareHouseDB.md Normal file
View 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
View 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
View 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
View 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
View 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
View 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"