diff --git a/Common/WebStore.Domain/DTO/Orders/OrderDTO.cs b/Common/WebStore.Domain/DTO/Orders/OrderDTO.cs new file mode 100644 index 0000000..0540696 --- /dev/null +++ b/Common/WebStore.Domain/DTO/Orders/OrderDTO.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using WebStore.Domain.ViewModels; + +namespace WebStore.Domain.DTO.Orders +{ + public record OrderItemDTO(int Id, decimal Price, int Quantity); + + public record OrderDTO( + int Id, + string Name, + string Phone, + string Address, + DateTime Date, + IEnumerable Items); + + public record CreateOrderModel(OrderViewModel Order, IList Items); +} diff --git a/Common/WebStore.Domain/DTO/Products/BrandDTO.cs b/Common/WebStore.Domain/DTO/Products/BrandDTO.cs new file mode 100644 index 0000000..1fd121a --- /dev/null +++ b/Common/WebStore.Domain/DTO/Products/BrandDTO.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WebStore.Domain.Entities.Base; + +namespace WebStore.Domain.DTO.Products +{ + public class BrandDTO: NamedEntity + { + public int Order { get; set; } + + public int ProductsCount { get; set; } + } +} diff --git a/Common/WebStore.Domain/DTO/Products/CategoryDTO.cs b/Common/WebStore.Domain/DTO/Products/CategoryDTO.cs new file mode 100644 index 0000000..996e962 --- /dev/null +++ b/Common/WebStore.Domain/DTO/Products/CategoryDTO.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WebStore.Domain.Entities.Base; + +namespace WebStore.Domain.DTO.Products +{ + public class CategoryDTO: NamedEntity + { + + public int Order { get; set; } + + public int? ParentId { get; set; } + + public int ProductsCount { get; set; } + } +} diff --git a/Common/WebStore.Domain/DTO/Products/ProductDTO.cs b/Common/WebStore.Domain/DTO/Products/ProductDTO.cs new file mode 100644 index 0000000..2255f9d --- /dev/null +++ b/Common/WebStore.Domain/DTO/Products/ProductDTO.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WebStore.Domain.Entities.Base; + +namespace WebStore.Domain.DTO.Products +{ + public class ProductDTO: NamedEntity + { + public int Order { get; set; } + public decimal Price { get; set; } + public string ImageUrl { get; set; } + public BrandDTO Brand { get; set; } + public CategoryDTO Category { get; set; } + public string Description { get; set; } + public int Discount { get; set; } + + } +} diff --git a/Common/WebStore.Domain/ViewModels/UserOrderViewModel.cs b/Common/WebStore.Domain/ViewModels/UserOrderViewModel.cs new file mode 100644 index 0000000..e8a77fd --- /dev/null +++ b/Common/WebStore.Domain/ViewModels/UserOrderViewModel.cs @@ -0,0 +1,20 @@ +using System.ComponentModel.DataAnnotations; + +namespace WebStore.Domain.ViewModels +{ + public class UserOrderViewModel + { + public int Id { get; set; } + + [Required] + public string Name { get; set; } + + [Required] + public string Phone { get; set; } + + [Required] + public string Address { get; set; } + + public decimal TotalSum { get; set; } + } +} diff --git a/Common/WebStore.Domain/WebStore.Domain.csproj b/Common/WebStore.Domain/WebStore.Domain.csproj index e9a2516..0632141 100644 --- a/Common/WebStore.Domain/WebStore.Domain.csproj +++ b/Common/WebStore.Domain/WebStore.Domain.csproj @@ -1,7 +1,7 @@ - + - netstandard2.0 + net5.0 diff --git a/Services/WebStore.Client/Base/BaseClient.cs b/Services/WebStore.Client/Base/BaseClient.cs index 00a968e..b587d52 100644 --- a/Services/WebStore.Client/Base/BaseClient.cs +++ b/Services/WebStore.Client/Base/BaseClient.cs @@ -1,6 +1,7 @@ using System; using System.Net.Http; using System.Net.Http.Headers; +using System.Threading.Tasks; using Microsoft.Extensions.Configuration; namespace WebStore.Client.Base @@ -22,5 +23,36 @@ protected BaseClient(IConfiguration Configuration, string ServiceAddress) } }; } + + protected T Get(string url) => GetAsync(url).Result; + protected async Task GetAsync(string url) + { + var response = await Http.GetAsync(url); + return await response.EnsureSuccessStatusCode().Content.ReadAsAsync(); + } + + protected HttpResponseMessage Post(string url, T item) => PostAsync(url, item).Result; + + protected async Task PostAsync(string url, T item) + { + var response = await Http.PostAsJsonAsync(url, item); + return response.EnsureSuccessStatusCode(); + } + + protected HttpResponseMessage Put(string url, T item) => PutAsync(url, item).Result; + + protected async Task PutAsync(string url, T item) + { + var response = await Http.PutAsJsonAsync(url, item); + return response.EnsureSuccessStatusCode(); + } + + protected HttpResponseMessage Delete(string url) => DeleteAsync(url).Result; + + protected async Task DeleteAsync(string url) + { + var response = await Http.DeleteAsync(url); + return response; + } } } diff --git a/Services/WebStore.Client/Employees/EmployeesClient.cs b/Services/WebStore.Client/Employees/EmployeesClient.cs new file mode 100644 index 0000000..235e727 --- /dev/null +++ b/Services/WebStore.Client/Employees/EmployeesClient.cs @@ -0,0 +1,36 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using Webstore.Interfaces.Services; +using WebStore.Client.Base; +using WebStore.Domain.Models; + +namespace WebStore.Client.Employees +{ + public class EmployeesClient: BaseClient, IEmployeesData + { + private readonly ILogger _Logger; + + public EmployeesClient(IConfiguration Configuration, ILogger Logger): base(Configuration, "api/employees") + { + _Logger = Logger; + } + + public int Add(Employee employee)=>Post(Address,employee).Content.ReadAsAsync().Result; + + public bool Delete(int id) => Delete($"{Address}/{id}").IsSuccessStatusCode; + + public IEnumerable Get() => Get>(Address); + + public Employee Get(int id) => Get($"{Address}/{id}"); + + public void Update(Employee employee) + { + _Logger.LogInformation("Редактирование сотрудника с id:{ 0}", employee.Id); + Put(Address, employee); + } + } +} diff --git a/Services/WebStore.Client/Orders/OrdersClient.cs b/Services/WebStore.Client/Orders/OrdersClient.cs new file mode 100644 index 0000000..cb0e6dc --- /dev/null +++ b/Services/WebStore.Client/Orders/OrdersClient.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Webstore.Interfaces; +using Webstore.Interfaces.Services; +using WebStore.Client.Base; +using WebStore.Domain.DTO.Orders; +using WebStore.Domain.ViewModels; +using WebStore.Interfaces; + +namespace WebStore.Client.Orders +{ + public class OrdersClient : BaseClient, IOrderService + { + private readonly ILogger _Logger; + public OrdersClient(IConfiguration Configuration, ILogger Logger) + : base(Configuration, WebAPI.Orders) => + _Logger = Logger; + + + public async Task> GetUserOrders(string UserName) => + await GetAsync>($"{Address}/user/{UserName}"); + + public async Task GetOrderById(int id) => + await GetAsync($"{Address}/{id}"); + + public async Task CreateOrder(string UserName, CreateOrderModel OrderModel) + { + var response = await PostAsync($"{Address}/{UserName}", OrderModel); + return await response.Content.ReadAsAsync(); + } + } +} diff --git a/Services/WebStore.Client/Products/ProductsClient.cs b/Services/WebStore.Client/Products/ProductsClient.cs new file mode 100644 index 0000000..12d4d38 --- /dev/null +++ b/Services/WebStore.Client/Products/ProductsClient.cs @@ -0,0 +1,55 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Webstore.Interfaces; +using Webstore.Interfaces.Services; +using WebStore.Client.Base; +using WebStore.Domain; +using WebStore.Domain.DTO.Products; + +namespace WebStore.Client.Products +{ + public class ProductsClient : BaseClient, IProductData + { + public ProductsClient(IConfiguration Configuration) : base(Configuration, WebAPI.Products) + { + } + + public IEnumerable GetBrands() + { + return Get>($"{Address}/brands"); + } + + public BrandDTO GetBrandsById(int id) + { + return Get($"{Address}/brands/{id}"); + } + + public ProductDTO GetProductById(int id) + { + return Get($"{Address}/{id}"); + } + + public IEnumerable GetProducts(ProductFilter Filter = null) + { + return Post(Address, Filter ?? new ProductFilter()) + .Content + .ReadAsAsync>() + .Result; + } + + public IEnumerable GetСategories() + { + return Get>($"{Address}/categories"); + } + + public CategoryDTO GetСategoriesById(int id) + { + return Get($"{Address}/categories/{id}"); + } + } +} diff --git a/Services/WebStore.Client/WebStore.Client.csproj b/Services/WebStore.Client/WebStore.Client.csproj index 5af1b39..d23926e 100644 --- a/Services/WebStore.Client/WebStore.Client.csproj +++ b/Services/WebStore.Client/WebStore.Client.csproj @@ -1,7 +1,7 @@ - netstandard2.1 + net5.0 diff --git a/Services/WebStore.ServiceHosting/Controllers/EmployeesApiController.cs b/Services/WebStore.ServiceHosting/Controllers/EmployeesApiController.cs new file mode 100644 index 0000000..9e91be0 --- /dev/null +++ b/Services/WebStore.ServiceHosting/Controllers/EmployeesApiController.cs @@ -0,0 +1,78 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Webstore.Interfaces.Services; +using WebStore.Domain.Models; + +namespace WebStore.ServiceHosting.Controllers +{ + [Route("api/employees")] + [ApiController] + public class EmployeesApiController : ControllerBase, IEmployeesData + { + private readonly IEmployeesData _db; + private readonly ILogger _Logger; + + public EmployeesApiController(IEmployeesData db, ILogger Logger) + { + _db = db; + _Logger = Logger; + } + + [HttpPost] + public int Add(Employee employee) + { + if (!ModelState.IsValid) + { + _Logger.LogWarning("Ошибка модели данных при добавлении нового сотрудника {0} {1}", + employee.LastName, employee.FirstName); + return 0; + } + _Logger.LogInformation("Добавление сотрудника {0} {1}", + employee.LastName, employee.FirstName); + var id = _db.Add(employee); + if (id > 0) + _Logger.LogInformation("Cотрудник [id:{0}] {1} {2} добавлен успешно", + employee.Id, employee.LastName, employee.FirstName); + else + _Logger.LogWarning("Ошибка при добавлении сотрудника {0} {1}", + employee.LastName, employee.FirstName); + return id; + } + + [HttpDelete("{id}")] + public bool Delete(int id) + { + + var result = _db.Delete(id); + if (result) + _Logger.LogInformation("Сотрудник с id:{0} успешно удалён", id); + else + _Logger.LogWarning("ошибка при попытке удаления сотрдуника с id:{0}", id); + + return result; + } + + [HttpGet] + public IEnumerable Get() + { + return _db.Get(); + } + + [HttpGet("{id}")] + public Employee Get(int id) + { + return _db.Get(id); + } + + [HttpPut] + public void Update(Employee employee) + { + _db.Update(employee); + } + } +} diff --git a/Services/WebStore.ServiceHosting/Controllers/OrdersApiController.cs b/Services/WebStore.ServiceHosting/Controllers/OrdersApiController.cs new file mode 100644 index 0000000..151c665 --- /dev/null +++ b/Services/WebStore.ServiceHosting/Controllers/OrdersApiController.cs @@ -0,0 +1,46 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Webstore.Interfaces; +using Webstore.Interfaces.Services; +using WebStore.Domain.DTO.Orders; +using WebStore.Domain.Entities.Orders; +using WebStore.Domain.ViewModels; + +namespace WebStore.ServiceHosting.Controllers +{ + [Route(WebAPI.Orders)] + [ApiController] + public class OrdersApiController : ControllerBase, IOrderService + { + private readonly IOrderService orderService; + private readonly ILogger logger; + + public OrdersApiController(IOrderService orderService, ILogger Logger) + { + this.orderService = orderService; + logger = Logger; + } + + [HttpPost("{UserName}")] + public async Task CreateOrder(string UserName, CreateOrderModel OrderModel) + { + return await orderService.CreateOrder(UserName, OrderModel); + } + + [HttpGet("{id}")] + public async Task GetOrderById(int id) + { + return await orderService.GetOrderById(id); + } + [HttpGet("user/{UserName}")] + public async Task> GetUserOrders(string UserName) + { + return await orderService.GetUserOrders(UserName); + } + } +} diff --git a/Services/WebStore.ServiceHosting/Controllers/ProductsApiController.cs b/Services/WebStore.ServiceHosting/Controllers/ProductsApiController.cs new file mode 100644 index 0000000..35251c8 --- /dev/null +++ b/Services/WebStore.ServiceHosting/Controllers/ProductsApiController.cs @@ -0,0 +1,65 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Webstore.Interfaces.Services; +using WebStore.Domain; +using WebStore.Domain.DTO.Products; +using WebStore.Domain.Entities; + +namespace WebStore.ServiceHosting.Controllers +{ + [Route("api/products")] + [ApiController] + public class ProductsApiController : ControllerBase, IProductData + { + private readonly IProductData _db; + private readonly ILogger _Logger; + + public ProductsApiController(IProductData db, ILogger Logger) + { + _db = db; + _Logger = Logger; + } + + [HttpGet("brands")] + public IEnumerable GetBrands() + { + return _db.GetBrands(); + } + + [HttpGet("brands/{id}")] + public BrandDTO GetBrandsById(int id) + { + throw new NotImplementedException(); + } + + [HttpGet("{id}")] + public ProductDTO GetProductById(int id) + { + return _db.GetProductById(id); + } + + + [HttpPost] + public IEnumerable GetProducts([FromBody]ProductFilter Filter = null) + { + return _db.GetProducts(Filter); + } + + [HttpGet("categories")] + public IEnumerable GetСategories() + { + return _db.GetСategories(); + } + + [HttpGet("categories/{id}")] + public CategoryDTO GetСategoriesById(int id) + { + throw new NotImplementedException(); + } + } +} diff --git a/Services/WebStore.ServiceHosting/Startup.cs b/Services/WebStore.ServiceHosting/Startup.cs index f4cb774..8071915 100644 --- a/Services/WebStore.ServiceHosting/Startup.cs +++ b/Services/WebStore.ServiceHosting/Startup.cs @@ -1,6 +1,8 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -10,6 +12,13 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Webstore.DAL.Context; +using Webstore.Interfaces.Services; +using Webstore.Services.Data; +using Webstore.Services.Products; +using Webstore.Services.Products.InCookies; +using Webstore.Services.Products.InSql; +using WebStore.Domain.Entities.Identity; namespace WebStore.ServiceHosting { @@ -17,14 +26,43 @@ public class Startup { public Startup(IConfiguration configuration) { - Configuration = configuration; + _Configuration = configuration; } - public IConfiguration Configuration { get; } + private readonly IConfiguration _Configuration; // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { + services.AddDbContext(opt => opt.UseSqlServer(_Configuration.GetConnectionString("Default"))); + services.AddTransient(); + + services.AddIdentity() + .AddEntityFrameworkStores() + .AddDefaultTokenProviders(); + + services.AddSingleton(); + services.AddTransient(); + services.AddScoped(); + services.AddScoped(); + + services.Configure(opt => + { +#if DEBUG + opt.Password.RequiredLength = 3; + opt.Password.RequireDigit = false; + opt.Password.RequireLowercase = false; + opt.Password.RequireUppercase = false; + opt.Password.RequireNonAlphanumeric = false; + opt.Password.RequiredUniqueChars = 3; +#endif + opt.User.RequireUniqueEmail = false; + opt.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + + opt.Lockout.AllowedForNewUsers = true; + opt.Lockout.MaxFailedAccessAttempts = 10; + opt.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(10); + }); services.AddControllers(); services.AddSwaggerGen(c => @@ -34,8 +72,9 @@ public void ConfigureServices(IServiceCollection services) } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, WebStoreDbInitializer db) { + db.Initialize(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); diff --git a/Services/WebStore.ServiceHosting/WebStore.ServiceHosting.csproj b/Services/WebStore.ServiceHosting/WebStore.ServiceHosting.csproj index 2884716..7169942 100644 --- a/Services/WebStore.ServiceHosting/WebStore.ServiceHosting.csproj +++ b/Services/WebStore.ServiceHosting/WebStore.ServiceHosting.csproj @@ -8,4 +8,11 @@ + + + + + + + diff --git a/Services/WebStore.ServiceHosting/appsettings.json b/Services/WebStore.ServiceHosting/appsettings.json index d9d9a9b..f455cbb 100644 --- a/Services/WebStore.ServiceHosting/appsettings.json +++ b/Services/WebStore.ServiceHosting/appsettings.json @@ -1,4 +1,7 @@ { + "ConnectionStrings": { + "Default": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WebStore-DB" + }, "Logging": { "LogLevel": { "Default": "Information", diff --git a/Services/Webstore.DAL/Webstore.DAL.csproj b/Services/Webstore.DAL/Webstore.DAL.csproj index be79c82..d57450a 100644 --- a/Services/Webstore.DAL/Webstore.DAL.csproj +++ b/Services/Webstore.DAL/Webstore.DAL.csproj @@ -1,7 +1,7 @@ - netstandard2.1 + net5.0 diff --git a/Services/Webstore.Interfaces/Services/IOrderService.cs b/Services/Webstore.Interfaces/Services/IOrderService.cs index 024e0c0..fba8b44 100644 --- a/Services/Webstore.Interfaces/Services/IOrderService.cs +++ b/Services/Webstore.Interfaces/Services/IOrderService.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; +using WebStore.Domain.DTO.Orders; using WebStore.Domain.Entities.Orders; using WebStore.Domain.ViewModels; @@ -7,10 +8,10 @@ namespace Webstore.Interfaces.Services { public interface IOrderService { - Task> GetUserOrders(string UserName); + Task> GetUserOrders(string UserName); - Task GetOrderById(int id); + Task GetOrderById(int id); - Task CreateOrder(string UserName, CartViewModel Cart, OrderViewModel OrderModel); + Task CreateOrder(string UserName, CreateOrderModel OrderModel); } } diff --git a/Services/Webstore.Interfaces/Services/IProductData.cs b/Services/Webstore.Interfaces/Services/IProductData.cs index 2c753ed..20811fd 100644 --- a/Services/Webstore.Interfaces/Services/IProductData.cs +++ b/Services/Webstore.Interfaces/Services/IProductData.cs @@ -1,17 +1,22 @@ using System.Collections.Generic; using WebStore.Domain; +using WebStore.Domain.DTO.Products; using WebStore.Domain.Entities; namespace Webstore.Interfaces.Services { public interface IProductData { - IEnumerable GetСategories(); + IEnumerable GetСategories(); - IEnumerable GetBrands(); + CategoryDTO GetСategoriesById(int id); - IEnumerable GetProducts(ProductFilter Filter = null); + IEnumerable GetBrands(); + + BrandDTO GetBrandsById(int id); - Product GetProductById(int id); + IEnumerable GetProducts(ProductFilter Filter = null); + + ProductDTO GetProductById(int id); } } diff --git a/Services/Webstore.Interfaces/WebAPI.cs b/Services/Webstore.Interfaces/WebAPI.cs new file mode 100644 index 0000000..dcc7edd --- /dev/null +++ b/Services/Webstore.Interfaces/WebAPI.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Webstore.Interfaces +{ + public static class WebAPI + { + public const string Products = "api/products"; + public const string Values = "api/values"; + public const string Employees = "api/employees"; + public const string Orders = "api/orders"; + } +} diff --git a/Services/Webstore.Interfaces/Webstore.Interfaces.csproj b/Services/Webstore.Interfaces/Webstore.Interfaces.csproj index aa3cb70..2f2fc48 100644 --- a/Services/Webstore.Interfaces/Webstore.Interfaces.csproj +++ b/Services/Webstore.Interfaces/Webstore.Interfaces.csproj @@ -1,7 +1,7 @@ - netstandard2.1 + net5.0 diff --git a/Services/Webstore.Services/Mapping/BrandMapper.cs b/Services/Webstore.Services/Mapping/BrandMapper.cs new file mode 100644 index 0000000..298a701 --- /dev/null +++ b/Services/Webstore.Services/Mapping/BrandMapper.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WebStore.Domain.DTO.Products; +using WebStore.Domain.Entities; + +namespace Webstore.Services.Mapping +{ + public static class BrandMapper + { + public static BrandDTO ToDTO(this Brand Brand) => Brand is null ? null + : new BrandDTO + { + Id = Brand.Id, + Name = Brand.Name, + Order = Brand.Order, + ProductsCount = Brand.Products.Count(), + }; + public static Brand FromDTO(this BrandDTO Brand) => Brand is null ? null + : new Brand + { + Id = Brand.Id, + Name = Brand.Name, + Order = Brand.Order + }; + public static IEnumerable ToDTO(this IEnumerable Brands) => Brands.Select(ToDTO); + + public static IEnumerable FromDTO(this IEnumerable Brands) => Brands.Select(FromDTO); + + } +} diff --git a/Services/Webstore.Services/Mapping/CategoryMapper.cs b/Services/Webstore.Services/Mapping/CategoryMapper.cs new file mode 100644 index 0000000..813a4d1 --- /dev/null +++ b/Services/Webstore.Services/Mapping/CategoryMapper.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WebStore.Domain.DTO.Products; +using WebStore.Domain.Entities; + +namespace Webstore.Services.Mapping +{ + public static class CategoryMapper + { + public static CategoryDTO ToDTO(this Category Category) => Category is null ? null + : new CategoryDTO + { + Id = Category.Id, + Name = Category.Name, + Order = Category.Order, + ParentId = Category.ParentId, + ProductsCount = Category.Products.Count() + }; + public static Category FromDTO(this CategoryDTO Category) => Category is null ? null + : new Category + { + Id = Category.Id, + Name = Category.Name, + Order = Category.Order, + ParentId = Category.ParentId + }; + public static IEnumerable ToDTO(this IEnumerable Categories) => Categories.Select(ToDTO); + + public static IEnumerable FromDTO(this IEnumerable Categories) => Categories.Select(FromDTO); + } +} diff --git a/Services/Webstore.Services/Mapping/OrderMapper.cs b/Services/Webstore.Services/Mapping/OrderMapper.cs new file mode 100644 index 0000000..e90ae88 --- /dev/null +++ b/Services/Webstore.Services/Mapping/OrderMapper.cs @@ -0,0 +1,38 @@ +using System.Linq; +using WebStore.Domain.DTO.Orders; +using WebStore.Domain.Entities.Orders; + +namespace WebStore.Services.Mapping +{ + public static class OrderMapper + { + public static OrderItemDTO ToDTO(this OrderItem Item) => Item is null + ? null + : new OrderItemDTO(Item.Id, Item.Price, Item.Quantity); + + public static OrderItem FromDTO(this OrderItemDTO Item) => Item is null + ? null + : new OrderItem + { + Id = Item.Id, + Price = Item.Price, + Quantity = Item.Quantity + }; + + public static OrderDTO ToDTO(this Order Order) => Order is null + ? null + : new OrderDTO(Order.Id, Order.Name, Order.Phone, Order.Address, Order.Date, Order.Items.Select(ToDTO)); + + public static Order FromDTO(this OrderDTO Order) => Order is null + ? null + : new Order + { + Id = Order.Id, + Name = Order.Name, + Phone = Order.Phone, + Address = Order.Address, + Date = Order.Date, + Items = Order.Items.Select(FromDTO).ToList() + }; + } +} diff --git a/Services/Webstore.Services/Mapping/ProductMapper.cs b/Services/Webstore.Services/Mapping/ProductMapper.cs index 9ecb34b..85af67a 100644 --- a/Services/Webstore.Services/Mapping/ProductMapper.cs +++ b/Services/Webstore.Services/Mapping/ProductMapper.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using WebStore.Domain.DTO.Products; using WebStore.Domain.Entities; using WebStore.Domain.ViewModels; @@ -21,9 +22,43 @@ public static class ProductMapper ImageUrl = p.ImageUrl, Name = p.Name, Order = p.Order, - Price = p.Price + Price = p.Price }; public static IEnumerable ToView(this IEnumerable p) => p.Select(ToView); + + public static ProductDTO ToDTO(this Product product) => product is null ? null + : new ProductDTO + { + Id = product.Id, + Name = product.Name, + Order = product.Order, + Price = product.Price, + ImageUrl = product.ImageUrl, + Brand = product.Brand.ToDTO(), + Category = product.Category.ToDTO(), + Description = product.Description, + Discount = product.Discount + }; + + public static Product FromDTO(this ProductDTO Product) => Product is null ? null + : new Product + { + Id = Product.Id, + Name = Product.Name, + Order = Product.Order, + Price = Product.Price, + ImageUrl = Product.ImageUrl, + Description = Product.Description, + Discount = Product.Discount, + Brand = Product.Brand.FromDTO(), + Category = Product.Category.FromDTO(), + BrandId = Product.Brand.Id, + CategoryId = Product.Category.Id + + }; + public static IEnumerable ToDTO(this IEnumerable products) => products.Select(ToDTO); + + public static IEnumerable FromDTO(this IEnumerable products) => products.Select(FromDTO); } } diff --git a/Services/Webstore.Services/Products/InCookies/InCookiesCartService.cs b/Services/Webstore.Services/Products/InCookies/InCookiesCartService.cs index dc77a4b..eb32380 100644 --- a/Services/Webstore.Services/Products/InCookies/InCookiesCartService.cs +++ b/Services/Webstore.Services/Products/InCookies/InCookiesCartService.cs @@ -113,7 +113,7 @@ public CartViewModel TransformFromCart() Ids = Cart.Items.Select(item => item.ProductId).ToArray() }); - var product_view_models = products.ToView().ToDictionary(p => p.Id); + var product_view_models = products.FromDTO().ToView().ToDictionary(p => p.Id); return new CartViewModel { diff --git a/Services/Webstore.Services/Products/InMemoryProductsData.cs b/Services/Webstore.Services/Products/InMemoryProductsData.cs index 885aca1..e042302 100644 --- a/Services/Webstore.Services/Products/InMemoryProductsData.cs +++ b/Services/Webstore.Services/Products/InMemoryProductsData.cs @@ -6,17 +6,19 @@ using WebStore.Domain; using WebStore.Domain.Entities; using Webstore.Services.Data; +using WebStore.Domain.DTO.Products; +using Webstore.Services.Mapping; namespace Webstore.Services.Products { [Obsolete("Класс устарел и не реализует необходимые методы")] public class InMemoryProductsData : IProductData { - public IEnumerable GetBrands() => TestDB.Brands; + public IEnumerable GetBrands() => TestDB.Brands.ToDTO(); - public IEnumerable GetСategories() => TestDB.Сategories; + public IEnumerable GetСategories() => TestDB.Сategories.ToDTO(); - public IEnumerable GetProducts(ProductFilter Filter = null) + public IEnumerable GetProducts(ProductFilter Filter = null) { var query = TestDB.Products; @@ -24,10 +26,20 @@ public IEnumerable GetProducts(ProductFilter Filter = null) query = query.Where(product => product.CategoryId == category_id).ToList(); if (Filter?.BrandId is { } brand_id) query = query.Where(product => product.BrandId == brand_id).ToList(); - return query; + return query.ToDTO(); } - public Product GetProductById(int id) + public ProductDTO GetProductById(int id) + { + throw new NotImplementedException(); + } + + public CategoryDTO GetСategoriesById(int id) + { + throw new NotImplementedException(); + } + + public BrandDTO GetBrandsById(int id) { throw new NotImplementedException(); } diff --git a/Services/Webstore.Services/Products/InSql/InSqIProductData.cs b/Services/Webstore.Services/Products/InSql/InSqIProductData.cs index 250a022..e8fc7cc 100644 --- a/Services/Webstore.Services/Products/InSql/InSqIProductData.cs +++ b/Services/Webstore.Services/Products/InSql/InSqIProductData.cs @@ -7,6 +7,8 @@ using Webstore.Interfaces.Services; using WebStore.Domain; using WebStore.Domain.Entities; +using Webstore.Services.Mapping; +using WebStore.Domain.DTO.Products; namespace Webstore.Services.Products.InSql { @@ -19,14 +21,19 @@ public InSqIProductData(WebStoreDB db) _db = db; } - public IEnumerable GetBrands() => _db.Brands.Include(brand => brand.Products); + public IEnumerable GetBrands() => _db.Brands.Include(brand => brand.Products).ToDTO(); - public Product GetProductById(int id) + public BrandDTO GetBrandsById(int id) { - return _db.Products.Include(p => p.Brand).Include(p => p.Category).FirstOrDefault(p => p.Id == id); + return _db.Brands.Include(brand => brand.Products).FirstOrDefault(brand => brand.Id == id).ToDTO(); } - public IEnumerable GetProducts(ProductFilter Filter = null) + public ProductDTO GetProductById(int id) + { + return _db.Products.Include(p => p.Brand).Include(p => p.Category).FirstOrDefault(p => p.Id == id).ToDTO(); + } + + public IEnumerable GetProducts(ProductFilter Filter = null) { IQueryable query = _db.Products.Include(p => p.Category).Include(p => p.Brand); @@ -43,9 +50,14 @@ public IEnumerable GetProducts(ProductFilter Filter = null) query = query.Where(product => product.CategoryId == Filter.СategoryId); } - return query; + return query.AsEnumerable().ToDTO(); } - public IEnumerable GetСategories() => _db.Categories.Include(category => category.Products); + public IEnumerable GetСategories() => _db.Categories.Include(category => category.Products).ToDTO(); + + public CategoryDTO GetСategoriesById(int id) + { + return _db.Categories.Include(category => category.Products).FirstOrDefault(category => category.Id == id).ToDTO(); + } } } diff --git a/Services/Webstore.Services/Products/InSql/SqlOrderService.cs b/Services/Webstore.Services/Products/InSql/SqlOrderService.cs index 30be498..9f5876d 100644 --- a/Services/Webstore.Services/Products/InSql/SqlOrderService.cs +++ b/Services/Webstore.Services/Products/InSql/SqlOrderService.cs @@ -7,9 +7,11 @@ using Microsoft.EntityFrameworkCore; using Webstore.DAL.Context; using Webstore.Interfaces.Services; +using WebStore.Domain.DTO.Orders; using WebStore.Domain.Entities.Identity; using WebStore.Domain.Entities.Orders; using WebStore.Domain.ViewModels; +using WebStore.Services.Mapping; namespace Webstore.Services.Products.InSql { @@ -24,18 +26,18 @@ public SqlOrderService(WebStoreDB db, UserManager UserManager) _UserManager = UserManager; } - public async Task> GetUserOrders(string UserName) => await _db.Orders + public async Task> GetUserOrders(string UserName) => (await _db.Orders .Include(order => order.User) .Include(order => order.Items) .Where(order => order.User.UserName == UserName) - .ToArrayAsync(); + .ToArrayAsync()).Select(o => o.ToDTO()); - public async Task GetOrderById(int id) => await _db.Orders + public async Task GetOrderById(int id) => (await _db.Orders .Include(order => order.User) .Include(order => order.Items) - .FirstOrDefaultAsync(order => order.Id == id); + .FirstOrDefaultAsync(order => order.Id == id)).ToDTO(); - public async Task CreateOrder(string UserName, CartViewModel Cart, OrderViewModel OrderModel) + public async Task CreateOrder(string UserName, CreateOrderModel OrderModel) { var user = await _UserManager.FindByNameAsync(UserName); if (user is null) @@ -45,32 +47,27 @@ public async Task CreateOrder(string UserName, CartViewModel Cart, OrderV var order = new Order { - Name = OrderModel.Name, - Address = OrderModel.Address, - Phone = OrderModel.Phone, + Name = OrderModel.Order.Name, + Address = OrderModel.Order.Address, + Phone = OrderModel.Order.Phone, User = user, Date = DateTime.Now, }; - var product_ids = Cart.Items.Select(i => i.Product.Id).ToArray(); - var cart_products = await _db.Products - .Where(p => product_ids.Contains(p.Id)) - .ToArrayAsync(); + foreach (var (id, _, quantity) in OrderModel.Items) + { + var product = await _db.Products.FindAsync(id); + if (product is null) continue; - var items = - from cart_item in Cart.Items - join product in cart_products - on cart_item.Product.Id equals product.Id - select new OrderItem + var order_item = new OrderItem { Order = order, Price = product.Price, - Quantity = cart_item.Quantity, + Quantity = quantity, Product = product }; - - foreach (var order_item in items) order.Items.Add(order_item); + } await _db.Orders.AddAsync(order); @@ -78,7 +75,7 @@ on cart_item.Product.Id equals product.Id await transaction.CommitAsync(); - return order; + return order.ToDTO(); } } } diff --git a/Services/Webstore.Services/Webstore.Services.csproj b/Services/Webstore.Services/Webstore.Services.csproj index 794bced..459e7cd 100644 --- a/Services/Webstore.Services/Webstore.Services.csproj +++ b/Services/Webstore.Services/Webstore.Services.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 diff --git a/UI/WebStore/Controllers/CartController.cs b/UI/WebStore/Controllers/CartController.cs index 6771fdc..58aa5d2 100644 --- a/UI/WebStore/Controllers/CartController.cs +++ b/UI/WebStore/Controllers/CartController.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading.Tasks; using Webstore.Interfaces.Services; +using WebStore.Domain.DTO.Orders; using WebStore.Domain.ViewModels; namespace WebStore.Controllers @@ -60,10 +61,18 @@ public async Task CheckOut(OrderViewModel OrderModel, [FromServic Order = OrderModel }); + var create_order_model = new CreateOrderModel( + OrderModel, + _CartService.TransformFromCart().Items + .Select(item => new OrderItemDTO( + item.Product.Id, + item.Product.Price, + item.Quantity)) + .ToList()); + var order = await OrderService.CreateOrder( User.Identity!.Name, - _CartService.TransformFromCart(), - OrderModel); + create_order_model); _CartService.Clear(); diff --git a/UI/WebStore/Controllers/ShopController.cs b/UI/WebStore/Controllers/ShopController.cs index 334b6bc..2a530af 100644 --- a/UI/WebStore/Controllers/ShopController.cs +++ b/UI/WebStore/Controllers/ShopController.cs @@ -5,7 +5,6 @@ using WebStore.Domain; using WebStore.Domain.ViewModels; using WebStore.Domain.Entities; -using WebStore.Domain.ViewModels; using Webstore.Interfaces.Services; using Webstore.Services.Mapping; @@ -24,9 +23,9 @@ public IActionResult Index(int? BrandId, int? CategoryID) СategoryId = CategoryID, }; - var productc = _ProductData.GetProducts(filter); - var brends = _ProductData.GetBrands(); - var categories = _ProductData.GetСategories(); + var productc = _ProductData.GetProducts(filter).FromDTO(); + var brends = _ProductData.GetBrands().FromDTO(); + var categories = _ProductData.GetСategories().FromDTO(); @@ -73,7 +72,7 @@ private IEnumerable Categories(IEnumerable Icateg } public IActionResult Product(int id) { - var product = _ProductData.GetProductById(id); + var product = _ProductData.GetProductById(id).FromDTO(); if (product is null) return NotFound(); return View(product.ToView()); diff --git a/UI/WebStore/Controllers/UserProfileController.cs b/UI/WebStore/Controllers/UserProfileController.cs new file mode 100644 index 0000000..57d0d57 --- /dev/null +++ b/UI/WebStore/Controllers/UserProfileController.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Webstore.Interfaces.Services; +using WebStore.Domain.ViewModels; + +namespace WebStore.Controllers +{ + [Authorize] + public class UserProfileController : Controller + { + public IActionResult Index() => View(); + + public async Task Orders([FromServices] IOrderService OrderService) + { + var orders = await OrderService.GetUserOrders(User.Identity!.Name); + + return View(orders.Select(order => new UserOrderViewModel + { + Id = order.Id, + Name = order.Name, + Phone = order.Phone, + Address = order.Address, + TotalSum = order.Items.Sum(item => item.Price * item.Quantity) + })); + } + } +} diff --git a/UI/WebStore/Startup.cs b/UI/WebStore/Startup.cs index de4f804..1a11475 100644 --- a/UI/WebStore/Startup.cs +++ b/UI/WebStore/Startup.cs @@ -19,6 +19,9 @@ using Webstore.Services.Products.InSql; using WebStore.Client.Values; using WebStore.Interfaces.TestAPI; +using WebStore.Client.Employees; +using WebStore.Client.Products; +using WebStore.Client.Orders; namespace WebStore { @@ -26,6 +29,7 @@ public class Startup { // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + private readonly IConfiguration _Configuration; public Startup(IConfiguration Configuration) => _Configuration = Configuration; @@ -70,13 +74,15 @@ public void ConfigureServices(IServiceCollection services) }); services.AddControllersWithViews().AddRazorRuntimeCompilation(); - services.AddSingleton(); + services.AddSingleton(); + //services.AddTransient(); - services.AddTransient(); - //InMemoryBlogsData: IBlogsData + //services.AddTransient(); + services.AddTransient(); services.AddSingleton(); services.AddScoped(); - services.AddScoped(); + //services.AddScoped(); + services.AddScoped(); services.AddScoped(); } diff --git a/UI/WebStore/Views/Shared/Components/UserInfo/UserInfo.cshtml b/UI/WebStore/Views/Shared/Components/UserInfo/UserInfo.cshtml index e4ffcc0..eaa69a3 100644 --- a/UI/WebStore/Views/Shared/Components/UserInfo/UserInfo.cshtml +++ b/UI/WebStore/Views/Shared/Components/UserInfo/UserInfo.cshtml @@ -1,5 +1,5 @@ @using WebStore.Domain.Entities.Identity - + @if (User.IsInRole(Role.Administrator)) { diff --git a/UI/WebStore/Views/UserProfile/Index.cshtml b/UI/WebStore/Views/UserProfile/Index.cshtml new file mode 100644 index 0000000..82d9a69 --- /dev/null +++ b/UI/WebStore/Views/UserProfile/Index.cshtml @@ -0,0 +1,8 @@ + +@{ + Layout = "_UserProfileLayout"; + ViewData["Title"] = "Профиль пользователя"; +} + +

Профиль пользователя

+ diff --git a/UI/WebStore/Views/UserProfile/Orders.cshtml b/UI/WebStore/Views/UserProfile/Orders.cshtml new file mode 100644 index 0000000..9dfa990 --- /dev/null +++ b/UI/WebStore/Views/UserProfile/Orders.cshtml @@ -0,0 +1,31 @@ +@model IEnumerable +@{ + Layout = "_UserProfileLayout"; + ViewData["Title"] = "Заказы"; +} + + + + + + + + + + + + + @{ var i = 1; } + @foreach (var order in Model) + { + + + + + + + + } + +
ИмяАдресТелефонСумма
@(i++)@order.Name@order.Address@order.Phone@order.TotalSum
+ diff --git a/UI/WebStore/Views/UserProfile/Partial/_Index_Scripts.cshtml b/UI/WebStore/Views/UserProfile/Partial/_Index_Scripts.cshtml new file mode 100644 index 0000000..f5b2aed --- /dev/null +++ b/UI/WebStore/Views/UserProfile/Partial/_Index_Scripts.cshtml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/UI/WebStore/Views/UserProfile/Partial/_Index_Styles.cshtml b/UI/WebStore/Views/UserProfile/Partial/_Index_Styles.cshtml new file mode 100644 index 0000000..40166d6 --- /dev/null +++ b/UI/WebStore/Views/UserProfile/Partial/_Index_Styles.cshtml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/UI/WebStore/Views/UserProfile/Partial/_UserProfileMenu.cshtml b/UI/WebStore/Views/UserProfile/Partial/_UserProfileMenu.cshtml new file mode 100644 index 0000000..f7dbebb --- /dev/null +++ b/UI/WebStore/Views/UserProfile/Partial/_UserProfileMenu.cshtml @@ -0,0 +1,15 @@ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/UI/WebStore/Views/UserProfile/_UserProfileLayout.cshtml b/UI/WebStore/Views/UserProfile/_UserProfileLayout.cshtml new file mode 100644 index 0000000..9da4ea5 --- /dev/null +++ b/UI/WebStore/Views/UserProfile/_UserProfileLayout.cshtml @@ -0,0 +1,20 @@ +@section Styles { + +} +@section Scripts { + +} +@{ + Layout = "_Layout"; +} + +
+
+
+ +
+
+ @RenderBody() +
+
+
\ No newline at end of file