api_auth_otp.txt 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778
  1. # EsimLao Authentication API Documentation
  2. ## Overview
  3. API xác thực người dùng qua email với OTP (One-Time Password).
  4. URL_UAT : http://149.28.132.56:8360/
  5. ---
  6. ## 1. Request OTP
  7. Gửi mã OTP đến email người dùng để xác thực đăng nhập.
  8. ### Endpoint
  9. ```
  10. POST /apis/auth/request-otp
  11. ```
  12. ### Request Headers
  13. | Header | Value | Required |
  14. |--------|-------|----------|
  15. | Content-Type | application/json | Yes |
  16. ### Request Body
  17. ```json
  18. {
  19. "email": "user@example.com",
  20. "lang": "lo" // Default: "lo" / en
  21. }
  22. ```
  23. ### Parameters
  24. | Field | Type | Required | Default | Description |
  25. |-------|------|----------|---------|-------------|
  26. | email | string | Yes | - | Email address của người dùng |
  27. | lang | string | No | "vi" | Ngôn ngữ email: `vi` (Tiếng Việt), `en` (English), `lo` (ລາວ) |
  28. ### Response Success (200)
  29. ```json
  30. {
  31. "errorCode": "0",
  32. "message": "<Config: OTP_SENT_SUCCESS>",
  33. "data": {
  34. "email": "user@example.com",
  35. "expireInSeconds": 300
  36. }
  37. }
  38. ```
  39. ### Response Error (200)
  40. ```json
  41. // Email không được cung cấp
  42. {
  43. "errorCode": "-801",
  44. "message": "<Config: EMAIL_REQUIRED>",
  45. "data": {}
  46. }
  47. // Lỗi hệ thống
  48. {
  49. "errorCode": "-6",
  50. "message": "<Config: SYSTEM_FAILURE>",
  51. "data": {}
  52. }
  53. ```
  54. ### Response Fields
  55. | Field | Type | Description |
  56. |-------|------|-------------|
  57. | errorCode| string | "0" = Success, khác "0" = Error (xem Error Codes) |
  58. | message | string | Thông báo từ CONFIG table (theo ngôn ngữ) |
  59. | data.email | string | Email đã gửi OTP |
  60. | data.expireInSeconds | int | Thời gian OTP hết hạn (giây) |
  61. ### Notes
  62. - OTP gồm 6 chữ số
  63. - OTP có hiệu lực trong 5 phút
  64. - Mỗi lần request mới sẽ hủy các OTP cũ chưa sử dụng
  65. - Nếu email chưa tồn tại, hệ thống tự động tạo tài khoản mới
  66. ---
  67. ## 2. Verify OTP
  68. Xác thực mã OTP và hoàn tất đăng nhập.
  69. ### Endpoint
  70. ```
  71. POST /apis/auth/verify-otp
  72. ```
  73. ### Request Headers
  74. | Header | Value | Required |
  75. |--------|-------|----------|
  76. | Content-Type | application/json | Yes |
  77. ### Request Body
  78. ```json
  79. {
  80. "email": "user@example.com",
  81. "otpCode": "123456",
  82. "lang": "lo" // Default: "lo" / en
  83. }
  84. ```
  85. ### Parameters
  86. | Field | Type | Required | Default | Description |
  87. |-------|------|----------|---------|-------------|
  88. | email | string | Yes | - | Email đã nhận OTP |
  89. | otpCode | string | Yes | - | Mã OTP 6 số |
  90. | lang | string | No | "lo" | Ngôn ngữ thông báo: `lo` (ລາວ), `en` (English) |
  91. ### Response Success (200)
  92. ```json
  93. {
  94. "errorCode": "0",
  95. "message": "<Config: LOGIN_SUCCESS>",
  96. "data": {
  97. "userId": 12345,
  98. "email": "user@example.com",
  99. "fullName": "Nguyen Van A",
  100. "avatarUrl": "https://example.com/avatar.jpg",
  101. "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  102. "refreshToken": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4...",
  103. "expiresAt": "2024-12-30T10:00:00Z"
  104. }
  105. }
  106. ```
  107. ### Response Error Cases
  108. ```json
  109. // Thiếu email hoặc OTP
  110. {
  111. "errorCode": "-801",
  112. "message": "<Config: EMAIL_OTP_REQUIRED>",
  113. "data": {}
  114. }
  115. // OTP không hợp lệ
  116. {
  117. "errorCode": "-201",
  118. "message": "<Config: OTP_INVALID>",
  119. "data": {}
  120. }
  121. // OTP đã được sử dụng
  122. {
  123. "errorCode": "-203",
  124. "message": "<Config: OTP_ALREADY_USED>",
  125. "data": {}
  126. }
  127. // OTP đã hết hạn
  128. {
  129. "errorCode": "-202",
  130. "message": "<Config: OTP_EXPIRED>",
  131. "data": {}
  132. }
  133. // Không tìm thấy người dùng
  134. {
  135. "errorCode": "-300",
  136. "message": "<Config: USER_NOT_FOUND>",
  137. "data": {}
  138. }
  139. // Lỗi hệ thống
  140. {
  141. "errorCode": "-6",
  142. "message": "<Config: SYSTEM_FAILURE>",
  143. "data": {}
  144. }
  145. ```
  146. ### Response Fields
  147. | Field | Type | Description |
  148. |-------|------|-------------|
  149. | errorCode| string | "0" = Success, khác "0" = Error (xem Error Codes) |
  150. | message | string | Thông báo từ CONFIG table (theo ngôn ngữ) |
  151. | data.userId | int | ID người dùng |
  152. | data.email | string | Email người dùng |
  153. | data.fullName | string | Họ tên đầy đủ |
  154. | data.avatarUrl | string | URL ảnh đại diện (nullable) |
  155. | data.accessToken | string | JWT access token |
  156. | data.refreshToken | string | Refresh token để làm mới access token |
  157. | data.expiresAt | datetime | Thời điểm access token hết hạn |
  158. ### Notes
  159. - Access token có hiệu lực 24 giờ
  160. - Refresh token có hiệu lực 30 ngày
  161. - Mỗi lần đăng nhập thành công, các token cũ sẽ bị thu hồi
  162. ---
  163. ## Error Codes
  164. ### Success
  165. | errorCode| Constant | Description |
  166. |------|----------|-------------|
  167. | "0" | Success | Thành công (mọi request thành công đều trả về "0") |
  168. ### General Errors (-1 to -99)
  169. | errorCode| Constant | Description |
  170. |------|----------|-------------|
  171. | "-1" | Error | Lỗi chung |
  172. | "-6" | SystemError | Lỗi hệ thống |
  173. ### OTP Errors (-200 to -299)
  174. | errorCode| Constant | Description |
  175. |------|----------|-------------|
  176. | "-200" | OtpRequired | Yêu cầu OTP |
  177. | "-201" | OtpInvalid | OTP không hợp lệ |
  178. | "-202" | OtpExpired | OTP đã hết hạn |
  179. | "-203" | OtpAlreadyUsed | OTP đã được sử dụng |
  180. | "-204" | OtpMaxAttemptsExceeded | Vượt quá số lần thử |
  181. | "-205" | OtpSendFailed | Gửi OTP thất bại |
  182. | "-206" | OtpTooManyRequests | Request quá nhiều |
  183. ### User Errors (-300 to -399)
  184. | errorCode| Constant | Description |
  185. |------|----------|-------------|
  186. | "-300" | UserNotFound | Không tìm thấy người dùng |
  187. | "-304" | InvalidEmail | Email không hợp lệ |
  188. ### Validation Errors (-800 to -899)
  189. | errorCode| Constant | Description |
  190. |------|----------|-------------|
  191. | "-801" | RequiredFieldMissing | Thiếu trường bắt buộc |
  192. ---
  193. ## Authentication
  194. Sau khi đăng nhập thành công, sử dụng `accessToken` trong header cho các API yêu cầu xác thực:
  195. ```
  196. Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
  197. ```
  198. ---
  199. ## Flow Diagram
  200. ```
  201. ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
  202. │ Client │ │ API │ │ Email │
  203. └──────┬──────┘ └──────┬──────┘ └──────┬──────┘
  204. │ │ │
  205. │ POST /request-otp │ │
  206. │──────────────────>│ │
  207. │ │ │
  208. │ │ Generate OTP │
  209. │ │ Save to DB │
  210. │ │ Queue Email │
  211. │ │ │
  212. │ Response │ │
  213. │<──────────────────│ │
  214. │ │ │
  215. │ │ Send OTP Email │
  216. │ │──────────────────>│
  217. │ │ │
  218. │ │ │ OTP Email
  219. │<──────────────────────────────────────│
  220. │ │ │
  221. │ POST /verify-otp │ │
  222. │──────────────────>│ │
  223. │ │ │
  224. │ │ Verify OTP │
  225. │ │ Generate JWT │
  226. │ │ │
  227. │ Token Response │ │
  228. │<──────────────────│ │
  229. │ │ │
  230. ```
  231. ---
  232. ## Example Usage (cURL)
  233. ### Request OTP
  234. ```bash
  235. curl -X POST https://api.esimlao.com/apis/auth/request-otp \
  236. -H "Content-Type: application/json" \
  237. -d '{
  238. "email": "user@example.com",
  239. "lang": "vi"
  240. }'
  241. ```
  242. ### Verify OTP
  243. ```bash
  244. curl -X POST https://api.esimlao.com/apis/auth/verify-otp \
  245. -H "Content-Type: application/json" \
  246. -d '{
  247. "email": "user@example.com",
  248. "otpCode": "123456"
  249. }'
  250. ```
  251. ### Use Token
  252. ```bash
  253. curl -X GET https://api.esimlao.com/apis/user/profile \
  254. -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  255. ```
  256. ---
  257. ## 3. Article Category
  258. Lấy danh sách danh mục bài viết.
  259. ### Endpoint
  260. ```
  261. POST /apis/article/category
  262. ```
  263. ### Request
  264. ```json
  265. {
  266. "lang": "lo",
  267. "pageNumber": 0,
  268. "pageSize": 10,
  269. "parentId": null
  270. }
  271. ```
  272. | Field | Type | Default | Description |
  273. |-------|------|---------|-------------|
  274. | lang | string | "lo" | Ngôn ngữ: "lo", "en" (có thể truyền qua header Accept-Language) |
  275. | pageNumber | int | 0 | Trang hiện tại |
  276. | pageSize | int | 10 | Số item mỗi trang |
  277. | parentId | int? | null | ID danh mục cha (null = root) |
  278. ### Response Success
  279. ```json
  280. {
  281. "errorCode": "0",
  282. "message": "Success",
  283. "data": {
  284. "categories": [
  285. {
  286. "id": 1,
  287. "categoryName": "Cẩm nang du lịch",
  288. "categorySlug": "cam-nang-du-lich",
  289. "description": "Mô tả...",
  290. "iconUrl": "/icons/travel.png",
  291. "parentId": null,
  292. "displayOrder": 1
  293. }
  294. ],
  295. "pagination": {
  296. "pageNumber": 0,
  297. "pageSize": 10,
  298. "totalCount": 5,
  299. "totalPages": 1
  300. }
  301. }
  302. }
  303. ```
  304. ### Response Fields - categories[]
  305. | Field | Type | DB Column | Description |
  306. |-------|------|-----------|-------------|
  307. | id | int | ARTICLE_CATEGORY.ID | ID danh mục bài viết (Primary Key) |
  308. | categoryName | string | ARTICLE_CATEGORY.CATEGORY_NAME<br/>CATEGORY_NAME_EN<br/>CATEGORY_NAME_LO | Tên danh mục (theo ngôn ngữ được chọn) |
  309. | categorySlug | string | ARTICLE_CATEGORY.CATEGORY_SLUG | URL-friendly slug cho danh mục |
  310. | description | string | ARTICLE_CATEGORY.DESCRIPTION<br/>DESCRIPTION_EN<br/>DESCRIPTION_LO | Mô tả chi tiết danh mục |
  311. | iconUrl | string | ARTICLE_CATEGORY.ICON_URL | Đường dẫn icon của danh mục |
  312. | parentId | int? | ARTICLE_CATEGORY.PARENT_ID | ID danh mục cha (null = danh mục gốc) |
  313. | displayOrder | int | ARTICLE_CATEGORY.DISPLAY_ORDER | Thứ tự hiển thị (số nhỏ hơn hiển thị trước) |
  314. ### Response Fields - pagination
  315. | Field | Type | Description |
  316. |-------|------|-------------|
  317. | pageNumber | int | Trang hiện tại (bắt đầu từ 0) |
  318. | pageSize | int | Số items trên mỗi trang |
  319. | totalCount | int | Tổng số items trong database |
  320. | totalPages | int | Tổng số trang (= ceiling(totalCount / pageSize)) |
  321. ---
  322. ## 4. Article Load
  323. Lấy danh sách bài viết hoặc chi tiết 1 bài viết.
  324. ### Endpoint
  325. ```
  326. POST /apis/article/load
  327. ```
  328. ### Request (danh sách)
  329. ```json
  330. {
  331. "lang": "lo",
  332. "pageNumber": 0,
  333. "pageSize": 10,
  334. "categoryId": 1,
  335. "isFeatured": false
  336. }
  337. ```
  338. ### Request (chi tiết)
  339. ```json
  340. {
  341. "lang": "lo",
  342. "slug": "huong-dan-cai-dat-esim"
  343. }
  344. ```
  345. | Field | Type | Default | Description |
  346. |-------|------|---------|-------------|
  347. | lang | string | "lo" | Ngôn ngữ (hoặc header Accept-Language) |
  348. | pageNumber | int | 0 | Trang hiện tại |
  349. | pageSize | int | 10 | Số item mỗi trang |
  350. | categoryId | int? | null | Lọc theo danh mục |
  351. | isFeatured | bool? | null | Lọc bài viết nổi bật |
  352. | slug | string? | null | Slug để lấy chi tiết bài viết |
  353. ### Response Success (danh sách)
  354. ```json
  355. {
  356. "errorCode": "0",
  357. "message": "Success",
  358. "data": {
  359. "articles": [
  360. {
  361. "id": 1,
  362. "title": "Hướng dẫn cài đặt eSIM",
  363. "slug": "huong-dan-cai-dat-esim",
  364. "summary": "Tóm tắt...",
  365. "thumbnailUrl": "/images/article1.jpg",
  366. "categoryId": 1,
  367. "viewCount": 150,
  368. "isFeatured": true,
  369. "isPinned": false,
  370. "publishedDate": "2024-12-25"
  371. }
  372. ],
  373. "pagination": {...}
  374. }
  375. }
  376. ```
  377. ### Response Success (chi tiết)
  378. ```json
  379. {
  380. "errorCode": "0",
  381. "message": "Success",
  382. "data": {
  383. "article": {
  384. "id": 1,
  385. "title": "Hướng dẫn cài đặt eSIM",
  386. "slug": "huong-dan-cai-dat-esim",
  387. "summary": "Tóm tắt...",
  388. "content": "<p>Nội dung HTML...</p>",
  389. "thumbnailUrl": "/images/article1.jpg",
  390. "coverImageUrl": "/images/cover1.jpg",
  391. "metaDescription": "SEO description",
  392. "metaKeywords": "esim, laos",
  393. "categoryId": 1,
  394. "viewCount": 151,
  395. "isFeatured": true,
  396. "publishedDate": "2024-12-25",
  397. "createdDate": "2024-12-20"
  398. }
  399. }
  400. }
  401. ```
  402. ### Response Fields - articles[] (List)
  403. | Field | Type | DB Column | Description |
  404. |-------|------|-----------|-------------|
  405. | id | int | ARTICLE.ID | ID bài viết (Primary Key) |
  406. | title | string | ARTICLE.TITLE<br/>TITLE_EN<br/>TITLE_LO | Tiêu đề bài viết (theo ngôn ngữ) |
  407. | slug | string | ARTICLE.SLUG | URL-friendly slug (unique) |
  408. | summary | string | ARTICLE.SUMMARY<br/>SUMMARY_EN<br/>SUMMARY_LO | Tóm tắt ngắn gọn |
  409. | thumbnailUrl | string | ARTICLE.THUMBNAIL_URL | Ảnh thumbnail (dùng cho list) |
  410. | categoryId | int | ARTICLE.CATEGORY_ID | ID danh mục (FK → ARTICLE_CATEGORY) |
  411. | viewCount | int | ARTICLE.VIEW_COUNT | Số lượt xem |
  412. | isFeatured | bool | ARTICLE.IS_FEATURED | Bài viết nổi bật (true/false) |
  413. | isPinned | bool | ARTICLE.IS_PINNED | Bài viết được ghim (true/false) |
  414. | publishedDate | datetime | ARTICLE.PUBLISHED_DATE | Ngày xuất bản |
  415. ### Response Fields - article (Detail)
  416. | Field | Type | DB Column | Description |
  417. |-------|------|-----------|-------------|
  418. | *Tất cả fields từ List + thêm:* | | | |
  419. | content | string | ARTICLE.CONTENT<br/>CONTENT_EN<br/>CONTENT_LO | Nội dung HTML đầy đủ |
  420. | coverImageUrl | string | ARTICLE.COVER_IMAGE_URL | Ảnh bìa (dùng cho detail page) |
  421. | metaDescription | string | ARTICLE.META_DESCRIPTION<br/>META_DESCRIPTION_EN<br/>META_DESCRIPTION_LO | SEO meta description |
  422. | metaKeywords | string | ARTICLE.META_KEYWORDS | SEO keywords (comma-separated) |
  423. | createdDate | datetime | ARTICLE.CREATED_DATE | Ngày tạo bài viết |
  424. ---
  425. ## 5. Banner Load
  426. Lấy danh sách banner.
  427. ### Endpoint
  428. ```
  429. POST /apis/content/banner
  430. ```
  431. ### Request
  432. ```json
  433. {
  434. "lang": "lo",
  435. "pageNumber": 0,
  436. "pageSize": 10,
  437. "position": "home"
  438. }
  439. ```
  440. | Field | Type | Default | Description |
  441. |-------|------|---------|-------------|
  442. | lang | string | "lo" | Ngôn ngữ (hoặc header Accept-Language) |
  443. | pageNumber | int | 0 | Trang hiện tại |
  444. | pageSize | int | 10 | Số item mỗi trang |
  445. | position | string? | null | Vị trí: "home", "sidebar"... |
  446. ### Response
  447. ```json
  448. {
  449. "errorCode": "0",
  450. "data": {
  451. "banners": [
  452. {
  453. "id": 1,
  454. "title": "Banner Title",
  455. "subtitle": "Subtitle",
  456. "imageUrl": "/images/banner1.jpg",
  457. "imageMobileUrl": "/images/banner1_m.jpg",
  458. "linkUrl": "/promo",
  459. "linkTarget": "_blank",
  460. "position": "home",
  461. "displayOrder": 1
  462. }
  463. ],
  464. "pagination": {...}
  465. }
  466. }
  467. ```
  468. ### Response Fields - banners[]
  469. | Field | Type | DB Column | Description |
  470. |-------|------|-----------|-------------|
  471. | id | int | BANNER.ID | ID banner (Primary Key) |
  472. | title | string | BANNER.TITLE<br/>TITLE_EN<br/>TITLE_LO | Tiêu đề banner (theo ngôn ngữ) |
  473. | subtitle | string | BANNER.SUBTITLE<br/>SUBTITLE_EN<br/>SUBTITLE_LO | Phụ đề banner |
  474. | imageUrl | string | BANNER.IMAGE_URL | Ảnh banner (desktop) |
  475. | imageMobileUrl | string | BANNER.IMAGE_MOBILE_URL | Ảnh banner (mobile) |
  476. | linkUrl | string | BANNER.LINK_URL | URL đích khi click banner |
  477. | linkTarget | string | BANNER.LINK_TARGET | Target (_self, _blank,...) |
  478. | position | string | BANNER.POSITION | Vị trí hiển thị (home, category,...) |
  479. | displayOrder | int | BANNER.DISPLAY_ORDER | Thứ tự hiển thị |
  480. **Lọc tự động**: Chỉ trả về banners với `STATUS = 1` và trong khoảng `START_DATE ≤ now ≤ END_DATE`
  481. ---
  482. ## 6. Customer Review Load
  483. Lấy đánh giá của khách hàng.
  484. ### Endpoint
  485. ```
  486. POST /apis/content/review
  487. ```
  488. ### Request
  489. ```json
  490. {
  491. "lang": "lo",
  492. "pageNumber": 0,
  493. "pageSize": 10,
  494. "isFeatured": true
  495. }
  496. ```
  497. | Field | Type | Default | Description |
  498. |-------|------|---------|-------------|
  499. | lang | string | "lo" | Ngôn ngữ |
  500. | pageNumber | int | 0 | Trang |
  501. | pageSize | int | 10 | Số item |
  502. | isFeatured | bool? | null | Lọc review nổi bật |
  503. ### Response
  504. ```json
  505. {
  506. "errorCode": "0",
  507. "data": {
  508. "reviews": [
  509. {
  510. "id": 1,
  511. "customerName": "Nguyen Van A",
  512. "avatarUrl": "/avatars/user1.jpg",
  513. "rating": 1,
  514. "reviewContent": "Dich vu rat tot...",
  515. "destination": "Vientiane, Laos",
  516. "isFeatured": true,
  517. "createdDate": "2024-12-25"
  518. }
  519. ],
  520. "pagination": {...}
  521. }
  522. }
  523. ```
  524. ### Response Fields - reviews[]
  525. | Field | Type | DB Column | Description |
  526. |-------|------|-----------|-------------|
  527. | id | int | CUSTOMER_REVIEW.ID | ID đánh giá (Primary Key) |
  528. | customerName | string | CUSTOMER_REVIEW.CUSTOMER_NAME | Tên khách hàng |
  529. | avatarUrl | string | CUSTOMER_REVIEW.AVATAR_URL | Ảnh đại diện (nullable) |
  530. | rating | int | CUSTOMER_REVIEW.RATING | Số sao (1-5) |
  531. | reviewContent | string | CUSTOMER_REVIEW.REVIEW_CONTENT<br/>REVIEW_CONTENT_EN<br/>REVIEW_CONTENT_LO | Nội dung đánh giá (theo ngôn ngữ) |
  532. | destination | string | CUSTOMER_REVIEW.DESTINATION<br/>DESTINATION_EN<br/>DESTINATION_LO | Địa điểm du lịch |
  533. | isFeatured | bool | CUSTOMER_REVIEW.IS_FEATURED | Review nổi bật |
  534. | createdDate | datetime | CUSTOMER_REVIEW.CREATED_DATE | Ngày tạo review |
  535. **Lọc tự động**: Chỉ trả về reviews với `STATUS = 1` (đã duyệt)
  536. ---
  537. ## 6.1 Customer Review Create
  538. Khách hàng gửi đánh giá (chờ duyệt).
  539. ### Endpoint
  540. ```
  541. POST /apis/content/review/create
  542. ```
  543. ### Request
  544. ```json
  545. {
  546. "lang": "lo",
  547. "customerName": "Nguyen Van A",
  548. "reviewContent": "Dich vu rat tot, toi rat hai long!",
  549. "destination": "Vientiane, Laos",
  550. "rating": 5
  551. }
  552. ```
  553. | Field | Type | Required | Description |
  554. |-------|------|----------|-------------|
  555. | lang | string | No | Ngôn ngữ |
  556. | customerName | string | Yes | Tên khách hàng |
  557. | reviewContent | string | Yes | Nội dung đánh giá |
  558. | destination | string | No | Địa điểm |
  559. | rating | int | No | Đánh giá (1-5) |
  560. ### Response Success
  561. ```json
  562. {
  563. "errorCode": "0",
  564. "message": "Review submitted successfully",
  565. "data": {
  566. "reviewId": 123
  567. }
  568. }
  569. ```
  570. ### Note
  571. - Review mới sẽ có `Status = false` (chờ admin duyệt)
  572. ---
  573. ## 7. FAQ Category Load
  574. Lấy danh mục FAQ.
  575. ### Endpoint
  576. ```
  577. POST /apis/content/faq-category
  578. ```
  579. ### Request
  580. ```json
  581. {
  582. "lang": "lo",
  583. "pageNumber": 0,
  584. "pageSize": 10
  585. }
  586. ```
  587. ### Response
  588. ```json
  589. {
  590. "errorCode": "0",
  591. "data": {
  592. "categories": [
  593. {
  594. "id": 1,
  595. "categoryName": "Cài đặt eSIM",
  596. "categorySlug": "cai-dat-esim",
  597. "description": "Hướng dẫn cài đặt",
  598. "iconUrl": "/icons/setup.png",
  599. "displayOrder": 1
  600. }
  601. ],
  602. "pagination": {...}
  603. }
  604. }
  605. ```
  606. ### Response Fields - categories[]
  607. | Field | Type | DB Column | Description |
  608. |-------|------|-----------|-------------|
  609. | id | int | FAQ_CATEGORY.ID | ID danh mục FAQ (Primary Key) |
  610. | categoryName | string | FAQ_CATEGORY.CATEGORY_NAME<br/>CATEGORY_NAME_EN<br/>CATEGORY_NAME_LO | Tên danh mục FAQ |
  611. | categorySlug | string | FAQ_CATEGORY.CATEGORY_SLUG | URL-friendly slug |
  612. | description | string | FAQ_CATEGORY.DESCRIPTION<br/>DESCRIPTION_EN<br/>DESCRIPTION_LO | Mô tả danh mục |
  613. | iconUrl | string | FAQ_CATEGORY.ICON_URL | Icon của danh mục |
  614. | displayOrder | int | FAQ_CATEGORY.DISPLAY_ORDER | Thứ tự hiển thị |
  615. ---
  616. ## 8. FAQ Load
  617. Lấy danh sách câu hỏi thường gặp.
  618. ### Endpoint
  619. ```
  620. POST /apis/content/faq
  621. ```
  622. ### Request
  623. ```json
  624. {
  625. "lang": "lo",
  626. "pageNumber": 0,
  627. "pageSize": 10,
  628. "categoryId": 1,
  629. "isFeatured": false
  630. }
  631. ```
  632. | Field | Type | Default | Description |
  633. |-------|------|---------|-------------|
  634. | lang | string | "lo" | Ngôn ngữ |
  635. | pageNumber | int | 0 | Trang |
  636. | pageSize | int | 10 | Số item |
  637. | categoryId | int? | null | Lọc theo danh mục |
  638. | isFeatured | bool? | null | Lọc FAQ nổi bật |
  639. ### Response
  640. ```json
  641. {
  642. "errorCode": "0",
  643. "data": {
  644. "faqs": [
  645. {
  646. "id": 1,
  647. "question": "Làm sao để cài đặt eSIM?",
  648. "answer": "<p>Hướng dẫn chi tiết...</p>",
  649. "categoryId": 1,
  650. "viewCount": 100,
  651. "isFeatured": true
  652. }
  653. ],
  654. "pagination": {...}
  655. }
  656. }
  657. ```
  658. ### Response Fields - faqs[]
  659. | Field | Type | DB Column | Description |
  660. |-------|------|-----------|-------------|
  661. | id | int | FAQ.ID | ID câu hỏi (Primary Key) |
  662. | question | string | FAQ.QUESTION<br/>QUESTION_EN<br/>QUESTION_LO | Câu hỏi (theo ngôn ngữ) |
  663. | answer | string | FAQ.ANSWER<br/>ANSWER_EN<br/>ANSWER_LO | Câu trả lời (HTML format) |
  664. | categoryId | int | FAQ.CATEGORY_ID | ID danh mục (FK → FAQ_CATEGORY) |
  665. | viewCount | int | FAQ.VIEW_COUNT | Số lượt xem |
  666. | isFeatured | bool | FAQ.IS_FEATURED | FAQ nổi bật |
  667. **Lọc tự động**: Chỉ trả về FAQs với `STATUS = 1`
  668. ---