Quellcode durchsuchen

Api bài viết

LamGiang vor 1 Monat
Ursprung
Commit
2eaffd4c9d

+ 3 - 13
EsimLao/Common/Constant/CommonConstant.cs

@@ -160,19 +160,9 @@ public static class ApiUrlConstant
     public const String RequestOtpUrl = "/apis/auth/request-otp";
     public const String VerifyOtpUrl = "/apis/auth/verify-otp";
 
-    public const String CheckOpeningAppUrl = "/apis/user/check-opening-app";
-    public const String DetectMsisdnUrl = "/apis/user/detect-msisdn";
-    public const String LoadCampaignUrl = "/apis/campaign/load";
-    public const String ClaimPrizeCampaignUrl = "/apis/campaign/claim/prize";
-
-    public const String LoadMissionUrl = "/apis/mission/load";
-    public const String CompleteMissionUrl = "/apis/mission/execute";
-    public const String ClaimPrizeMissionUrl = "/apis/mission/claim/prize";
-
-    public const String UserInviteUrl = "/apis/user/invite";
-    public const String LoadPaymentChannelUrl = "/apis/payment/channel/load";
-    public const String VendorPackageRegisterUrl = "/apis/user/vendor-package/register";
-    public const String VendorPackageLoadUrl = "/apis/user/vendor-package/load";
+    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";

+ 77 - 0
EsimLao/Common/Http/ArticleRequest.cs

@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Common.Http
+{
+    /// <summary>
+    /// Request to load article categories
+    /// </summary>
+    public class ArticleCategoryReq
+    {
+        /// <summary>
+        /// Language code for response: "lo" (Lao), "en" (English)
+        /// Default: "lo"
+        /// </summary>
+        public string? lang { get; set; } = "lo";
+
+        /// <summary>
+        /// Page number (0-based index)
+        /// Default: 0
+        /// </summary>
+        public int pageNumber { get; set; } = 0;
+
+        /// <summary>
+        /// Number of items per page
+        /// Default: 10
+        /// </summary>
+        public int pageSize { get; set; } = 10;
+
+        /// <summary>
+        /// Parent category ID (null = get root categories)
+        /// </summary>
+        public int? parentId { get; set; }
+    }
+
+    /// <summary>
+    /// Request to load articles
+    /// </summary>
+    public class ArticleLoadReq
+    {
+        /// <summary>
+        /// Language code for response: "lo" (Lao), "en" (English)
+        /// Default: "lo"
+        /// </summary>
+        public string? lang { get; set; } = "lo";
+
+        /// <summary>
+        /// Page number (0-based index)
+        /// Default: 0
+        /// </summary>
+        public int pageNumber { get; set; } = 0;
+
+        /// <summary>
+        /// Number of items per page
+        /// Default: 10
+        /// </summary>
+        public int pageSize { get; set; } = 10;
+
+        /// <summary>
+        /// Filter by category ID (optional)
+        /// </summary>
+        public int? categoryId { get; set; }
+
+        /// <summary>
+        /// Filter by featured articles only
+        /// </summary>
+        public bool? isFeatured { get; set; }
+
+        /// <summary>
+        /// Article slug for getting single article detail
+        /// </summary>
+        public string? slug { get; set; }
+    }
+}

+ 276 - 0
EsimLao/Esim.Apis/Business/Article/ArticleBusinessImpl.cs

@@ -0,0 +1,276 @@
+using Common;
+using Common.Constant;
+using Common.Http;
+using Esim.Apis.Singleton;
+using Database.Database;
+using log4net;
+using Microsoft.AspNetCore.Mvc;
+using Newtonsoft.Json;
+
+namespace Esim.Apis.Business
+{
+    public class ArticleBusinessImpl : IArticleBusiness
+    {
+        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(
+            typeof(ArticleBusinessImpl)
+        );
+
+        private ModelContext dbContext;
+        IConfiguration configuration;
+
+        public ArticleBusinessImpl(ModelContext _dbContext, IConfiguration _configuration)
+        {
+            dbContext = _dbContext;
+            configuration = _configuration;
+        }
+
+        private string GetParameter(string key)
+        {
+            return configuration.GetSection(key).Value ?? "";
+        }
+
+
+        /// <summary>
+        /// Load article categories with pagination
+        /// </summary>
+        public async Task<IActionResult> ArticleCategory(HttpRequest httpRequest, ArticleCategoryReq request)
+        {
+            var url = httpRequest.Path;
+            var json = JsonConvert.SerializeObject(request);
+            log.Debug("URL: " + url + " => Request: " + json);
+            try
+            {
+                string lang = (request.lang ?? "lo").ToLower();
+                int pageNumber = request.pageNumber < 0 ? 0 : request.pageNumber;
+                int pageSize = request.pageSize <= 0 ? 10 : request.pageSize;
+
+                // Query categories
+                var query = dbContext.ArticleCategories
+                    .Where(c => c.Status == true);
+
+                // Filter by parent category
+                if (request.parentId.HasValue)
+                {
+                    query = query.Where(c => c.ParentId == request.parentId);
+                }
+                else
+                {
+                    query = query.Where(c => c.ParentId == null); // Root categories
+                }
+
+                // Get total count for pagination
+                int totalCount = query.Count();
+                int totalPages = (int)Math.Ceiling((double)totalCount / pageSize);
+
+                // Apply pagination and ordering
+                var categories = query
+                    .OrderBy(c => c.DisplayOrder)
+                    .ThenBy(c => c.Id)
+                    .Skip(pageNumber * pageSize)
+                    .Take(pageSize)
+                    .Select(c => new
+                    {
+                        c.Id,
+                        categoryName = lang == "en"
+                            ? (c.CategoryNameEn ?? c.CategoryName)
+                            : (c.CategoryNameLo ?? c.CategoryName),
+                        c.CategorySlug,
+                        description = lang == "en"
+                            ? (c.DescriptionEn ?? c.Description)
+                            : (c.DescriptionLo ?? c.Description),
+                        c.IconUrl,
+                        c.ParentId,
+                        c.DisplayOrder
+                    })
+                    .ToList();
+
+                return DotnetLib.Http.HttpResponse.BuildResponse(
+                    log,
+                    url,
+                    json,
+                    CommonErrorCode.Success,
+                    ConfigManager.Instance.GetConfigWebValue("LOAD_SUCCESS", lang),
+                    new
+                    {
+                        items = categories,
+                        pagination = new
+                        {
+                            pageNumber,
+                            pageSize,
+                            totalCount,
+                            totalPages
+                        }
+                    }
+                );
+            }
+            catch (Exception exception)
+            {
+                log.Error("Exception: ", exception);
+            }
+            return DotnetLib.Http.HttpResponse.BuildResponse(
+                log,
+                url,
+                json,
+                CommonErrorCode.SystemError,
+                ConfigManager.Instance.GetConfigWebValue("SYSTEM_FAILURE"),
+                new { }
+            );
+        }
+
+        /// <summary>
+        /// Load articles with pagination and filters
+        /// </summary>
+        public async Task<IActionResult> ArticleLoad(HttpRequest httpRequest, ArticleLoadReq request)
+        {
+            var url = httpRequest.Path;
+            var json = JsonConvert.SerializeObject(request);
+            log.Debug("URL: " + url + " => Request: " + json);
+            try
+            {
+                string lang = (request.lang ?? "lo").ToLower();
+                int pageNumber = request.pageNumber < 0 ? 0 : request.pageNumber;
+                int pageSize = request.pageSize <= 0 ? 10 : request.pageSize;
+
+                // If slug is provided, return single article detail
+                if (!string.IsNullOrEmpty(request.slug))
+                {
+                    var article = dbContext.Articles
+                        .Where(a => a.Slug == request.slug && a.Status == true)
+                        .Select(a => new
+                        {
+                            a.Id,
+                            title = lang == "en"
+                                ? (a.TitleEn ?? a.Title)
+                                : (a.TitleLo ?? a.Title),
+                            a.Slug,
+                            summary = lang == "en"
+                                ? (a.SummaryEn ?? a.Summary)
+                                : (a.SummaryLo ?? a.Summary),
+                            content = lang == "en"
+                                ? (a.ContentEn ?? a.Content)
+                                : (a.ContentLo ?? a.Content),
+                            a.ThumbnailUrl,
+                            a.CoverImageUrl,
+                            metaDescription = lang == "en"
+                                ? (a.MetaDescriptionEn ?? a.MetaDescription)
+                                : (a.MetaDescriptionLo ?? a.MetaDescription),
+                            a.MetaKeywords,
+                            a.CategoryId,
+                            a.ViewCount,
+                            a.IsFeatured,
+                            a.PublishedDate,
+                            a.CreatedDate
+                        })
+                        .FirstOrDefault();
+
+                    if (article == null)
+                    {
+                        return DotnetLib.Http.HttpResponse.BuildResponse(
+                            log,
+                            url,
+                            json,
+                            CommonErrorCode.Error,
+                            ConfigManager.Instance.GetConfigWebValue("ARTICLE_NOT_FOUND", lang),
+                            new { }
+                        );
+                    }
+
+                    // Increment view count
+                    var articleEntity = dbContext.Articles.FirstOrDefault(a => a.Slug == request.slug);
+                    if (articleEntity != null)
+                    {
+                        articleEntity.ViewCount = (articleEntity.ViewCount ?? 0) + 1;
+                        await dbContext.SaveChangesAsync();
+                    }
+
+                    return DotnetLib.Http.HttpResponse.BuildResponse(
+                        log,
+                        url,
+                        json,
+                        CommonErrorCode.Success,
+                        ConfigManager.Instance.GetConfigWebValue("LOAD_SUCCESS", lang),
+                        new { article }
+                    );
+                }
+
+                // Query articles list
+                var query = dbContext.Articles
+                    .Where(a => a.Status == true);
+
+                // Filter by category
+                if (request.categoryId.HasValue)
+                {
+                    query = query.Where(a => a.CategoryId == request.categoryId);
+                }
+
+                // Filter by featured
+                if (request.isFeatured.HasValue && request.isFeatured.Value)
+                {
+                    query = query.Where(a => a.IsFeatured == true);
+                }
+
+                // Get total count for pagination
+                int totalCount = query.Count();
+                int totalPages = (int)Math.Ceiling((double)totalCount / pageSize);
+
+                // Apply pagination and ordering (pinned first, then by published date)
+                var articles = query
+                    .OrderByDescending(a => a.IsPinned)
+                    .ThenByDescending(a => a.PublishedDate)
+                    .ThenByDescending(a => a.CreatedDate)
+                    .Skip(pageNumber * pageSize)
+                    .Take(pageSize)
+                    .Select(a => new
+                    {
+                        a.Id,
+                        title = lang == "en"
+                            ? (a.TitleEn ?? a.Title)
+                            : (a.TitleLo ?? a.Title),
+                        a.Slug,
+                        summary = lang == "en"
+                            ? (a.SummaryEn ?? a.Summary)
+                            : (a.SummaryLo ?? a.Summary),
+                        a.ThumbnailUrl,
+                        a.CategoryId,
+                        a.ViewCount,
+                        a.IsFeatured,
+                        a.IsPinned,
+                        a.PublishedDate
+                    })
+                    .ToList();
+
+                return DotnetLib.Http.HttpResponse.BuildResponse(
+                    log,
+                    url,
+                    json,
+                    CommonErrorCode.Success,
+                    ConfigManager.Instance.GetConfigWebValue("LOAD_SUCCESS", lang),
+                    new
+                    {
+                        items = articles,
+                        pagination = new
+                        {
+                            pageNumber,
+                            pageSize,
+                            totalCount,
+                            totalPages
+                        }
+                    }
+                );
+            }
+            catch (Exception exception)
+            {
+                log.Error("Exception: ", exception);
+            }
+            return DotnetLib.Http.HttpResponse.BuildResponse(
+                log,
+                url,
+                json,
+                CommonErrorCode.SystemError,
+                ConfigManager.Instance.GetConfigWebValue("SYSTEM_FAILURE"),
+                new { }
+            );
+        }
+
+    }
+}

+ 15 - 0
EsimLao/Esim.Apis/Business/Article/IArticleBusiness.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections;
+using Common.Http;
+using log4net;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+namespace Esim.Apis.Business
+{
+    public interface IArticleBusiness
+    {
+        Task<IActionResult> ArticleCategory(HttpRequest httpRequest, ArticleCategoryReq request);
+        Task<IActionResult> ArticleLoad(HttpRequest httpRequest, ArticleLoadReq request);
+    }
+}

+ 0 - 1
EsimLao/Esim.Apis/Business/User/IUserBusiness.cs

@@ -18,6 +18,5 @@ namespace Esim.Apis.Business
         // Auth methods
         Task<IActionResult> RequestOtp(HttpRequest httpRequest, RequestOtpReq request);
         Task<IActionResult> VerifyOtp(HttpRequest httpRequest, VerifyOtpReq request);
-
     }
 }

+ 66 - 0
EsimLao/Esim.Apis/Controllers/ArticleController.cs

@@ -0,0 +1,66 @@
+using System;
+using Common;
+using Common.Constant;
+using Common.Http;
+using Esim.Apis.Business;
+using Database.Database;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace RevoSystem.Apis.Controllers
+{
+    [ApiController]
+    public class ArticleController : Controller
+    {
+        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(
+            typeof(ArticleController)
+        );
+
+        ModelContext dbContext;
+        IConfiguration configuration;
+
+        IArticleBusiness articleBusiness;
+
+        public ArticleController(
+            IConfiguration _configuration,
+            IArticleBusiness articleBusiness,
+            ModelContext dbContext
+        )
+        {
+            configuration = _configuration;
+            this.articleBusiness = articleBusiness;
+            this.dbContext = dbContext;
+        }
+
+        private string GetParameter(string key)
+        {
+            return configuration.GetSection(key).Value ?? "";
+        }
+
+        #region Article Endpoints
+
+        /// <summary>
+        /// Load article categories with pagination
+        /// POST /apis/article/category
+        /// </summary>
+        [HttpPost]
+        [Route(ApiUrlConstant.ArticleCategoryUrl)]
+        public async Task<IActionResult> ArticleCategory(ArticleCategoryReq request)
+        {
+            return await articleBusiness.ArticleCategory(HttpContext.Request, request);
+        }
+
+        /// <summary>
+        /// Load articles with pagination and filters
+        /// POST /apis/article/load
+        /// </summary>
+        [HttpPost]
+        [Route(ApiUrlConstant.ArticleLoadUrl)]
+        public async Task<IActionResult> ArticleLoad(ArticleLoadReq request)
+        {
+            return await articleBusiness.ArticleLoad(HttpContext.Request, request);
+        }
+
+        #endregion
+    }
+}

+ 0 - 34
EsimLao/Esim.Apis/Controllers/UserController.cs

@@ -37,39 +37,6 @@ namespace RevoSystem.Apis.Controllers
             return configuration.GetSection(key).Value ?? "";
         }
 
-        //[HttpPost]
-        //[Route(ApiUrlConstant.DetectMsisdnUrl)]
-        //public async Task<IActionResult> DetectMsisdn(DetectMsisdnReq request)
-        //{
-        //    return await userBusiness.DetectMsisdn(HttpContext.Request, request);
-        //}
-
-        //[Authorize]
-        //[HttpPost]
-        //[Route(ApiUrlConstant.UserInviteUrl)]
-        //public async Task<IActionResult> UserInvite(UserInviteReq request)
-        //{
-        //    return await userBusiness.UserInvite(HttpContext.Request, request);
-        //}
-
-        //[Authorize]
-        //[HttpPost]
-        //[Route(ApiUrlConstant.VendorPackageRegisterUrl)]
-        //public async Task<IActionResult> VendorPackageRegister(VendorPackageRegisterReq request)
-        //{
-        //    return await userBusiness.VendorPackageRegister(HttpContext.Request, request);
-        //}
-
-        //[Authorize]
-        //[HttpPost]
-        //[Route(ApiUrlConstant.VendorPackageLoadUrl)]
-        //public async Task<IActionResult> VendorPackageLoad(LoadVendorPackageReq request)
-        //{
-        //    return await userBusiness.LoadVendorPackage(HttpContext.Request, request);
-        //}
-
-        #region Auth Endpoints - OTP Login
-
         /// <summary>
         /// Request OTP via email
         /// POST /apis/auth/request-otp
@@ -92,6 +59,5 @@ namespace RevoSystem.Apis.Controllers
             return await userBusiness.VerifyOtp(HttpContext.Request, request);
         }
 
-        #endregion
     }
 }

+ 1 - 0
EsimLao/Esim.Apis/Program.cs

@@ -21,6 +21,7 @@ builder.Services.AddDbContext<ModelContext>(options =>
 
 // Add Business Services
 builder.Services.AddScoped<IUserBusiness, UserBusinessImpl>();
+builder.Services.AddScoped<IArticleBusiness, ArticleBusinessImpl>();
 
 // Configure JWT Authentication
 var jwtKey = builder.Configuration["Jwt:Key"] ?? "EsimLaoSecretKey12345678901234567890";