API sàn TMDT: Đặc tả & PRD đầy đủ nghiệp vụ

API sàn TMDT: Đặc tả & PRD đầy đủ nghiệp vụ
5.5k một tháng trước

1. Lời mở đầu…

Hello các bạn, lại là mình đây. Mùa làm đồ án tốt nghiệp lại tới, và shop bán hàng, thương mại điện tử lại được các coder tương lai lựa chọn làm đề tài chính cho sản phẩm tốt nghiệp của mình/nhóm mình vì tính phổ biến, dễ hiểu nghiệp vụ và thường gặp trong các bài tập lớn. Tuy nhiên, nếu để làm ở mức nhỏ, đơn giản thì ai cũng làm được, mức độ cạnh tranh rất cao cũng như không đủ wow trong mắt các giáo viên cũng như nhà tuyển dụng. Vì vậy để giúp các bạn phân tích một phần nhỏ về các tính năng của shop bán hàng, thương mại điện tử, mình sẽ viết một số API cho các bạn tham khảo một cách đơn giản nhất có thể.

Lưu ý: Không cần thiết phải thực hiện tất cả các tính năng, API trong này, tuy nhiên nếu hoàn thiện hết, thì chắc chắn bạn sẽ có một sản phẩm tốt nghiệp đẹp. Và sẵn sàng đem cho các nhà tuyển dụng với offer mức lương khủng bố hehe.


2. Hướng dẫn sử dụng

  • Tất cả các API (trừ đăng ký, đăng nhập, xác thực email, refresh token, reset password) đều yêu cầu xác thực Bearer Token.
  • Các trường hợp lỗi trả về { "error": "..." } với status code phù hợp.
  • Super admin có quyền duyệt ngành hàng, xác minh shop, tạo voucher toàn sàn.
  • Shop owner chỉ tạo được voucher riêng shop, danh mục riêng, sản phẩm riêng.
  • Các API mock cho phép đổi trạng thái đơn hàng, session thanh toán để test frontend.

3. Endpoints & Chức năng

3.1. Authentication & User

Đăng ký tài khoản

  • POST /auth/register
  • Body:
{
	"email": "[email protected]",
	"password": "P@ssw0rd123",
	"name": "Nguyen Van A"
}
  • Response:
{
	"status": 201,
	"message": "User registered successfully",
	"data": {
		"user": {
			"userId": "u123",
			"email": "[email protected]",
			"name": "Nguyen Van A"
		},
		"token": "...",
		"refreshToken": "..."
	},
	"pagination": null
}
  • Error:
{
	"status": 400,
	"message": "Email already exists",
	"data": null,
	"pagination": null
}

Đăng nhập

  • POST /auth/login
  • Body:
{
	"email": "[email protected]",
	"password": "P@ssw0rd123"
}
  • Response:
{
	"status": 200,
	"message": "Login successful",
	"data": {
		"user": {
			"userId": "u123",
			"email": "[email protected]",
			"name": "Nguyen Van A"
		},
		"token": "...",
		"refreshToken": "..."
	},
	"pagination": null
}
  • Error:
{
	"status": 401,
	"message": "Invalid credentials",
	"data": null,
	"pagination": null
}

Refresh token

  • POST /auth/refresh-token
  • Body:
{ "refreshToken": "..." }
  • Response:
{
	"status": 200,
	"message": "Token refreshed",
	"data": {
		"token": "...",
		"refreshToken": "..."
	},
	"pagination": null
}
  • Error:
{
	"status": 401,
	"message": "Invalid or expired refresh token",
	"data": null,
	"pagination": null
}

Xác thực email

  • GET /auth/validate?token=...
  • Response:
{
	"status": 200,
	"message": "Email validated",
	"data": null,
	"pagination": null
}
  • Error:
{
	"status": 400,
	"message": "Invalid or expired token",
	"data": null,
	"pagination": null
}

Đặt lại mật khẩu

  • POST /auth/reset-password
  • Body:
{ "email": "[email protected]" }
  • Response:
{
	"status": 200,
	"message": "Password reset email sent",
	"data": null,
	"pagination": null
}
  • Error:
{
	"status": 404,
	"message": "Email not found",
	"data": null,
	"pagination": null
}

Đăng xuất

  • POST /auth/logout
  • Header: Authorization: Bearer <token>
  • Response:
{
	"status": 200,
	"message": "Logged out successfully",
	"data": null,
	"pagination": null
}
  • Error:
{
	"status": 401,
	"message": "Invalid token",
	"data": null,
	"pagination": null
}

3.2. Shop Management

Tạo shop

  • POST /shops
  • Body:
{
	"name": "Shop A",
	"description": "Shop bán hàng của A",
	"address": "123 Đường ABC",
	"industries": ["Thời trang"]
}
  • Response:
{
	"status": 201,
	"message": "Shop created successfully",
	"data": {
		"shopId": "s456",
		"name": "Shop A",
		"description": "Shop bán hàng của A",
		"address": "123 Đường ABC",
		"industries": ["Thời trang"],
		"verified": false,
		"metadata": {
			"totalProducts": 0,
			"realProducts": 0,
			"followers": 0,
			"joinedAt": "2024-06-01"
		}
	},
	"pagination": null
}
  • Error:
{
	"status": 400,
	"message": "Shop name already taken",
	"data": null,
	"pagination": null
}

Chỉnh sửa shop

  • PUT /shops/{shopId}
  • Body:
{
	"name": "Shop A Updated",
	"description": "Shop bán hàng của A Updated",
	"address": "456 Đường XYZ",
	"industries": ["Thời trang", "Phụ kiện"]
}
  • Response:
{
	"status": 200,
	"message": "Shop updated successfully",
	"data": {
		"shopId": "s456",
		"name": "Shop A Updated",
		"description": "Shop bán hàng của A Updated",
		"address": "456 Đường XYZ",
		"industries": ["Thời trang", "Phụ kiện"],
		"verified": false,
		"metadata": {
			"totalProducts": 10,
			"realProducts": 8,
			"followers": 1234,
			"joinedAt": "2024-01-01"
		}
	},
	"pagination": null
}
  • Error:
{
	"status": 404,
	"message": "Shop not found",
	"data": null,
	"pagination": null
}

Lấy thông tin shop

  • GET /shops/{shopId}
  • Response:
{
	"status": 200,
	"message": "Shop info fetched successfully",
	"data": {
			"shopId": "s456",
			"name": "Shop A",
			"description": "Shop bán hàng của A",
			"address": "123 Đường ABC",
			"industries": ["Thời trang"],
			"verified": false,
			"products": [ ... ],
			"outOfStockProducts": [ ... ],
			"categories": [ ... ],
			"vouchers": [ ... ],
			"metadata": {
				"totalProducts": 100,
				"realProducts": 80,
				"followers": 1234,
				"joinedAt": "2024-01-01"
			}
	},
	"pagination": null
}
  • Error:
{
	"status": 404,
	"message": "Shop not found",
	"data": null,
	"pagination": null
}

Xác minh shop

  • POST /shops/{shopId}/verify
  • Body:
{
	"registrationNumber": "123456789",
	"businessAddress": "789 Đường KLM",
	"productDescription": "Áo quần thời trang",
	"operatingHours": "9:00 - 17:00"
}
  • Response:
{
	"status": 200,
	"message": "Verification request submitted",
	"data": null,
	"pagination": null
}
  • Error:
{
	"status": 400,
	"message": "Missing required information",
	"data": null,
	"pagination": null
}

Xóa shop

  • DELETE /shops/{shopId}
  • Response:
{
	"status": 200,
	"message": "Shop deleted successfully",
	"data": {
		"shopId": "s456",
		"name": "Shop A Updated",
		"description": "Shop bán hàng của A Updated",
		"address": "456 Đường XYZ",
		"industries": ["Thời trang", "Phụ kiện"]
	},
	"pagination": null
}
  • Error:
{
	"status": 403,
	"message": "Shop has products, cannot delete",
	"data": null,
	"pagination": null
}

Lấy list ngành hàng

  • GET /industries
  • Response:
{
	"status": 200,
	"message": "Industry list fetched successfully",
	"data": [{ "id": "i1", "title": "Thời trang" }],
	"pagination": null
}

Yêu cầu thêm ngành hàng mới

  • POST /industries/request
  • Body:
{ "title": "Ngành mới" }
  • Response:
{
	"status": 200,
	"message": "Industry request submitted",
	"data": null,
	"pagination": null
}

3.3. Product Management

Tạo sản phẩm

  • POST /products
  • Body:
{
	"name": "Áo thun trắng",
	"description": "Áo thun cotton mềm mại",
	"price": 150000,
	"images": ["https://example.com/image1.jpg"],
	"tags": ["thời trang", "áo thun"],
	"categoryId": "cat789",
	"attributes": [{ "title": "Chất liệu", "description": "Cotton" }],
	"variants": [
		{
			"sku": "TSHIRT-WHITE-L",
			"attributes": { "color": "trắng", "size": "L" },
			"price": 155000,
			"stock": 20,
			"images": ["https://example.com/variant1.jpg"]
		},
		{
			"sku": "TSHIRT-WHITE-M",
			"attributes": { "color": "trắng", "size": "M" },
			"price": 150000,
			"stock": 10,
			"images": ["https://example.com/variant2.jpg"]
		}
	]
}
  • Response:
{
	"status": 201,
	"message": "Product created successfully",
	"data": {
		"productId": "p101",
		"name": "Áo thun trắng",
		"description": "Áo thun cotton mềm mại",
		"price": 150000,
		"images": ["https://example.com/image1.jpg"],
		"tags": ["thời trang", "áo thun"],
		"categoryId": "cat789",
		"attributes": [{ "title": "Chất liệu", "description": "Cotton" }],
		"variants": [
			{
				"variantId": "v404",
				"sku": "TSHIRT-WHITE-L",
				"attributes": { "color": "trắng", "size": "L" },
				"price": 155000,
				"stock": 20,
				"images": ["https://example.com/variant1.jpg"]
			},
			{
				"variantId": "v405",
				"sku": "TSHIRT-WHITE-M",
				"attributes": { "color": "trắng", "size": "M" },
				"price": 150000,
				"stock": 10,
				"images": ["https://example.com/variant2.jpg"]
			}
		]
	},
	"pagination": null
}
  • Error:
{ "error": "Category not found" }

Chỉnh sửa sản phẩm

  • PUT /products/{productId}
  • Body: như tạo sản phẩm
  • Response:
{
	"status": 200,
	"message": "Product updated successfully",
	"data": {
		"productId": "p101",
		"name": "Áo thun trắng Updated",
		"description": "Áo thun cotton mềm mại Updated",
		"price": 155000,
		"images": ["https://example.com/image1.jpg"],
		"tags": ["thời trang", "áo thun"],
		"categoryId": "cat789",
		"attributes": [{ "title": "Chất liệu", "description": "Cotton" }],
		"variants": [
			{
				"variantId": "v404",
				"sku": "TSHIRT-WHITE-L",
				"attributes": { "color": "trắng", "size": "L" },
				"price": 155000,
				"stock": 20,
				"images": ["https://example.com/variant1.jpg"]
			},
			{
				"variantId": "v405",
				"sku": "TSHIRT-WHITE-M",
				"attributes": { "color": "trắng", "size": "M" },
				"price": 150000,
				"stock": 10,
				"images": ["https://example.com/variant2.jpg"]
			}
		]
	},
	"pagination": null
}

Xóa sản phẩm

  • DELETE /products/{productId}
  • Response:
{
	"status": 200,
	"message": "Product deleted successfully",
	"data": {
		"productId": "p101",
		"name": "Áo thun trắng Updated",
		"description": "Áo thun cotton mềm mại Updated"
	},
	"pagination": null
}

Tạo tag

  • POST /tags
  • Body:
{
	"title": "Mùa hè",
	"description": "Sản phẩm phù hợp mùa hè",
	"industry": "Thời trang"
}
  • Response:
{
	"status": 201,
	"message": "Tag created successfully",
	"data": {
		"tagId": "t202",
		"title": "Mùa hè",
		"description": "Sản phẩm phù hợp mùa hè",
		"industry": "Thời trang"
	},
	"pagination": null
}

Gợi ý tag

  • GET /tags?industry=...

Example: GET /tags?industry=Mùa

  • Response:
{
	"status": 200,
	"message": "Tag list fetched successfully",
	"data": [
		{ "id": "t202", "title": "Mùa hè" },
		{ "id": "t203", "title": "Mùa đông" },
		{ "id": "t204", "title": "Mùa xuân" },
		{ "id": "t205", "title": "Mùa thu" },
		{ "id": "t206", "title": "Mùa lễ hội" }
	],
	"pagination": {
		"total": 10,
		"page": 1,
		"limit": 5,
		"canNext": true,
		"canPrev": false
	}
}

Nhóm sản phẩm theo danh mục

  • POST /categories/{categoryId}/products
  • Body:
{ "productId": "p101" }
  • Response:
{
	"status": 200,
	"message": "Product added to category",
	"data": null,
	"pagination": null
}
  • Error:
{
	"status": 404,
	"message": "Category not found",
	"data": null,
	"pagination": null
}

Tạo danh mục

  • POST /categories
  • Body:
{ "title": "Áo thun", "description": "Danh mục áo thun", "shopId": "s456" }
  • Response:
{
	"status": 201,
	"message": "Category created successfully",
	"data": {
		"categoryId": "cat789",
		"title": "Áo thun",
		"description": "Danh mục áo thun",
		"shopId": "s456"
	},
	"pagination": null
}

Biến thể sản phẩm (Product Variants)

  • POST /products/{productId}/variants
  • Body:
{
	"sku": "TSHIRT-WHITE-L",
	"attributes": { "color": "trắng", "size": "L" },
	"price": 155000,
	"stock": 20,
	"images": ["https://example.com/variant1.jpg"]
}
  • Response:
{
	"status": 201,
	"message": "Variant created successfully",
	"data": {
		"variantId": "v404",
		"sku": "TSHIRT-WHITE-L",
		"attributes": { "color": "trắng", "size": "L" },
		"price": 155000,
		"stock": 20,
		"images": ["https://example.com/variant1.jpg"]
	},
	"pagination": null
}

Lấy danh sách biến thể

  • GET /products/{productId}/variants
  • Response:
{
	"status": 200,
	"message": "Variant list fetched successfully",
	"data": [
		{
			"variantId": "v404",
			"sku": "TSHIRT-WHITE-L",
			"attributes": { "color": "trắng", "size": "L" },
			"price": 155000,
			"stock": 20,
			"images": ["https://example.com/variant1.jpg"]
		}
	],
	"pagination": null
}

Sản phẩm liên quan

  • GET /products/{productId}/related?type=attribute|tag|userBehavior

  • Response:

{
	"status": 200,
	"message": "Related products fetched successfully",
	"data": [
		{ "productId": "p101", "name": "Áo thun trắng" },
		{ "productId": "p102", "name": "Áo thun đen" },
		{ "productId": "p103", "name": "Áo thun xanh" },
		{ "productId": "p104", "name": "Áo thun vàng" },
		{ "productId": "p105", "name": "Áo thun đỏ" }
	],
	"pagination": {
		"total": 10,
		"page": 1,
		"limit": 5,
		"canNext": true,
		"canPrev": false
	}
}

Đánh giá sản phẩm

  • POST /products/{productId}/reviews
  • Body:
{
	"rating": 5,
	"comment": "Sản phẩm tuyệt vời!",
	"images": ["https://example.com/review1.jpg"]
}
  • Response:
{
	"status": 201,
	"message": "Review created successfully",
	"data": {
		"reviewId": "r303",
		"rating": 5,
		"comment": "Sản phẩm tuyệt vời!",
		"images": ["https://example.com/review1.jpg"]
	},
	"pagination": null
}
  • Error:
{
	"status": 404,
	"message": "Product not found",
	"data": null,
	"pagination": null
}

Lấy danh sách đánh giá

  • GET /products/{productId}/reviews
  • Response:
{
	"status": 200,
	"message": "Review list fetched successfully",
	"data": [
		{ "reviewId": "r303", "rating": 5, "comment": "Tuyệt vời!" },
		{ "reviewId": "r304", "rating": 4, "comment": "Tuyệt vời!" },
		{ "reviewId": "r305", "rating": 5, "comment": "Tuyệt vời!" },
		{ "reviewId": "r306", "rating": 4, "comment": "Tuyệt vời!" },
		{ "reviewId": "r307", "rating": 5, "comment": "Tuyệt vời!" }
	],
	"pagination": {
		"total": 50,
		"page": 1,
		"limit": 5,
		"canNext": true,
		"canPrev": false
	}
}

Comment sản phẩm

  • POST /products/{productId}/comments
  • Body:
{ "comment": "Đẹp quá!", "images": ["https://example.com/comment1.jpg"] }
  • Response:
{
	"status": 201,
	"message": "Comment created successfully",
	"data": {
		"commentId": "c505",
		"comment": "Đẹp quá!",
		"images": ["https://example.com/comment1.jpg"]
	},
	"pagination": null
}

Lấy danh sách comment

  • GET /products/{productId}/comments
  • Response:
{
	"status": 200,
	"message": "Comment list fetched successfully",
	"data": [
		{ "commentId": "c506", "comment": "Đẹp quá!" },
		{ "commentId": "c507", "comment": "Đẹp quá!" },
		{ "commentId": "c508", "comment": "Đẹp quá!" },
		{ "commentId": "c509", "comment": "Đẹp quá!" },
		{ "commentId": "c510", "comment": "Đẹp quá!" }
	],
	"pagination": {
		"total": 10,
		"page": 2,
		"limit": 5,
		"canNext": true,
		"canPrev": true
	}
}

Ghi nhận hành vi người dùng (User Behavior Tracking)

  • POST /user-behaviors
  • Body:
{
	"userId": "u123",
	"productId": "p101",
	"action": "VIEW" // VIEW | CLICK | ADD_TO_CART | PURCHASE
}
  • Response:
{
	"status": 201,
	"message": "User behavior tracked successfully",
	"data": {
		"userId": "u123",
		"productId": "p101",
		"action": "VIEW" // VIEW | CLICK | ADD_TO_CART | PURCHASE
	},
	"pagination": null
}

3.4. Cart & Checkout

Thêm vào giỏ hàng

  • POST /carts
  • Body:
{ "productId": "p101", "quantity": 2, "variantId": "v404" }
  • Response:
{
	"status": 201,
	"message": "Added to cart",
	"data": {
		"cartProducts": [
			{
				"shopId": "s456",
				"products": [
					{
						"productId": "p101",
						"variantId": "v404",
						"quantity": 2,
						"price": 155000,
						"inStock": 20,
						"inCarts": 10
					}
				],
				"shopVoucher": { "code": "SHOPSALE", "discount": 20000 }
			}
		],
		"globalVouchers": [{ "code": "SUMMER2025", "discount": 50000 }],
		"shippingVouchers": [{ "code": "FREESHIP", "discount": 30000 }],
		"total": 310000,
		"finalTotal": 260000
	},
	"pagination": null
}
  • Error:
{
	"status": 400,
	"message": "Product out of stock",
	"data": null,
	"pagination": null
}

Xem giỏ hàng

  • GET /carts/{cartId}
  • Response:
{
	"status": 200,
	"message": "Cart fetched successfully",
	"data": {
		"items": [
			{
				"shopId": "s456",
				"products": [
					{
						"productId": "p101",
						"variantId": "v404",
						"quantity": 2,
						"price": 155000,
						"inStock": 20,
						"inCarts": 10
					}
				],
				"shopVoucher": { "code": "SHOPSALE", "discount": 20000 }
			}
		],
		"globalVouchers": [{ "code": "SUMMER2025", "discount": 50000 }],
		"shippingVouchers": [{ "code": "FREESHIP", "discount": 30000 }],
		"total": 310000,
		"finalTotal": 260000
	},
	"pagination": null
}

Áp dụng mã giảm giá

  • POST /carts/{cartId}/vouchers
  • Body:
{ "voucherCode": "SUMMER2025" }
  • Response:
{
	"status": 200,
	"message": "Voucher applied",
	"data": {
		"discount": 50000,
		"voucherCode": "SUMMER2025"
	},
	"pagination": null
}
  • Error:
{
	"status": 400,
	"message": "Voucher not valid",
	"data": null,
	"pagination": null
}

Thanh toán (Checkout)

  • POST /checkout
  • Body:
{
	"cartId": "c505",
	"addressId": "a606",
	"paymentMethod": "ewallet",
	"voucherCode": "SUMMER2025"
}
  • Response:
{
	"status": 201,
	"message": "Checkout created successfully",
	"data": {
		"orderId": "o707",
		"sessionId": "s808"
	},
	"pagination": null
}
  • Error:
{
	"status": 400,
	"message": "Cart is empty",
	"data": null,
	"pagination": null
}

Kiểm tra trạng thái thanh toán

  • GET /checkout/{sessionId}/status
  • Response:
{
	"status": 200,
	"message": "Session status fetched successfully",
	"data": {
		"status": "COMPLETED"
	},
	"pagination": null
}

API mock đổi trạng thái session

  • POST /checkout/{sessionId}/mock-status
  • Body:
{ "status": "COMPLETED" }
  • Response:
{
	"status": 200,
	"message": "Session status updated",
	"data": null,
	"pagination": null
}

3.5. Order Management

Lấy danh sách đơn hàng

  • GET /orders?status=CONFIRMED|PACKED|SHIPPING|DELIVERED|CANCELLED
  • Response:
{
	"status": 200,
	"message": "Order list fetched successfully",
	"data": [
		{ "orderId": "o707", "status": "CONFIRMED" },
		{ "orderId": "o708", "status": "PACKED" },
		{ "orderId": "o709", "status": "SHIPPING" },
		{ "orderId": "o710", "status": "DELIVERED" },
		{ "orderId": "o711", "status": "CANCELLED" }
	],
	"pagination": {
		"total": 50,
		"page": 2,
		"limit": 5,
		"canNext": true,
		"canPrev": true
	}
}

Lấy chi tiết đơn hàng

  • GET /orders/{orderId}
  • Response:
{
	"status": 200,
	"message": "Order info fetched successfully",
	"data": {
		"orderId": "o707",
		"status": "shipping",
		"total": 310000,
		"finalTotal": 260000,
		"paymentMethod": "ewallet",
		"voucherCode": "SUMMER2025",
		"address": "123 Đường ABC",
		"shops": [
			{
				"shopId": "s456",
				"shopName": "Shop A",
				"products": [
					{
						"productId": "p101",
						"productName": "Áo thun trắng",
						"variantId": "v404",
						"price": 155000,
						"status": "packed",
						"quantity": 2
					}
				],
				"shopStatus": "packed"
			},
			{
				"shopId": "s457",
				"shopName": "Shop B",
				"products": [
					{
						"productId": "p102",
						"productName": "Áo thun trắng",
						"variantId": "v405",
						"price": 155000,
						"status": "packed",
						"quantity": 1
					}
				],
				"shopStatus": "shipping"
			}
		],
		"createdAt": "2024-01-01"
	},
	"pagination": null
}
  • Error:
{
	"status": 404,
	"message": "Order not found",
	"data": null,
	"pagination": null
}

Cập nhật trạng thái đơn hàng (mock)

  • POST /orders/{orderId}/mock-status
  • Body:
{ "status": "packed" }
  • Response:
{
	"status": 200,
	"message": "Order status updated",
	"data": {
		"orderId": "o707",
		"status": "packed"
	},
	"pagination": null
}

Xác nhận đã nhận hàng

  • POST /orders/{orderId}/confirm
  • Response:
{
	"status": 200,
	"message": "Order confirmed as delivered",
	"data": {
		"orderId": "o707",
		"status": "delivered"
	},
	"pagination": null
}

3.6. Voucher & Promotion

Tạo mã giảm giá (shop/super admin)

  • POST /vouchers
  • Body:
{
	"title": "Giảm giá mùa hè",
	"type": "SHIPPING|PRICE|PERCENTAGE",
	"conditions": {
		"minAmount": 300000,
		"paymentMethods": ["CASH", "EWALLET", "BANK_CARD", "TRANSFER_TO_PLATFORM"]
	},
	"discount": 50000,
	"shopId": "s456"
}
  • Response:
{
	"status": 201,
	"message": "Voucher created successfully",
	"data": {
		"voucherId": "v909",
		"code": "SUMMER2025",
		"title": "Giảm giá mùa hè",
		"type": "SHIPPING|PRICE|PERCENTAGE",
		"conditions": {
			"minAmount": 300000,
			"paymentMethods": ["CASH", "EWALLET", "BANK_CARD", "TRANSFER_TO_PLATFORM"]
		},
		"discount": 50000,
		"shopId": "s456" | ["s456", "s457"] | null // null as global voucher
	},
	"pagination": null
}
  • Error:
{
	"status": 400,
	"message": "Voucher code already exists",
	"data": null,
	"pagination": null
}

Lấy danh sách voucher

  • GET /vouchers?shopId=...
  • Response:
{
	"status": 200,
	"message": "Voucher list fetched successfully",
	"data": [
		{ "voucherId": "v909", "code": "SUMMER2025", "discount": 50000 },
		{ "voucherId": "v910", "code": "SUMMER2026", "discount": 50000 }
	],
	"pagination": {
		"total": 2,
		"page": 1,
		"limit": 5,
		"canNext": true,
		"canPrev": false
	}
}

3.7. Address & Profile

Thêm địa chỉ

  • POST /users/{userId}/addresses
  • Body:
{ "address": "456 Đường XYZ", "tag": "Công ty" }
  • Response:
{
	"status": 201,
	"message": "Address created successfully",
	"data": {
		"addressId": "a707",
		"address": "456 Đường XYZ",
		"tag": "Công ty"
	},
	"pagination": null
}

Sửa địa chỉ

  • PUT /users/{userId}/addresses/{addressId}
  • Body:
{ "address": "789 Đường ABC", "tag": "Nhà riêng" }
  • Response:
{
	"status": 200,
	"message": "Address updated successfully",
	"data": {
		"addressId": "a707",
		"address": "789 Đường ABC",
		"tag": "Nhà riêng"
	},
	"pagination": null
}

Đặt mặc định

  • POST /users/{userId}/addresses/{addressId}/default
  • Response:
{
	"status": 200,
	"message": "Address set as default",
	"data": null,
	"pagination": null
}

Lấy profile

  • GET /users/{userId}
  • Response:
{
	"status": 200,
	"message": "Profile fetched successfully",
	"data": {
		"userId": "u123",
		"name": "Nguyen Van A",
		"email": "[email protected]",
		"avatar": "https://example.com/avatar.jpg",
		"age": 25,
		"walletLinks": ["momo", "zalopay"],
		"cardLinks": ["visa", "mastercard"],
		"addresses": [{ "id": "a606", "address": "123 Đường ABC", "tag": "Nhà" }]
	},
	"pagination": null
}

Cập nhật profile

  • PUT /users/{userId}
  • Body:
{
	"name": "Nguyen Van B",
	"avatar": "https://example.com/avatar2.jpg",
	"age": 26
}
  • Response:
{
	"status": 200,
	"message": "Profile updated successfully",
	"data": {
		"userId": "u123",
		"name": "Nguyen Van B",
		"avatar": "https://example.com/avatar2.jpg",
		"age": 26
	},
	"pagination": null
}

Đổi mật khẩu/gửi lại mail

  • POST /auth/reset-password
  • Body:
{ "email": "[email protected]" }
  • Response:
{
	"status": 200,
	"message": "Password reset email sent",
	"data": null,
	"pagination": null
}
  • Error:
{
	"status": 400,
	"message": "Email not found",
	"data": null,
	"pagination": null
}

Cập nhật mật khẩu

  • PUT /auth/update-password
  • Body:
{ "token": "abc123xyz", "newPassword": "NewP@ssw0rd" }
  • Response:
{
	"status": 200,
	"message": "Password updated successfully",
	"data": null,
	"pagination": null
}
  • Error:
{
	"status": 400,
	"message": "Invalid or expired token",
	"data": null,
	"pagination": null
}

4. Database

DBML (Database Markup Language)

// Enums
enum UserRole {
  USER
  SHOP_OWNER
  ADMIN
}

enum StatusType {
  PENDING
  APPROVED
  REJECTED
}

enum OrderStatus {
  CONFIRMED
  PACKED
  SHIPPING
  DELIVERED
  CANCELLED
}

enum PaymentStatus {
  PENDING
  COMPLETED
  FAILED
}

enum PaymentMethod {
  CASH
  EWALLET
  BANK_CARD
  TRANSFER_TO_PLATFORM
}

enum VoucherType {
  SHIPPING
  PRICE
  PERCENTAGE
}

enum UserBehaviorAction {
  VIEW
  CLICK
  ADD_TO_CART
  PURCHASE
}

// Users & Authentication
Table users {
  id String [pk]
  email String [unique]
  password String
  name String
  avatar String
  age Int
  role UserRole [default: 'USER']
  verified Boolean [default: false]
  createdAt DateTime [default: `now()`]
  updatedAt DateTime [updatedAt]
}

Table addresses {
  id String [pk]
  userId String [ref: > users.id]
  address String
  tag String
  isDefault Boolean [default: false]
  createdAt DateTime [default: `now()`]
  updatedAt DateTime [updatedAt]
}

// Shop Management
Table industries {
  id String [pk]
  title String
  status StatusType [default: 'PENDING']
  createdAt DateTime [default: `now()`]
  updatedAt DateTime [updatedAt]
}

Table shops {
  id String [pk]
  name String
  description String
  address String
  verified Boolean [default: false]
  userId String [ref: > users.id]
  createdAt DateTime [default: `now()`]
  updatedAt DateTime [updatedAt]
}

Table shop_industries {
  shopId String [ref: > shops.id]
  industryId String [ref: > industries.id]

  indexes {
    (shopId, industryId) [pk]
  }
}

Table verification_requests {
  id String [pk]
  shopId String [ref: > shops.id]
  registrationNumber String
  businessAddress String
  productDescription String
  operatingHours String
  status StatusType [default: 'PENDING']
  createdAt DateTime [default: `now()`]
  updatedAt DateTime [updatedAt]
}

// Product Management
Table categories {
  id String [pk]
  title String
  description String
  shopId String [ref: > shops.id]
  createdAt DateTime [default: `now()`]
  updatedAt DateTime [updatedAt]
}

Table tags {
  id String [pk]
  title String
  description String
  industry String
  createdAt DateTime [default: `now()`]
  updatedAt DateTime [updatedAt]
}

Table products {
  id String [pk]
  name String
  description String
  price Decimal
  shopId String [ref: > shops.id]
  categoryId String [ref: > categories.id]
  createdAt DateTime [default: `now()`]
  updatedAt DateTime [updatedAt]
}

Table product_images {
  id String [pk]
  productId String [ref: > products.id]
  url String
  createdAt DateTime [default: `now()`]
}

Table product_attributes {
  id String [pk]
  productId String [ref: > products.id]
  title String
  description String
  createdAt DateTime [default: `now()`]
}

Table product_tags {
  productId String [ref: > products.id]
  tagId String [ref: > tags.id]

  indexes {
    (productId, tagId) [pk]
  }
}

Table variants {
  id String [pk]
  productId String [ref: > products.id]
  sku String [unique]
  price Decimal
  stock Int
  createdAt DateTime [default: `now()`]
  updatedAt DateTime [updatedAt]
}

Table variant_attributes {
  id String [pk]
  variantId String [ref: > variants.id]
  key String
  value String

  indexes {
    (variantId, key) [unique]
  }
}

Table variant_images {
  id String [pk]
  variantId String [ref: > variants.id]
  url String
  createdAt DateTime [default: `now()`]
}

// Reviews & Comments
Table reviews {
  id String [pk]
  productId String [ref: > products.id]
  userId String [ref: > users.id]
  rating Int
  comment String
  createdAt DateTime [default: `now()`]
  updatedAt DateTime [updatedAt]
}

Table review_images {
  id String [pk]
  reviewId String [ref: > reviews.id]
  url String
  createdAt DateTime [default: `now()`]
}

Table comments {
  id String [pk]
  productId String [ref: > products.id]
  userId String [ref: > users.id]
  comment String
  createdAt DateTime [default: `now()`]
  updatedAt DateTime [updatedAt]
}

Table comment_images {
  id String [pk]
  commentId String [ref: > comments.id]
  url String
  createdAt DateTime [default: `now()`]
}

// Cart & Checkout
Table carts {
  id String [pk]
  userId String [ref: > users.id]
  createdAt DateTime [default: `now()`]
  updatedAt DateTime [updatedAt]
}

Table cart_items {
  id String [pk]
  cartId String [ref: > carts.id]
  productId String [ref: > products.id]
  variantId String [ref: > variants.id]
  quantity Int
  createdAt DateTime [default: `now()`]
  updatedAt DateTime [updatedAt]
}

Table cart_vouchers {
  cartId String [ref: > carts.id]
  voucherId String [ref: > vouchers.id]

  indexes {
    (cartId, voucherId) [pk]
  }
}

// Orders
Table orders {
  id String [pk]
  userId String [ref: > users.id]
  addressId String [ref: > addresses.id]
  status OrderStatus [default: 'CONFIRMED']
  total Decimal
  finalTotal Decimal
  paymentMethod PaymentMethod
  voucherCode String
  createdAt DateTime [default: `now()`]
  updatedAt DateTime [updatedAt]
}

Table order_items {
  id String [pk]
  orderId String [ref: > orders.id]
  productId String [ref: > products.id]
  variantId String [ref: > variants.id]
  shopId String [ref: > shops.id]
  quantity Int
  price Decimal
  status OrderStatus [default: 'CONFIRMED']
  createdAt DateTime [default: `now()`]
  updatedAt DateTime [updatedAt]
}

Table payment_sessions {
  id String [pk]
  orderId String [ref: > orders.id]
  status PaymentStatus [default: 'PENDING']
  method PaymentMethod
  amount Decimal
  createdAt DateTime [default: `now()`]
  updatedAt DateTime [updatedAt]
}

// Vouchers & Promotions
Table vouchers {
  id String [pk]
  code String [unique]
  title String
  type VoucherType
  discount Decimal
  minAmount Decimal
  createdAt DateTime [default: `now()`]
  updatedAt DateTime [updatedAt]
  expiresAt DateTime
}

Table voucher_payment_methods {
  voucherId String [ref: > vouchers.id]
  paymentMethod PaymentMethod

  indexes {
    (voucherId, paymentMethod) [pk]
  }
}

Table shop_vouchers {
  voucherId String [ref: > vouchers.id]
  shopId String [ref: > shops.id]

  indexes {
    (voucherId, shopId) [pk]
  }
}

// User wallet and payment info
Table user_wallets {
  id String [pk]
  userId String [ref: > users.id]
  provider String // momo, zalopay
  linked Boolean [default: false]
  createdAt DateTime [default: `now()`]
  updatedAt DateTime [updatedAt]
}

Table user_cards {
  id String [pk]
  userId String [ref: > users.id]
  type String // visa, mastercard
  last4 String
  createdAt DateTime [default: `now()`]
  updatedAt DateTime [updatedAt]
}

// Refresh tokens
Table refresh_tokens {
  id String [pk]
  userId String [ref: > users.id]
  token String [unique]
  expiresAt DateTime
  createdAt DateTime [default: `now()`]
}

Table user_behaviors {
  id String [pk]
  userId String [ref: > users.id]
  productId String [ref: > products.id]
  action UserBehaviorAction
  createdAt DateTime [default: `now()`]
}

Prisma schema

// This is your Prisma schema file

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

// Enums
enum UserRole {
  USER
  SHOP_OWNER
  ADMIN
}

enum StatusType {
  PENDING
  APPROVED
  REJECTED
}

enum OrderStatus {
  CONFIRMED
  PACKED
  SHIPPING
  DELIVERED
  CANCELLED
}

enum PaymentStatus {
  PENDING
  COMPLETED
  FAILED
}

enum PaymentMethod {
  CASH
  EWALLET
  BANK_CARD
  TRANSFER_TO_PLATFORM
}

enum VoucherType {
  SHIPPING
  PRICE
  PERCENTAGE
}

enum UserBehaviorAction {
  VIEW
  CLICK
  ADD_TO_CART
  PURCHASE
}

// Users & Authentication
model User {
  id             String          @id @default(uuid())
  email          String          @unique
  password       String
  name           String
  avatar         String?
  age            Int?
  role           UserRole        @default(USER)
  verified       Boolean         @default(false)
  createdAt      DateTime        @default(now())
  updatedAt      DateTime        @updatedAt
  addresses      Address[]
  shops          Shop[]
  reviews        Review[]
  comments       Comment[]
  carts          Cart[]
  orders         Order[]
  wallets        UserWallet[]
  cards          UserCard[]
  refreshTokens  RefreshToken[]
}

model Address {
  id         String   @id @default(uuid())
  userId     String
  address    String
  tag        String
  isDefault  Boolean  @default(false)
  createdAt  DateTime @default(now())
  updatedAt  DateTime @updatedAt

  user       User     @relation(fields: [userId], references: [id])
  orders     Order[]
}

// Shop Management
model Industry {
  id          String          @id @default(uuid())
  title       String
  status      StatusType      @default(PENDING)
  createdAt   DateTime        @default(now())
  updatedAt   DateTime        @updatedAt
  shops       ShopIndustry[]
}

model Shop {
  id                 String               @id @default(uuid())
  name               String
  description        String
  address            String
  verified           Boolean              @default(false)
  userId             String
  createdAt          DateTime             @default(now())
  updatedAt          DateTime             @updatedAt

  user               User                 @relation(fields: [userId], references: [id])
  industries         ShopIndustry[]
  categories         Category[]
  products           Product[]
  verificationRequests VerificationRequest[]
  vouchers           ShopVoucher[]
  orderItems         OrderItem[]
}

model ShopIndustry {
  shopId      String
  industryId  String

  shop        Shop      @relation(fields: [shopId], references: [id])
  industry    Industry  @relation(fields: [industryId], references: [id])

  @@id([shopId, industryId])
}

model VerificationRequest {
  id                  String     @id @default(uuid())
  shopId              String
  registrationNumber  String
  businessAddress     String
  productDescription  String
  operatingHours      String
  status              StatusType @default(PENDING)
  createdAt           DateTime   @default(now())
  updatedAt           DateTime   @updatedAt

  shop                Shop       @relation(fields: [shopId], references: [id])
}

// Product Management
model Category {
  id          String    @id @default(uuid())
  title       String
  description String
  shopId      String
  createdAt   DateTime  @default(now())
  updatedAt   DateTime  @updatedAt

  shop        Shop      @relation(fields: [shopId], references: [id])
  products    Product[]
}

model Tag {
  id          String       @id @default(uuid())
  title       String
  description String
  industry    String
  createdAt   DateTime     @default(now())
  updatedAt   DateTime     @updatedAt

  products    ProductTag[]
}

model Product {
  id            String           @id @default(uuid())
  name          String
  description   String
  price         Decimal
  shopId        String
  categoryId    String
  createdAt     DateTime         @default(now())
  updatedAt     DateTime         @updatedAt

  shop          Shop             @relation(fields: [shopId], references: [id])
  category      Category         @relation(fields: [categoryId], references: [id])
  images        ProductImage[]
  attributes    ProductAttribute[]
  tags          ProductTag[]
  variants      Variant[]
  reviews       Review[]
  comments      Comment[]
  cartItems     CartItem[]
  orderItems    OrderItem[]
}

model ProductImage {
  id         String   @id @default(uuid())
  productId  String
  url        String
  createdAt  DateTime @default(now())

  product    Product  @relation(fields: [productId], references: [id])
}

model ProductAttribute {
  id           String   @id @default(uuid())
  productId    String
  title        String
  description  String
  createdAt    DateTime @default(now())

  product      Product  @relation(fields: [productId], references: [id])
}

model ProductTag {
  productId  String
  tagId      String

  product    Product   @relation(fields: [productId], references: [id])
  tag        Tag       @relation(fields: [tagId], references: [id])

  @@id([productId, tagId])
}

model Variant {
  id           String             @id @default(uuid())
  productId    String
  sku          String             @unique
  price        Decimal
  stock        Int
  createdAt    DateTime           @default(now())
  updatedAt    DateTime           @updatedAt

  product      Product            @relation(fields: [productId], references: [id])
  attributes   VariantAttribute[]
  images       VariantImage[]
  cartItems    CartItem[]
  orderItems   OrderItem[]
}

model VariantAttribute {
  id         String   @id @default(uuid())
  variantId  String
  key        String
  value      String

  variant    Variant  @relation(fields: [variantId], references: [id])

  @@unique([variantId, key])
}

model VariantImage {
  id         String   @id @default(uuid())
  variantId  String
  url        String
  createdAt  DateTime @default(now())

  variant    Variant  @relation(fields: [variantId], references: [id])
}

// Reviews & Comments
model Review {
  id         String        @id @default(uuid())
  productId  String
  userId     String
  rating     Int
  comment    String
  createdAt  DateTime      @default(now())
  updatedAt  DateTime      @updatedAt

  product    Product       @relation(fields: [productId], references: [id])
  user       User          @relation(fields: [userId], references: [id])
  images     ReviewImage[]
}

model ReviewImage {
  id         String   @id @default(uuid())
  reviewId   String
  url        String
  createdAt  DateTime @default(now())

  review     Review   @relation(fields: [reviewId], references: [id])
}

model Comment {
  id         String         @id @default(uuid())
  productId  String
  userId     String
  comment    String
  createdAt  DateTime       @default(now())
  updatedAt  DateTime       @updatedAt

  product    Product        @relation(fields: [productId], references: [id])
  user       User           @relation(fields: [userId], references: [id])
  images     CommentImage[]
}

model CommentImage {
  id         String   @id @default(uuid())
  commentId  String
  url        String
  createdAt  DateTime @default(now())

  comment    Comment  @relation(fields: [commentId], references: [id])
}

// Cart & Checkout
model Cart {
  id         String       @id @default(uuid())
  userId     String
  createdAt  DateTime     @default(now())
  updatedAt  DateTime     @updatedAt

  user       User         @relation(fields: [userId], references: [id])
  items      CartItem[]
  vouchers   CartVoucher[]
}

model CartItem {
  id         String   @id @default(uuid())
  cartId     String
  productId  String
  variantId  String
  quantity   Int
  createdAt  DateTime @default(now())
  updatedAt  DateTime @updatedAt

  cart       Cart     @relation(fields: [cartId], references: [id])
  product    Product  @relation(fields: [productId], references: [id])
  variant    Variant  @relation(fields: [variantId], references: [id])
}

model CartVoucher {
  cartId     String
  voucherId  String

  cart       Cart     @relation(fields: [cartId], references: [id])
  voucher    Voucher  @relation(fields: [voucherId], references: [id])

  @@id([cartId, voucherId])
}

// Orders
model Order {
  id            String           @id @default(uuid())
  userId        String
  addressId     String
  status        OrderStatus      @default(CONFIRMED)
  total         Decimal
  finalTotal    Decimal
  paymentMethod PaymentMethod
  voucherCode   String?
  createdAt     DateTime         @default(now())
  updatedAt     DateTime         @updatedAt

  user          User             @relation(fields: [userId], references: [id])
  address       Address          @relation(fields: [addressId], references: [id])
  items         OrderItem[]
  paymentSessions PaymentSession[]
}

model OrderItem {
  id         String      @id @default(uuid())
  orderId    String
  productId  String
  variantId  String
  shopId     String
  quantity   Int
  price      Decimal
  status     OrderStatus @default(CONFIRMED)
  createdAt  DateTime    @default(now())
  updatedAt  DateTime    @updatedAt

  order      Order       @relation(fields: [orderId], references: [id])
  product    Product     @relation(fields: [productId], references: [id])
  variant    Variant     @relation(fields: [variantId], references: [id])
  shop       Shop        @relation(fields: [shopId], references: [id])
}

model PaymentSession {
  id         String        @id @default(uuid())
  orderId    String
  status     PaymentStatus @default(PENDING)
  method     PaymentMethod
  amount     Decimal
  createdAt  DateTime      @default(now())
  updatedAt  DateTime      @updatedAt

  order      Order         @relation(fields: [orderId], references: [id])
}

// Vouchers & Promotions
model Voucher {
  id            String                  @id @default(uuid())
  code          String                  @unique
  title         String
  type          VoucherType
  discount      Decimal
  minAmount     Decimal
  createdAt     DateTime                @default(now())
  updatedAt     DateTime                @updatedAt
  expiresAt     DateTime

  paymentMethods VoucherPaymentMethod[]
  shops         ShopVoucher[]
  carts         CartVoucher[]
}

model VoucherPaymentMethod {
  voucherId     String
  paymentMethod PaymentMethod

  voucher       Voucher      @relation(fields: [voucherId], references: [id])

  @@id([voucherId, paymentMethod])
}

model ShopVoucher {
  voucherId  String
  shopId     String

  voucher    Voucher   @relation(fields: [voucherId], references: [id])
  shop       Shop      @relation(fields: [shopId], references: [id])

  @@id([voucherId, shopId])
}

// User wallet and payment info
model UserWallet {
  id         String         @id @default(uuid())
  userId     String
  provider   WalletProvider
  linked     Boolean        @default(false)
  createdAt  DateTime       @default(now())
  updatedAt  DateTime       @updatedAt

  user       User           @relation(fields: [userId], references: [id])
}

model UserCard {
  id         String    @id @default(uuid())
  userId     String
  type       CardType
  last4      String
  createdAt  DateTime  @default(now())
  updatedAt  DateTime  @updatedAt

  user       User      @relation(fields: [userId], references: [id])
}

// Refresh tokens
model RefreshToken {
  id         String   @id @default(uuid())
  userId     String
  token      String   @unique
  expiresAt  DateTime
  createdAt  DateTime @default(now())

  user       User     @relation(fields: [userId], references: [id])
}

model UserBehavior {
  id         String             @id @default(uuid())
  userId     String
  productId  String
  action     UserBehaviorAction
  createdAt  DateTime           @default(now())

  user       User               @relation(fields: [userId], references: [id])
  product    Product            @relation(fields: [productId], references: [id])
}

5. Luồng nghiệp vụ chính (Business Flow)

1. Đăng ký, xác thực, đăng nhập tài khoản

  • User đăng ký tài khoản với email, password, tên.
  • Gửi email xác thực: User nhận link xác thực qua email, click để xác thực tài khoản.
  • Đăng nhập: Sau khi xác thực, user đăng nhập lấy access token, refresh token.

2. Quản lý Shop

  • Tạo shop: User tạo shop với tên, mô tả, địa chỉ, chọn ngành hàng từ list có sẵn.
  • Chỉnh sửa shop: Sửa tên, mô tả, địa chỉ, cập nhật ngành hàng.
  • Xác minh shop: Gửi yêu cầu xác minh với các thông tin: số đăng ký kinh doanh, địa chỉ đăng ký, mô tả mặt hàng, thời gian làm việc. Nhân viên sàn sẽ kiểm chứng và cập nhật trạng thái verified.
  • Quản lý metadata shop: Shop có các thông tin: tổng số sản phẩm, số sản phẩm thực (trừ hết hàng), số người theo dõi, ngày tham gia, voucher riêng, danh mục tự tạo, trạng thái verified.

3. Quản lý Sản phẩm, Tag, Danh mục

  • Tạo sản phẩm: Shop owner tạo sản phẩm với tên, mô tả (markdown), giá, ảnh (list), thuộc tính (tự tạo), tag (gợi ý từ tag đã có), danh mục (folder).
  • Chỉnh sửa/xóa sản phẩm: Sửa/xóa thông tin sản phẩm.
  • Quản lý biến thể (variant): Tạo các loại sản phẩm con với thuộc tính riêng (tên, ảnh, giá, tồn kho, đã bán).
  • Gán sản phẩm vào danh mục: Gom nhóm sản phẩm theo danh mục tự tạo.
  • Quản lý tag: Tạo tag mới (title, desc, ngành hàng), gợi ý tag theo ngành hàng.
  • Sản phẩm liên quan: Gợi ý dựa trên thuộc tính, tag, thói quen user (tracking).

4. Quản lý Giỏ hàng & Voucher

  • Thêm vào giỏ hàng: User thêm sản phẩm (theo variant) vào giỏ, nhóm theo shop.
  • Xem giỏ hàng: Lấy thông tin sản phẩm trong giỏ, voucher áp dụng, tổng tiền, tồn kho, số người đã cho vào giỏ.
  • Áp dụng voucher: Áp dụng mã giảm giá riêng shop, mã giảm giá vận chuyển, mã giảm giá tiền.
  • Tạo voucher: Shop owner/super admin tạo voucher với tiêu đề, loại (vận chuyển, giá), điều kiện (minAmount, paymentMethods).

5. Thanh toán (Checkout)

  • Quản lý địa chỉ: Thêm, sửa, xóa, chọn địa chỉ nhận hàng, đặt mặc định, gán tag (nhà riêng, công sở,…).
  • Chọn phương thức thanh toán: Tiền mặt, ví điện tử, thẻ ngân hàng, chuyển khoản vào sàn.
  • Tạo đơn hàng: Gom nhóm đơn theo shop, tính tổng tiền, tổng tiền cuối cùng (sau voucher, thuộc tính).
  • Mở session thanh toán: Với các phương thức không phải COD, tạo sessionId (live 10 phút), frontend polling check status, có API mock đổi trạng thái session.

6. Quản lý Đơn hàng

  • Lấy danh sách đơn hàng: Gom nhóm theo trạng thái (đã xác nhận, đã đóng gói, đang vận chuyển, đã nhận hàng), đã hủy.
  • Chi tiết đơn hàng: Trạng thái đơn, sản phẩm trong đơn (gom nhóm theo shop), trạng thái từng sản phẩm, từng shop.
  • Cập nhật trạng thái đơn hàng: Có API mock đổi trạng thái, xác nhận đã nhận hàng (nếu không confirm sau 10 ngày, tự động xác nhận).

7. Đánh giá & Bình luận sản phẩm

  • Đánh giá sản phẩm: User đánh giá (số sao, mô tả, ảnh).
  • Bình luận sản phẩm: User comment (mô tả, ảnh).
  • Lấy danh sách đánh giá, comment: Hỗ trợ phân trang.

8. Quản lý Profile User

  • Thông tin cá nhân: Tên, tuổi, ảnh, email.
  • Liên kết ví điện tử, thẻ thanh toán: Thêm/xóa liên kết.
  • Quản lý địa chỉ nhận hàng: List, thêm, sửa, xóa, tag địa chỉ.
  • Đổi mật khẩu, cấp lại mật khẩu qua email: Gửi mail reset, cập nhật mật khẩu mới.

9. Quản trị Super Admin

  • Duyệt ngành hàng mới: Xem và duyệt các yêu cầu thêm ngành hàng.
  • Xác minh shop: Duyệt yêu cầu xác minh shop.
  • Tạo voucher toàn sàn: Tạo mã giảm giá áp dụng toàn hệ thống.

6. Ghi chú triển khai (Implementation Notes)

1. Chuẩn hóa API & Response

  • Tất cả API trả về dữ liệu đúng nghiệp vụ, gom nhóm theo shop khi cần.
  • Hỗ trợ phân trang, lọc, tìm kiếm cho các list (sản phẩm, đơn hàng, đánh giá, comment…).
  • Response mẫu: { status, message, data, pagination } hoặc { error } với status code phù hợp.

Với API có phân trang, response sẽ có thêm pagination và data là array:

{
	"status": 200,
	"message": "Success",
	"data": [
		{
			"id": "o707",
			"status": "CONFIRMED",
			"totalAmount": 100000
		}
	],
	"pagination": {
		"total": 10,
		"page": 1,
		"limit": 1,
		"canNext": true,
		"canPrev": true
	}
}

Với API không có phân trang, response sẽ có thêm data là object hoặc null:

{
	"status": 200,
	"message": "Success",
	"data": null,
	"pagination": null
}

2. Phân quyền & Bảo mật

  • Tách quyền rõ ràng: super admin, shop owner, user.
  • Các API cần xác thực: Yêu cầu Bearer Token, kiểm tra quyền truy cập.
  • Bảo mật thông tin cá nhân, token, dữ liệu nhạy cảm.

3. Email & Xác thực

  • Tích hợp email service: Gửi mail xác thực, reset password, thông báo trạng thái shop.
  • Link xác thực email: Có token, hết hạn sau thời gian nhất định.

4. Thanh toán & Session

  • Tích hợp payment gateway: Hỗ trợ nhiều phương thức thanh toán.
  • Session thanh toán: Tạo sessionId, timeout 10 phút, API polling check status, API mock đổi trạng thái cho test frontend.

5. Xử lý nghiệp vụ đặc thù

  • Voucher: Áp dụng đúng điều kiện (minAmount, paymentMethods, loại voucher), phân biệt voucher toàn sàn và voucher shop.
  • Đơn hàng: Gom nhóm theo shop, trạng thái từng sản phẩm, từng shop, từng đơn.
  • Sản phẩm liên quan: Gợi ý dựa trên thuộc tính, tag, tracking hành vi user.

6. Tài liệu hóa & Test

  • Tài liệu hóa rõ ràng từng endpoint: Request, response, status code, error, phân quyền.
  • Mock API: Hỗ trợ đổi trạng thái đơn hàng, session thanh toán để test frontend.
  • Test case: Đảm bảo các flow chính đều có test, bao gồm edge case (hết hàng, voucher không hợp lệ, session timeout…).

7. Khả năng mở rộng & maintain

  • Thiết kế schema, API dễ mở rộng: Thêm ngành hàng, phương thức thanh toán, loại voucher mới.
  • Tối ưu truy vấn, index cho các bảng lớn (sản phẩm, đơn hàng, user…).
  • Đảm bảo code clean, dễ maintain, dễ kiểm thử.
0%