diff --git a/E-Commerce.Domain/Contracts/IBasketRepository.cs b/E-Commerce.Domain/Contracts/IBasketRepository.cs new file mode 100644 index 0000000..4a972d1 --- /dev/null +++ b/E-Commerce.Domain/Contracts/IBasketRepository.cs @@ -0,0 +1,17 @@ +using E_Commerce.Domain.Entities.BasketModule; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace E_Commerce.Domain.Contracts +{ + public interface IBasketRepository + { + Task GetBasketAsync(string basketid); + Task CreateOrUpdateBasketAsync(CustomerBasket basket , TimeSpan timeTolive=default); + Task DeleteAsync(string basketid); + + } +} diff --git a/E-Commerce.Domain/Entities/BasketModule/BasketItem.cs b/E-Commerce.Domain/Entities/BasketModule/BasketItem.cs new file mode 100644 index 0000000..1c983ed --- /dev/null +++ b/E-Commerce.Domain/Entities/BasketModule/BasketItem.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace E_Commerce.Domain.Entities.BasketModule +{ + public class BasketItem + { + + public int Id { get; set; } + public string ProductName { get; set; } = default!; + public string PictureUrl { get; set; } = default!; + public decimal Price { get; set; } + public int Quantity { get; set; } + + } +} diff --git a/E-Commerce.Domain/Entities/BasketModule/CustomerBasket.cs b/E-Commerce.Domain/Entities/BasketModule/CustomerBasket.cs new file mode 100644 index 0000000..2904173 --- /dev/null +++ b/E-Commerce.Domain/Entities/BasketModule/CustomerBasket.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace E_Commerce.Domain.Entities.BasketModule +{ + public class CustomerBasket + { + public String Id { get; set; } = default!; // Will Be GUID : Created By Client [FrontEnd] + public ICollection Items { get; set; } = []; + } +} diff --git a/E-Commerce.Persistance/E-Commerce.Persistance.csproj b/E-Commerce.Persistance/E-Commerce.Persistance.csproj index 3bb66d3..3a1869b 100644 --- a/E-Commerce.Persistance/E-Commerce.Persistance.csproj +++ b/E-Commerce.Persistance/E-Commerce.Persistance.csproj @@ -9,6 +9,7 @@ + diff --git a/E-Commerce.Persistance/Repositories/BasketRepository.cs b/E-Commerce.Persistance/Repositories/BasketRepository.cs new file mode 100644 index 0000000..4b48012 --- /dev/null +++ b/E-Commerce.Persistance/Repositories/BasketRepository.cs @@ -0,0 +1,51 @@ +using E_Commerce.Domain.Contracts; +using E_Commerce.Domain.Entities.BasketModule; +using StackExchange.Redis; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +namespace E_Commerce.Persistance.Repositories +{ + public class BasketRepository : IBasketRepository + { + private readonly IDatabase _database; + public BasketRepository(IConnectionMultiplexer connection) + { + _database = connection.GetDatabase(); + } + public async Task CreateOrUpdateBasketAsync(CustomerBasket basket, TimeSpan timeTolive = default) + { + var jsonBasket = JsonSerializer.Serialize(basket); + var IsCreatedOrUpdated = await _database.StringSetAsync(basket.Id, jsonBasket, + (timeTolive == default) ? TimeSpan.FromDays(7) : timeTolive); + if (IsCreatedOrUpdated) + { + //var Basket = await _database.StringGetAsync(basket.Id); + //return JsonSerializer.Deserialize(Basket!); + // = + return await GetBasketAsync(basket.Id); + + } + else + { + return null; + } + } + + public async Task DeleteAsync(string basketid) => await _database.KeyDeleteAsync(basketid); + + + public async Task GetBasketAsync(string basketid) + { + var Basket = await _database.StringGetAsync(basketid); + if(Basket.IsNullOrEmpty) + return null; + else + return JsonSerializer.Deserialize(Basket!); + } + } +} diff --git a/E-Commerce.Presentaion/Controllers/BasketsController.cs b/E-Commerce.Presentaion/Controllers/BasketsController.cs new file mode 100644 index 0000000..9ef7690 --- /dev/null +++ b/E-Commerce.Presentaion/Controllers/BasketsController.cs @@ -0,0 +1,48 @@ +using E_Commerce.Services.Abstraction; +using E_Commerce.Shared.DTOs.BasketDTOs; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace E_Commerce.Presentaion.Controllers +{ + [ApiController] + [Route("api/[Controller]")] + public class BasketsController : ControllerBase + { + private readonly IBasketService _basketService; + + public BasketsController(IBasketService basketService) + { + this._basketService = basketService; + } + + // GET : BaseUrl/api/Baskets?id= => Id Will Be Token As A Query String + [HttpGet] + public async Task> GetBasket(string id) + { + var Basket = await _basketService.GetBasketAsync(id); + return Ok(Basket); + } + + // POST : BaseUrl/api/Baskets + [HttpPost] + public async Task> CreateOrUpdateBasket(BasketDTO basket) + { + var Basket = await _basketService.CreateOrUpdateBasketAsync(basket); + return Ok(Basket); + } + + // Delete : BaseUrl/api/Bakets/{id} => Id Will Be Token From Route + [HttpDelete("{id}")] + public async Task> DeleteBasket(string id) + { + var result = await _basketService.DeleteBasketAsync(id); + return Ok(result); + } + + } +} diff --git a/E-Commerce.Services.Abstraction/IBasketService.cs b/E-Commerce.Services.Abstraction/IBasketService.cs new file mode 100644 index 0000000..3d0a118 --- /dev/null +++ b/E-Commerce.Services.Abstraction/IBasketService.cs @@ -0,0 +1,16 @@ +using E_Commerce.Shared.DTOs.BasketDTOs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace E_Commerce.Services.Abstraction +{ + public interface IBasketService + { + Task GetBasketAsync(string id); + Task CreateOrUpdateBasketAsync(BasketDTO basketDTO); + Task DeleteBasketAsync(string id); + } +} diff --git a/E-Commerce.Services/BasketService.cs b/E-Commerce.Services/BasketService.cs new file mode 100644 index 0000000..a742a22 --- /dev/null +++ b/E-Commerce.Services/BasketService.cs @@ -0,0 +1,41 @@ +using AutoMapper; +using E_Commerce.Domain.Contracts; +using E_Commerce.Domain.Entities.BasketModule; +using E_Commerce.Services.Abstraction; +using E_Commerce.Shared.DTOs.BasketDTOs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace E_Commerce.Services +{ + public class BasketService : IBasketService + { + private readonly IBasketRepository _basketRepository; + private readonly IMapper _mapper; + + public BasketService(IBasketRepository basketRepository, IMapper mapper) + { + this._basketRepository = basketRepository; + this._mapper = mapper; + } + public async Task CreateOrUpdateBasketAsync(BasketDTO basketDTO) + { + var CustomerBasket = _mapper.Map(basketDTO); + var CreatedOrUpdatedBasket = await _basketRepository.CreateOrUpdateBasketAsync(CustomerBasket); + return _mapper.Map(CreatedOrUpdatedBasket!); + + } + + public async Task DeleteBasketAsync(string id) => await _basketRepository.DeleteAsync(id); + + + public async Task GetBasketAsync(string id) + { + var Basket = await _basketRepository.GetBasketAsync(id); + return _mapper.Map(Basket!); + } + } +} diff --git a/E-Commerce.Services/MappingProfiels/BasketProfile.cs b/E-Commerce.Services/MappingProfiels/BasketProfile.cs new file mode 100644 index 0000000..8a9751e --- /dev/null +++ b/E-Commerce.Services/MappingProfiels/BasketProfile.cs @@ -0,0 +1,21 @@ +using AutoMapper; +using E_Commerce.Domain.Entities.BasketModule; +using E_Commerce.Shared.DTOs.BasketDTOs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace E_Commerce.Services.MappingProfiels +{ + public class BasketProfile :Profile + { + public BasketProfile() + { + CreateMap().ReverseMap(); + + CreateMap().ReverseMap(); + } + } +} diff --git a/E-Commerce.Shared/DTOs/BasketDTOs/BasketDTO.cs b/E-Commerce.Shared/DTOs/BasketDTOs/BasketDTO.cs new file mode 100644 index 0000000..e3ba899 --- /dev/null +++ b/E-Commerce.Shared/DTOs/BasketDTOs/BasketDTO.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace E_Commerce.Shared.DTOs.BasketDTOs +{ + public record BasketDTO(string Id, ICollection Items); + + // record: A special type in C# used for concise data storage, ideal for DTOs + // immutable: Values cannot be changed after creation (safe for transfer and comparison) + // Value equality: Compared by content, not by reference (if two objects have identical content → considered equal) + // Supports quick construction via a built-in constructor in the same line + // Facilitates working with JSON in Web APIs + // Less code and clearer than a regular class + +} diff --git a/E-Commerce.Shared/DTOs/BasketDTOs/BasketItemDTO.cs b/E-Commerce.Shared/DTOs/BasketDTOs/BasketItemDTO.cs new file mode 100644 index 0000000..4957ced --- /dev/null +++ b/E-Commerce.Shared/DTOs/BasketDTOs/BasketItemDTO.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace E_Commerce.Shared.DTOs.BasketDTOs +{ + public record BasketItemDTO( + int Id, + String ProductName , + string PictureUrl, + [Range(1,double.MaxValue)] + decimal Price, + [Range(1,100)] + int Quantity + ); + +} diff --git a/E-CommerceProject/Program.cs b/E-CommerceProject/Program.cs index c60b8a5..08aeaa3 100644 --- a/E-CommerceProject/Program.cs +++ b/E-CommerceProject/Program.cs @@ -8,6 +8,7 @@ using E_Commerce.Services.MappingProfiels; using E_CommerceProject.Extentions; using Microsoft.EntityFrameworkCore; +using StackExchange.Redis; using System.Reflection; using System.Threading.Tasks; @@ -39,10 +40,18 @@ public static async Task Main(string[] args) builder.Services.AddScoped(); - + builder.Services.AddSingleton(sp => + { + return ConnectionMultiplexer.Connect(builder.Configuration.GetConnectionString("RedisConnection")!); + }); + builder.Services.AddScoped(); + builder.Services.AddScoped(); #endregion + #region Redis Connection + + #endregion var app = builder.Build(); #region Data Seeding - Pending Migations diff --git a/E-CommerceProject/appsettings.Development.json b/E-CommerceProject/appsettings.Development.json index 38cf6cd..09cb6cf 100644 --- a/E-CommerceProject/appsettings.Development.json +++ b/E-CommerceProject/appsettings.Development.json @@ -6,7 +6,9 @@ } }, "ConnectionStrings": { - "DefaultConnection": " Server =.; Database =Ecommerce; Trusted_Connection=True;TrustServerCertificate=True;" + "DefaultConnection": " Server =.; Database =Ecommerce; Trusted_Connection=True;TrustServerCertificate=True;", + "RedisConnection": "localhost" + },