Browse Source

no message

LamGiang 1 tháng trước cách đây
mục cha
commit
b31f81ea48

+ 26 - 0
EsimLao/Common/CommonLogic.cs

@@ -153,6 +153,32 @@ public class CommonLogic
         return password.ToString();
     }
 
+    /// <summary>
+    /// Get language from request header or body
+    /// Priority: header Accept-Language > body lang > default "lo"
+    /// </summary>
+    /// <param name="httpRequest">HTTP request</param>
+    /// <param name="bodyLang">Language from request body (optional)</param>
+    /// <returns>Language code: "lo" or "en"</returns>
+    public static string GetLanguage(HttpRequest httpRequest, string? bodyLang = null)
+    {
+        // Check header first
+        var headerLang = httpRequest.Headers["Accept-Language"].FirstOrDefault();
+        if (!string.IsNullOrEmpty(headerLang))
+        {
+            // Accept-Language can be "en", "lo", "en-US,en;q=0.9", etc.
+            var lang = headerLang.Split(',')[0].Split('-')[0].ToLower();
+            if (lang == "en" || lang == "lo")
+                return lang;
+        }
+
+        // Then check body
+        if (!string.IsNullOrEmpty(bodyLang))
+            return bodyLang.ToLower();
+
+        return "lo"; // Default
+    }
+
     public static string ConvertIntStatus(int? status)
     {
         switch (status)

+ 10 - 6
EsimLao/Common/Constant/CommonConstant.cs

@@ -160,12 +160,16 @@ public static class ApiUrlConstant
     public const String RequestOtpUrl = "/apis/auth/request-otp";
     public const String VerifyOtpUrl = "/apis/auth/verify-otp";
 
-    public const String ArticleCategoryUrl = "/apis/auth/article-category";
-    public const String ArticleLoadUrl = "/apis/auth/article-load";
-
-
-    // ---- KOKAK
-    // public const String MpsApiUrl = "http://127.0.0.1:8866/apiportal";
+    // Article URLs
+    public const String ArticleCategoryUrl = "/apis/article/category";
+    public const String ArticleLoadUrl = "/apis/article/load";
+
+    // Content URLs
+    public const String BannerLoadUrl = "/apis/content/banner";
+    public const String CustomerReviewLoadUrl = "/apis/content/review";
+    public const String CustomerReviewCreateUrl = "/apis/content/review/create";
+    public const String FaqCategoryLoadUrl = "/apis/content/faq-category";
+    public const String FaqLoadUrl = "/apis/content/faq";
 }
 
 public static class CommonErrorCode

+ 3 - 4
EsimLao/Esim.Apis/Business/User/UserBusinessImpl.cs

@@ -118,7 +118,7 @@ namespace Esim.Apis.Business
 
                 // Add to MESSAGE_QUEUE for background email sending
                 // Resolve template content now so Worker only needs to send email
-                string lang = (request.lang ?? "lo").ToLower();
+                string lang = CommonLogic.GetLanguage(httpRequest, request.lang);
                 string templateCode = "OTP_LOGIN";
 
                 // Query template and get language-specific content
@@ -210,7 +210,7 @@ namespace Esim.Apis.Business
             {
                 if (string.IsNullOrEmpty(request.email) || string.IsNullOrEmpty(request.otpCode))
                 {
-                    string lang = (request.lang ?? "lo").ToLower();
+                    string lang = CommonLogic.GetLanguage(httpRequest, request.lang);
                     return DotnetLib.Http.HttpResponse.BuildResponse(
                         log,
                         url,
@@ -222,7 +222,7 @@ namespace Esim.Apis.Business
                 }
 
                 // Get language for response messages
-                string responseLang = (request.lang ?? "lo").ToLower();
+                string responseLang = CommonLogic.GetLanguage(httpRequest, request.lang);
 
                 // Find valid OTP
                 var otpRecord = dbContext.OtpVerifications
@@ -400,6 +400,5 @@ namespace Esim.Apis.Business
             return ipAddress ?? "Unknown";
         }
 
-        #endregion
     }
 }

+ 397 - 17
EsimLao/docs/api_auth_otp.txt

@@ -36,7 +36,7 @@ POST /apis/auth/request-otp
 ### Response Success (200)
 ```json
 {
-    "code": 0,
+    "errorCode": 0,
     "message": "<Config: OTP_SENT_SUCCESS>",
     "data": {
         "email": "user@example.com",
@@ -49,14 +49,14 @@ POST /apis/auth/request-otp
 ```json
 // Email không được cung cấp
 {
-    "code": -801,
+    "errorCode": -801,
     "message": "<Config: EMAIL_REQUIRED>",
     "data": {}
 }
 
 // Lỗi hệ thống
 {
-    "code": -6,
+    "errorCode": -6,
     "message": "<Config: SYSTEM_FAILURE>",
     "data": {}
 }
@@ -65,7 +65,7 @@ POST /apis/auth/request-otp
 ### Response Fields
 | Field | Type | Description |
 |-------|------|-------------|
-| code | int | 0 = Success, khác 0 = Error (xem Error Codes) |
+| errorCode| int | 0 = Success, khác 0 = Error (xem Error Codes) |
 | message | string | Thông báo từ CONFIG table (theo ngôn ngữ) |
 | data.email | string | Email đã gửi OTP |
 | data.expireInSeconds | int | Thời gian OTP hết hạn (giây) |
@@ -111,7 +111,7 @@ POST /apis/auth/verify-otp
 ### Response Success (200)
 ```json
 {
-    "code": 0,
+    "errorCode": 0,
     "message": "<Config: LOGIN_SUCCESS>",
     "data": {
         "userId": 12345,
@@ -129,42 +129,42 @@ POST /apis/auth/verify-otp
 ```json
 // Thiếu email hoặc OTP
 {
-    "code": -801,
+    "errorCode": -801,
     "message": "<Config: EMAIL_OTP_REQUIRED>",
     "data": {}
 }
 
 // OTP không hợp lệ
 {
-    "code": -201,
+    "errorCode": -201,
     "message": "<Config: OTP_INVALID>",
     "data": {}
 }
 
 // OTP đã được sử dụng
 {
-    "code": -203,
+    "errorCode": -203,
     "message": "<Config: OTP_ALREADY_USED>",
     "data": {}
 }
 
 // OTP đã hết hạn
 {
-    "code": -202,
+    "errorCode": -202,
     "message": "<Config: OTP_EXPIRED>",
     "data": {}
 }
 
 // Không tìm thấy người dùng
 {
-    "code": -300,
+    "errorCode": -300,
     "message": "<Config: USER_NOT_FOUND>",
     "data": {}
 }
 
 // Lỗi hệ thống
 {
-    "code": -6,
+    "errorCode": -6,
     "message": "<Config: SYSTEM_FAILURE>",
     "data": {}
 }
@@ -173,7 +173,7 @@ POST /apis/auth/verify-otp
 ### Response Fields
 | Field | Type | Description |
 |-------|------|-------------|
-| code | int | 0 = Success, khác 0 = Error (xem Error Codes) |
+| errorCode| int | 0 = Success, khác 0 = Error (xem Error Codes) |
 | message | string | Thông báo từ CONFIG table (theo ngôn ngữ) |
 | data.userId | int | ID người dùng |
 | data.email | string | Email người dùng |
@@ -193,18 +193,18 @@ POST /apis/auth/verify-otp
 ## Error Codes
 
 ### Success
-| Code | Constant | Description |
+| errorCode| Constant | Description |
 |------|----------|-------------|
 | 0 | Success | Thành công (mọi request thành công đều trả về 0) |
 
 ### General Errors (-1 to -99)
-| Code | Constant | Description |
+| errorCode| Constant | Description |
 |------|----------|-------------|
 | -1 | Error | Lỗi chung |
 | -6 | SystemError | Lỗi hệ thống |
 
 ### OTP Errors (-200 to -299)
-| Code | Constant | Description |
+| errorCode| Constant | Description |
 |------|----------|-------------|
 | -200 | OtpRequired | Yêu cầu OTP |
 | -201 | OtpInvalid | OTP không hợp lệ |
@@ -215,13 +215,13 @@ POST /apis/auth/verify-otp
 | -206 | OtpTooManyRequests | Request quá nhiều |
 
 ### User Errors (-300 to -399)
-| Code | Constant | Description |
+| errorCode| Constant | Description |
 |------|----------|-------------|
 | -300 | UserNotFound | Không tìm thấy người dùng |
 | -304 | InvalidEmail | Email không hợp lệ |
 
 ### Validation Errors (-800 to -899)
-| Code | Constant | Description |
+| errorCode| Constant | Description |
 |------|----------|-------------|
 | -801 | RequiredFieldMissing | Thiếu trường bắt buộc |
 
@@ -300,3 +300,383 @@ curl -X POST https://api.esimlao.com/apis/auth/verify-otp \
 curl -X GET https://api.esimlao.com/apis/user/profile \
   -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
 ```
+
+---
+
+## 3. Article Category
+
+Lấy danh sách danh mục bài viết.
+
+### Endpoint
+```
+POST /apis/auth/article-category-load
+```
+
+### Request
+```json
+{
+    "lang": "lo",
+    "pageNumber": 0,
+    "pageSize": 10,
+    "parentId": null
+}
+```
+
+| Field | Type | Default | Description |
+|-------|------|---------|-------------|
+| lang | string | "lo" | Ngôn ngữ: "lo", "en" (có thể truyền qua header Accept-Language) |
+| pageNumber | int | 0 | Trang hiện tại |
+| pageSize | int | 10 | Số item mỗi trang |
+| parentId | int? | null | ID danh mục cha (null = root) |
+
+### Response Success
+```json
+{
+    "errorCode": 0,
+    "message": "Success",
+    "data": {
+        "items": [
+            {
+                "id": 1,
+                "categoryName": "Cẩm nang du lịch",
+                "categorySlug": "cam-nang-du-lich",
+                "description": "Mô tả...",
+                "iconUrl": "/icons/travel.png",
+                "parentId": null,
+                "displayOrder": 1
+            }
+        ],
+        "pagination": {
+            "pageNumber": 0,
+            "pageSize": 10,
+            "totalCount": 5,
+            "totalPages": 1
+        }
+    }
+}
+```
+
+---
+
+## 4. Article Load
+
+Lấy danh sách bài viết hoặc chi tiết 1 bài viết.
+
+### Endpoint
+```
+POST /apis/auth/article-load
+```
+
+### Request (danh sách)
+```json
+{
+    "lang": "lo",
+    "pageNumber": 0,
+    "pageSize": 10,
+    "categoryId": 1,
+    "isFeatured": false
+}
+```
+
+### Request (chi tiết)
+```json
+{
+    "lang": "lo",
+    "slug": "huong-dan-cai-dat-esim"
+}
+```
+
+| Field | Type | Default | Description |
+|-------|------|---------|-------------|
+| lang | string | "lo" | Ngôn ngữ (hoặc header Accept-Language) |
+| pageNumber | int | 0 | Trang hiện tại |
+| pageSize | int | 10 | Số item mỗi trang |
+| categoryId | int? | null | Lọc theo danh mục |
+| isFeatured | bool? | null | Lọc bài viết nổi bật |
+| slug | string? | null | Slug để lấy chi tiết bài viết |
+
+### Response Success (danh sách)
+```json
+{
+    "errorCode": 0,
+    "message": "Success",
+    "data": {
+        "items": [
+            {
+                "id": 1,
+                "title": "Hướng dẫn cài đặt eSIM",
+                "slug": "huong-dan-cai-dat-esim",
+                "summary": "Tóm tắt...",
+                "thumbnailUrl": "/images/article1.jpg",
+                "categoryId": 1,
+                "viewCount": 150,
+                "isFeatured": true,
+                "isPinned": false,
+                "publishedDate": "2024-12-25"
+            }
+        ],
+        "pagination": {...}
+    }
+}
+```
+
+### Response Success (chi tiết)
+```json
+{
+    "errorCode": 0,
+    "message": "Success",
+    "data": {
+        "article": {
+            "id": 1,
+            "title": "Hướng dẫn cài đặt eSIM",
+            "slug": "huong-dan-cai-dat-esim",
+            "summary": "Tóm tắt...",
+            "content": "<p>Nội dung HTML...</p>",
+            "thumbnailUrl": "/images/article1.jpg",
+            "coverImageUrl": "/images/cover1.jpg",
+            "metaDescription": "SEO description",
+            "metaKeywords": "esim, laos",
+            "categoryId": 1,
+            "viewCount": 151,
+            "isFeatured": true,
+            "publishedDate": "2024-12-25",
+            "createdDate": "2024-12-20"
+        }
+    }
+}
+```
+
+---
+
+## 5. Banner Load
+
+Lấy danh sách banner.
+
+### Endpoint
+```
+POST /apis/content/banner
+```
+
+### Request
+```json
+{
+    "lang": "lo",
+    "pageNumber": 0,
+    "pageSize": 10,
+    "position": "home"
+}
+```
+
+| Field | Type | Default | Description |
+|-------|------|---------|-------------|
+| lang | string | "lo" | Ngôn ngữ (hoặc header Accept-Language) |
+| pageNumber | int | 0 | Trang hiện tại |
+| pageSize | int | 10 | Số item mỗi trang |
+| position | string? | null | Vị trí: "home", "sidebar"... |
+
+### Response
+```json
+{
+    "errorCode": 0,
+    "data": {
+        "items": [
+            {
+                "id": 1,
+                "title": "Banner Title",
+                "subtitle": "Subtitle",
+                "imageUrl": "/images/banner1.jpg",
+                "imageMobileUrl": "/images/banner1_m.jpg",
+                "linkUrl": "/promo",
+                "linkTarget": "_blank",
+                "position": "home",
+                "displayOrder": 1
+            }
+        ],
+        "pagination": {...}
+    }
+}
+```
+
+---
+
+## 6. Customer Review Load
+
+Lấy đánh giá của khách hàng.
+
+### Endpoint
+```
+POST /apis/content/review
+```
+
+### Request
+```json
+{
+    "lang": "lo",
+    "pageNumber": 0,
+    "pageSize": 10,
+    "isFeatured": true
+}
+```
+
+| Field | Type | Default | Description |
+|-------|------|---------|-------------|
+| lang | string | "lo" | Ngôn ngữ |
+| pageNumber | int | 0 | Trang |
+| pageSize | int | 10 | Số item |
+| isFeatured | bool? | null | Lọc review nổi bật |
+
+### Response
+```json
+{
+    "errorCode": 0,
+    "data": {
+        "items": [
+            {
+                "id": 1,
+                "customerName": "Nguyen Van A",
+                "avatarUrl": "/avatars/user1.jpg",
+                "rating": true,
+                "reviewContent": "Dich vu rat tot...",
+                "destination": "Vientiane, Laos",
+                "isFeatured": true,
+                "createdDate": "2024-12-25"
+            }
+        ],
+        "pagination": {...}
+    }
+}
+```
+
+---
+
+## 6.1 Customer Review Create
+
+Khách hàng gửi đánh giá (chờ duyệt).
+
+### Endpoint
+```
+POST /apis/content/review/create
+```
+
+### Request
+```json
+{
+    "lang": "lo",
+    "customerName": "Nguyen Van A",
+    "reviewContent": "Dich vu rat tot, toi rat hai long!",
+    "destination": "Vientiane, Laos",
+    "rating": 5
+}
+```
+
+| Field | Type | Required | Description |
+|-------|------|----------|-------------|
+| lang | string | No | Ngôn ngữ |
+| customerName | string | Yes | Tên khách hàng |
+| reviewContent | string | Yes | Nội dung đánh giá |
+| destination | string | No | Địa điểm |
+| rating | int | No | Đánh giá (1-5) |
+
+### Response Success
+```json
+{
+    "errorCode": 0,
+    "message": "Review submitted successfully",
+    "data": {
+        "reviewId": 123
+    }
+}
+```
+
+### Note
+- Review mới sẽ có `Status = false` (chờ admin duyệt)
+
+---
+
+## 7. FAQ Category Load
+
+Lấy danh mục FAQ.
+
+### Endpoint
+```
+POST /apis/content/faq-category
+```
+
+### Request
+```json
+{
+    "lang": "lo",
+    "pageNumber": 0,
+    "pageSize": 10
+}
+```
+
+### Response
+```json
+{
+    "errorCode": 0,
+    "data": {
+        "items": [
+            {
+                "id": 1,
+                "categoryName": "Cài đặt eSIM",
+                "categorySlug": "cai-dat-esim",
+                "description": "Hướng dẫn cài đặt",
+                "iconUrl": "/icons/setup.png",
+                "displayOrder": 1
+            }
+        ],
+        "pagination": {...}
+    }
+}
+```
+
+---
+
+## 8. FAQ Load
+
+Lấy danh sách câu hỏi thường gặp.
+
+### Endpoint
+```
+POST /apis/content/faq
+```
+
+### Request
+```json
+{
+    "lang": "lo",
+    "pageNumber": 0,
+    "pageSize": 10,
+    "categoryId": 1,
+    "isFeatured": false
+}
+```
+
+| Field | Type | Default | Description |
+|-------|------|---------|-------------|
+| lang | string | "lo" | Ngôn ngữ |
+| pageNumber | int | 0 | Trang |
+| pageSize | int | 10 | Số item |
+| categoryId | int? | null | Lọc theo danh mục |
+| isFeatured | bool? | null | Lọc FAQ nổi bật |
+
+### Response
+```json
+{
+    "errorCode": 0,
+    "data": {
+        "items": [
+            {
+                "id": 1,
+                "question": "Làm sao để cài đặt eSIM?",
+                "answer": "<p>Hướng dẫn chi tiết...</p>",
+                "categoryId": 1,
+                "viewCount": 100,
+                "isFeatured": true
+            }
+        ],
+        "pagination": {...}
+    }
+}
+```