feat : complete create and edit product , add file controller
add upload and get files in file controller , complete create and edit product with uploading imagesrelease
parent
1c780f9fac
commit
45a4791cd9
|
@ -0,0 +1,45 @@
|
||||||
|
using NetinaShop.Domain.Enums;
|
||||||
|
|
||||||
|
namespace NetinaShop.Api.Controller;
|
||||||
|
|
||||||
|
public class FileController : ICarterModule
|
||||||
|
{
|
||||||
|
public void AddRoutes(IEndpointRouteBuilder app)
|
||||||
|
{
|
||||||
|
var group = app.NewVersionedApi("File")
|
||||||
|
.MapGroup("api/file")
|
||||||
|
.RequireAuthorization(builder => builder.AddAuthenticationSchemes("Bearer").RequireAuthenticatedUser());
|
||||||
|
|
||||||
|
group.MapGet("", GetFilesAsync)
|
||||||
|
.WithDisplayName("GetFilesAsync")
|
||||||
|
.HasApiVersion(1.0);
|
||||||
|
|
||||||
|
group.MapPost("", UploadFileAsync)
|
||||||
|
.WithDisplayName("UploadFileAsync")
|
||||||
|
.HasApiVersion(1.0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IResult> GetFilesAsync([FromQuery]StorageFileType? fileType,[FromServices] IStorageService storageService, CancellationToken cancellationToken)
|
||||||
|
=> TypedResults.Ok(await storageService.GetStorageFiles(fileType: fileType ?? StorageFileType.Image));
|
||||||
|
public async Task<IResult> UploadFileAsync([FromBody] FileUploadRequest uploadRequest, [FromServices] IStorageService storageService, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var bytes = Convert.FromBase64String(uploadRequest.StringBaseFile);
|
||||||
|
using var originalMedFileStream = new MemoryStream(bytes);
|
||||||
|
using var originalThumbFileStream = new MemoryStream(bytes);
|
||||||
|
using var thumbnailFileStream = new MemoryStream();
|
||||||
|
using var mediumFileStream = new MemoryStream();
|
||||||
|
|
||||||
|
await uploadRequest.ImageResize(originalMedFileStream, mediumFileStream, 1280);
|
||||||
|
await uploadRequest.ImageResize(originalThumbFileStream, thumbnailFileStream, 200);
|
||||||
|
var medFileName = await storageService.UploadObjectFromFileAsync(uploadRequest.FileName, $"{uploadRequest.FileUploadType.ToDisplay()}/Med", uploadRequest.ContentType, mediumFileStream);
|
||||||
|
await storageService.UploadObjectFromFileAsync(uploadRequest.FileName, $"{uploadRequest.FileUploadType.ToDisplay()}/Thumb", uploadRequest.ContentType, thumbnailFileStream);
|
||||||
|
var response = new FileUploadResponse
|
||||||
|
{
|
||||||
|
FileName = medFileName,
|
||||||
|
FileLocation = $"{uploadRequest.FileUploadType.ToDisplay()}/Med/{medFileName}",
|
||||||
|
FileUrl = $"https://storage.vesmook.com/{uploadRequest.FileUploadType.ToDisplay()}/Med/{medFileName}"
|
||||||
|
};
|
||||||
|
return TypedResults.Ok(response);
|
||||||
|
}
|
||||||
|
}
|
|
@ -52,7 +52,7 @@
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.12" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.12" />
|
||||||
<PackageReference Include="System.Data.SqlClient" Version="4.8.5" />
|
<PackageReference Include="System.Data.SqlClient" Version="4.8.6" />
|
||||||
<PackageReference Include="System.Drawing.Common" Version="8.0.0" />
|
<PackageReference Include="System.Drawing.Common" Version="8.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -74,8 +74,10 @@
|
||||||
<Using Include="NetinaShop.Common.Models.Exception" />
|
<Using Include="NetinaShop.Common.Models.Exception" />
|
||||||
<Using Include="NetinaShop.Common.Models.Mapper" />
|
<Using Include="NetinaShop.Common.Models.Mapper" />
|
||||||
<Using Include="NetinaShop.Core" />
|
<Using Include="NetinaShop.Core" />
|
||||||
|
<Using Include="NetinaShop.Core.Abstracts" />
|
||||||
<Using Include="NetinaShop.Core.CoreServices.Abstracts" />
|
<Using Include="NetinaShop.Core.CoreServices.Abstracts" />
|
||||||
<Using Include="NetinaShop.Core.Models.Api" />
|
<Using Include="NetinaShop.Core.Models.Api" />
|
||||||
|
<Using Include="NetinaShop.Core.Utilities" />
|
||||||
<Using Include="NetinaShop.Domain" />
|
<Using Include="NetinaShop.Domain" />
|
||||||
<Using Include="NetinaShop.Domain.CommandQueries.Commands" />
|
<Using Include="NetinaShop.Domain.CommandQueries.Commands" />
|
||||||
<Using Include="NetinaShop.Domain.CommandQueries.Queries" />
|
<Using Include="NetinaShop.Domain.CommandQueries.Queries" />
|
||||||
|
|
|
@ -2,14 +2,18 @@
|
||||||
{
|
{
|
||||||
public enum FileUploadType
|
public enum FileUploadType
|
||||||
{
|
{
|
||||||
|
[Display(Name = "Images")]
|
||||||
|
Image,
|
||||||
|
[Display(Name = "Handouts")]
|
||||||
Handout,
|
Handout,
|
||||||
|
[Display(Name = "Videos")]
|
||||||
Video,
|
Video,
|
||||||
Image
|
|
||||||
}
|
}
|
||||||
public class FileUploadRequest
|
public class FileUploadRequest
|
||||||
{
|
{
|
||||||
public string StringBaseFile { get; set; }
|
public string StringBaseFile { get; set; }
|
||||||
public string FileName { get; set; }
|
public string FileName { get; set; }
|
||||||
|
public string ContentType { get; set; }
|
||||||
public FileUploadType FileUploadType { get; set; }
|
public FileUploadType FileUploadType { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace NetinaShop.Common.Models.Api;
|
||||||
|
|
||||||
|
public class FileUploadResponse
|
||||||
|
{
|
||||||
|
public string FileUrl { get; set; }
|
||||||
|
public string FileLocation { get; set; }
|
||||||
|
public string FileName { get; set; }
|
||||||
|
public bool IsOriginal { get; set; } = true;
|
||||||
|
}
|
|
@ -24,7 +24,7 @@
|
||||||
<PackageReference Include="MD.PersianDateTime.Standard" Version="2.5.0" />
|
<PackageReference Include="MD.PersianDateTime.Standard" Version="2.5.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.3" />
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace NetinaShop.Core.Abstracts;
|
||||||
|
|
||||||
|
public interface IStorageService : IScopedDependency
|
||||||
|
{
|
||||||
|
Task<string> UploadObjectFromFileAsync(string fileName, string filePath, string contentType, byte[] fileBytes);
|
||||||
|
Task<string> UploadObjectFromFileAsync(string fileName, string filePath, string contentType, Stream fileStream);
|
||||||
|
|
||||||
|
Task<List<StorageFileSDto>> GetStorageFiles(StorageFileType fileType);
|
||||||
|
}
|
|
@ -12,6 +12,7 @@
|
||||||
<PackageReference Include="AspNetCoreRateLimit.Redis" Version="2.0.0" />
|
<PackageReference Include="AspNetCoreRateLimit.Redis" Version="2.0.0" />
|
||||||
<PackageReference Include="Autofac.Extras.Quartz" Version="9.0.0" />
|
<PackageReference Include="Autofac.Extras.Quartz" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
||||||
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.0" />
|
||||||
<PackageReference Include="Quartz" Version="3.8.0" />
|
<PackageReference Include="Quartz" Version="3.8.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -22,10 +23,10 @@
|
||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Abstracts\" />
|
|
||||||
<Folder Include="BaseServices\Abstracts\" />
|
<Folder Include="BaseServices\Abstracts\" />
|
||||||
<Folder Include="EntityServices\ReviewHandlers\" />
|
<Folder Include="EntityServices\ReviewHandlers\" />
|
||||||
<Folder Include="Models\Api\" />
|
<Folder Include="Models\Api\" />
|
||||||
|
<Folder Include="Utilities\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
|
using SixLabors.ImageSharp.Processing;
|
||||||
|
|
||||||
|
namespace NetinaShop.Core.Utilities;
|
||||||
|
|
||||||
|
public static class ImageConvertor
|
||||||
|
{
|
||||||
|
public static async Task<Stream> ImageResize(this FileUploadRequest fileUpload, Stream input, Stream output, int newWidth)
|
||||||
|
{
|
||||||
|
using var image = await Image.LoadAsync(input);
|
||||||
|
var height_width = image.Height / image.Width;
|
||||||
|
var new_Height = newWidth * height_width;
|
||||||
|
image.Mutate(x => x.Resize(newWidth, new_Height));
|
||||||
|
image.Mutate(x => x.Resize(newWidth, new_Height));
|
||||||
|
await image.SaveAsJpegAsync(output);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,8 +8,15 @@ public class ProductLDto : BaseDto<ProductLDto,Product>
|
||||||
public string ExpertCheck { get; set; } = string.Empty;
|
public string ExpertCheck { get; set; } = string.Empty;
|
||||||
public string Tags { get; set; } = string.Empty;
|
public string Tags { get; set; } = string.Empty;
|
||||||
public string Warranty { get; set; } = string.Empty;
|
public string Warranty { get; set; } = string.Empty;
|
||||||
|
public bool BeDisplayed { get; set; }
|
||||||
|
public bool HasExpressDelivery { get; set; }
|
||||||
|
public int MaxOrderCount { get; set; }
|
||||||
|
public double Cost { get; set; }
|
||||||
|
public double PackingCost { get; set; }
|
||||||
public Guid BrandId { get; set; }
|
public Guid BrandId { get; set; }
|
||||||
public string BrandNames { get; set; } = string.Empty;
|
public string BrandName { get; set; } = string.Empty;
|
||||||
|
public Guid CategoryId { get; set; }
|
||||||
|
public string CategoryName { get; set; } = string.Empty;
|
||||||
|
|
||||||
public List<SpecificationSDto> Specifications { get; set; } = new();
|
public List<SpecificationSDto> Specifications { get; set; } = new();
|
||||||
public List<ReviewSDto> Reviews { get; set; } = new();
|
public List<ReviewSDto> Reviews { get; set; } = new();
|
||||||
|
|
|
@ -1,11 +1,26 @@
|
||||||
namespace NetinaShop.Domain.Dtos.SmallDtos;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
|
namespace NetinaShop.Domain.Dtos.SmallDtos;
|
||||||
|
|
||||||
public class StorageFileSDto : BaseDto<StorageFileSDto , StorageFile>
|
public class StorageFileSDto : BaseDto<StorageFileSDto , StorageFile>
|
||||||
{
|
{
|
||||||
public string Name { get; internal set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
public string FileLocation { get; internal set; } = string.Empty;
|
public string FileLocation { get; set; } = string.Empty;
|
||||||
public string FileName { get; internal set; } = string.Empty;
|
public string FileName { get; set; } = string.Empty;
|
||||||
public bool IsHeader { get; internal set; }
|
public bool IsHeader { get; set; }
|
||||||
public bool IsPrimary { get; internal set; }
|
public bool IsPrimary { get; set; }
|
||||||
public StorageFileType FileType { get; internal set; }
|
public StorageFileType FileType { get; set; }
|
||||||
|
public bool Selected { get; set; }
|
||||||
|
|
||||||
|
public DateTime CreatedAt
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string date = FileName.Split('.').First().Split('_').Last();
|
||||||
|
if (!date.IsNullOrEmpty() && long.TryParse(date, out long longDate))
|
||||||
|
return DateTimeExtensions.UnixTimeStampToDateTime(longDate);
|
||||||
|
return DateTime.MinValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -36,7 +36,7 @@ public partial class Product
|
||||||
|
|
||||||
public Specification AddSpecification(string title, string detail,string value, bool isFeature, Guid parentId)
|
public Specification AddSpecification(string title, string detail,string value, bool isFeature, Guid parentId)
|
||||||
{
|
{
|
||||||
var ent = Specification.Create(title, detail, value, isFeature, Id, parentId);
|
var ent = Specification.Create(title, value, detail, isFeature, Id, parentId);
|
||||||
Specifications.Add(ent);
|
Specifications.Add(ent);
|
||||||
return ent;
|
return ent;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,8 +53,8 @@ public partial class Product : ApiEntity
|
||||||
public float Rate { get; internal set; }
|
public float Rate { get; internal set; }
|
||||||
public int ReviewCount { get; internal set; }
|
public int ReviewCount { get; internal set; }
|
||||||
public int Viewed { get; internal set; }
|
public int Viewed { get; internal set; }
|
||||||
public bool HasExpressDelivery { get; set; }
|
public bool HasExpressDelivery { get; internal set; }
|
||||||
public int MaxOrderCount { get; set; }
|
public int MaxOrderCount { get; internal set; }
|
||||||
|
|
||||||
|
|
||||||
public Guid BrandId { get; internal set; }
|
public Guid BrandId { get; internal set; }
|
||||||
|
|
|
@ -7,17 +7,20 @@ public partial class Specification : ApiEntity
|
||||||
{
|
{
|
||||||
public Specification()
|
public Specification()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Specification(string title, string detail,string value, bool isFeature, Guid productId, Guid parentId)
|
public Specification(string title, string detail, string value, bool isFeature, Guid productId, Guid parentId)
|
||||||
{
|
{
|
||||||
Title = title;
|
Title = title;
|
||||||
Detail = detail;
|
Detail = detail;
|
||||||
Value = value;
|
Value = value;
|
||||||
IsFeature = isFeature;
|
IsFeature = isFeature;
|
||||||
ProductId = productId;
|
ProductId = productId;
|
||||||
ParentId = parentId;
|
if (parentId != default)
|
||||||
|
ParentId = parentId;
|
||||||
|
else
|
||||||
|
ParentId = null;
|
||||||
}
|
}
|
||||||
public string Title { get; internal set; } = string.Empty;
|
public string Title { get; internal set; } = string.Empty;
|
||||||
public string Detail { get; internal set; } = string.Empty;
|
public string Detail { get; internal set; } = string.Empty;
|
||||||
|
@ -27,7 +30,7 @@ public partial class Specification : ApiEntity
|
||||||
public Guid ProductId { get; internal set; }
|
public Guid ProductId { get; internal set; }
|
||||||
public Product? Product { get; internal set; }
|
public Product? Product { get; internal set; }
|
||||||
|
|
||||||
public Guid ParentId { get; internal set; }
|
public Guid? ParentId { get; internal set; }
|
||||||
public Specification? Parent { get; internal set; }
|
public Specification? Parent { get; internal set; }
|
||||||
|
|
||||||
public List<Specification> Children { get; internal set; } = new();
|
public List<Specification> Children { get; internal set; } = new();
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
public enum StorageFileType
|
public enum StorageFileType
|
||||||
{
|
{
|
||||||
|
[Display(Name = "Images")]
|
||||||
Image,
|
Image,
|
||||||
|
[Display(Name = "Videos")]
|
||||||
Video
|
Video
|
||||||
}
|
}
|
|
@ -1,6 +0,0 @@
|
||||||
namespace NetinaShop.Domain.Mappers
|
|
||||||
{
|
|
||||||
public static partial class CategoryStorageFileMapper
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,7 +20,13 @@ namespace NetinaShop.Domain.Mappers
|
||||||
ExpertCheck = p1.ExpertCheck,
|
ExpertCheck = p1.ExpertCheck,
|
||||||
Tags = p1.Tags,
|
Tags = p1.Tags,
|
||||||
Warranty = p1.Warranty,
|
Warranty = p1.Warranty,
|
||||||
|
Cost = p1.Cost,
|
||||||
|
BeDisplayed = p1.BeDisplayed,
|
||||||
|
PackingCost = p1.PackingCost,
|
||||||
|
HasExpressDelivery = p1.HasExpressDelivery,
|
||||||
|
MaxOrderCount = p1.MaxOrderCount,
|
||||||
BrandId = p1.BrandId,
|
BrandId = p1.BrandId,
|
||||||
|
CategoryId = p1.CategoryId,
|
||||||
Specifications = funcMain1(p1.Specifications),
|
Specifications = funcMain1(p1.Specifications),
|
||||||
Reviews = funcMain2(p1.Reviews),
|
Reviews = funcMain2(p1.Reviews),
|
||||||
Files = funcMain3(p1.Files),
|
Files = funcMain3(p1.Files),
|
||||||
|
@ -41,7 +47,13 @@ namespace NetinaShop.Domain.Mappers
|
||||||
result.ExpertCheck = p5.ExpertCheck;
|
result.ExpertCheck = p5.ExpertCheck;
|
||||||
result.Tags = p5.Tags;
|
result.Tags = p5.Tags;
|
||||||
result.Warranty = p5.Warranty;
|
result.Warranty = p5.Warranty;
|
||||||
|
result.Cost = p5.Cost;
|
||||||
|
result.BeDisplayed = p5.BeDisplayed;
|
||||||
|
result.PackingCost = p5.PackingCost;
|
||||||
|
result.HasExpressDelivery = p5.HasExpressDelivery;
|
||||||
|
result.MaxOrderCount = p5.MaxOrderCount;
|
||||||
result.BrandId = p5.BrandId;
|
result.BrandId = p5.BrandId;
|
||||||
|
result.CategoryId = p5.CategoryId;
|
||||||
result.Specifications = funcMain4(p5.Specifications, result.Specifications);
|
result.Specifications = funcMain4(p5.Specifications, result.Specifications);
|
||||||
result.Reviews = funcMain5(p5.Reviews, result.Reviews);
|
result.Reviews = funcMain5(p5.Reviews, result.Reviews);
|
||||||
result.Files = funcMain6(p5.Files, result.Files);
|
result.Files = funcMain6(p5.Files, result.Files);
|
||||||
|
@ -57,7 +69,13 @@ namespace NetinaShop.Domain.Mappers
|
||||||
ExpertCheck = p13.ExpertCheck,
|
ExpertCheck = p13.ExpertCheck,
|
||||||
Tags = p13.Tags,
|
Tags = p13.Tags,
|
||||||
Warranty = p13.Warranty,
|
Warranty = p13.Warranty,
|
||||||
|
Cost = p13.Cost,
|
||||||
|
BeDisplayed = p13.BeDisplayed,
|
||||||
|
PackingCost = p13.PackingCost,
|
||||||
|
HasExpressDelivery = p13.HasExpressDelivery,
|
||||||
|
MaxOrderCount = p13.MaxOrderCount,
|
||||||
BrandId = p13.BrandId,
|
BrandId = p13.BrandId,
|
||||||
|
CategoryId = p13.CategoryId,
|
||||||
Specifications = p13.Specifications.Select<SpecificationSDto, Specification>(p14 => new Specification()
|
Specifications = p13.Specifications.Select<SpecificationSDto, Specification>(p14 => new Specification()
|
||||||
{
|
{
|
||||||
Title = p14.Title,
|
Title = p14.Title,
|
||||||
|
@ -65,7 +83,7 @@ namespace NetinaShop.Domain.Mappers
|
||||||
Value = p14.Value,
|
Value = p14.Value,
|
||||||
IsFeature = p14.IsFeature,
|
IsFeature = p14.IsFeature,
|
||||||
ProductId = p14.ProductId,
|
ProductId = p14.ProductId,
|
||||||
ParentId = p14.ParentId,
|
ParentId = (Guid?)p14.ParentId,
|
||||||
Id = p14.Id
|
Id = p14.Id
|
||||||
}).ToList<Specification>(),
|
}).ToList<Specification>(),
|
||||||
Reviews = p13.Reviews.Select<ReviewSDto, Review>(p15 => new Review()
|
Reviews = p13.Reviews.Select<ReviewSDto, Review>(p15 => new Review()
|
||||||
|
@ -100,7 +118,15 @@ namespace NetinaShop.Domain.Mappers
|
||||||
ExpertCheck = p17.ExpertCheck,
|
ExpertCheck = p17.ExpertCheck,
|
||||||
Tags = p17.Tags,
|
Tags = p17.Tags,
|
||||||
Warranty = p17.Warranty,
|
Warranty = p17.Warranty,
|
||||||
|
BeDisplayed = p17.BeDisplayed,
|
||||||
|
HasExpressDelivery = p17.HasExpressDelivery,
|
||||||
|
MaxOrderCount = p17.MaxOrderCount,
|
||||||
|
Cost = p17.Cost,
|
||||||
|
PackingCost = p17.PackingCost,
|
||||||
BrandId = p17.BrandId,
|
BrandId = p17.BrandId,
|
||||||
|
BrandName = p17.Brand == null ? null : p17.Brand.Name,
|
||||||
|
CategoryId = p17.CategoryId,
|
||||||
|
CategoryName = p17.Category == null ? null : p17.Category.Name,
|
||||||
Specifications = funcMain7(p17.Specifications),
|
Specifications = funcMain7(p17.Specifications),
|
||||||
Reviews = funcMain8(p17.Reviews),
|
Reviews = funcMain8(p17.Reviews),
|
||||||
Files = funcMain9(p17.Files),
|
Files = funcMain9(p17.Files),
|
||||||
|
@ -121,7 +147,15 @@ namespace NetinaShop.Domain.Mappers
|
||||||
result.ExpertCheck = p21.ExpertCheck;
|
result.ExpertCheck = p21.ExpertCheck;
|
||||||
result.Tags = p21.Tags;
|
result.Tags = p21.Tags;
|
||||||
result.Warranty = p21.Warranty;
|
result.Warranty = p21.Warranty;
|
||||||
|
result.BeDisplayed = p21.BeDisplayed;
|
||||||
|
result.HasExpressDelivery = p21.HasExpressDelivery;
|
||||||
|
result.MaxOrderCount = p21.MaxOrderCount;
|
||||||
|
result.Cost = p21.Cost;
|
||||||
|
result.PackingCost = p21.PackingCost;
|
||||||
result.BrandId = p21.BrandId;
|
result.BrandId = p21.BrandId;
|
||||||
|
result.BrandName = p21.Brand == null ? null : p21.Brand.Name;
|
||||||
|
result.CategoryId = p21.CategoryId;
|
||||||
|
result.CategoryName = p21.Category == null ? null : p21.Category.Name;
|
||||||
result.Specifications = funcMain10(p21.Specifications, result.Specifications);
|
result.Specifications = funcMain10(p21.Specifications, result.Specifications);
|
||||||
result.Reviews = funcMain11(p21.Reviews, result.Reviews);
|
result.Reviews = funcMain11(p21.Reviews, result.Reviews);
|
||||||
result.Files = funcMain12(p21.Files, result.Files);
|
result.Files = funcMain12(p21.Files, result.Files);
|
||||||
|
@ -137,7 +171,15 @@ namespace NetinaShop.Domain.Mappers
|
||||||
ExpertCheck = p29.ExpertCheck,
|
ExpertCheck = p29.ExpertCheck,
|
||||||
Tags = p29.Tags,
|
Tags = p29.Tags,
|
||||||
Warranty = p29.Warranty,
|
Warranty = p29.Warranty,
|
||||||
|
BeDisplayed = p29.BeDisplayed,
|
||||||
|
HasExpressDelivery = p29.HasExpressDelivery,
|
||||||
|
MaxOrderCount = p29.MaxOrderCount,
|
||||||
|
Cost = p29.Cost,
|
||||||
|
PackingCost = p29.PackingCost,
|
||||||
BrandId = p29.BrandId,
|
BrandId = p29.BrandId,
|
||||||
|
BrandName = p29.Brand.Name,
|
||||||
|
CategoryId = p29.CategoryId,
|
||||||
|
CategoryName = p29.Category.Name,
|
||||||
Specifications = p29.Specifications.Select<Specification, SpecificationSDto>(p30 => new SpecificationSDto()
|
Specifications = p29.Specifications.Select<Specification, SpecificationSDto>(p30 => new SpecificationSDto()
|
||||||
{
|
{
|
||||||
Title = p30.Title,
|
Title = p30.Title,
|
||||||
|
@ -145,7 +187,7 @@ namespace NetinaShop.Domain.Mappers
|
||||||
Value = p30.Value,
|
Value = p30.Value,
|
||||||
IsFeature = p30.IsFeature,
|
IsFeature = p30.IsFeature,
|
||||||
ProductId = p30.ProductId,
|
ProductId = p30.ProductId,
|
||||||
ParentId = p30.ParentId,
|
ParentId = p30.ParentId == null ? default(Guid) : (Guid)p30.ParentId,
|
||||||
Id = p30.Id
|
Id = p30.Id
|
||||||
}).ToList<SpecificationSDto>(),
|
}).ToList<SpecificationSDto>(),
|
||||||
Reviews = p29.Reviews.Select<Review, ReviewSDto>(p31 => new ReviewSDto()
|
Reviews = p29.Reviews.Select<Review, ReviewSDto>(p31 => new ReviewSDto()
|
||||||
|
@ -312,7 +354,7 @@ namespace NetinaShop.Domain.Mappers
|
||||||
Value = item.Value,
|
Value = item.Value,
|
||||||
IsFeature = item.IsFeature,
|
IsFeature = item.IsFeature,
|
||||||
ProductId = item.ProductId,
|
ProductId = item.ProductId,
|
||||||
ParentId = item.ParentId,
|
ParentId = (Guid?)item.ParentId,
|
||||||
Id = item.Id
|
Id = item.Id
|
||||||
});
|
});
|
||||||
i++;
|
i++;
|
||||||
|
@ -402,7 +444,7 @@ namespace NetinaShop.Domain.Mappers
|
||||||
Value = item.Value,
|
Value = item.Value,
|
||||||
IsFeature = item.IsFeature,
|
IsFeature = item.IsFeature,
|
||||||
ProductId = item.ProductId,
|
ProductId = item.ProductId,
|
||||||
ParentId = item.ParentId,
|
ParentId = (Guid?)item.ParentId,
|
||||||
Id = item.Id
|
Id = item.Id
|
||||||
});
|
});
|
||||||
i++;
|
i++;
|
||||||
|
@ -492,7 +534,7 @@ namespace NetinaShop.Domain.Mappers
|
||||||
Value = item.Value,
|
Value = item.Value,
|
||||||
IsFeature = item.IsFeature,
|
IsFeature = item.IsFeature,
|
||||||
ProductId = item.ProductId,
|
ProductId = item.ProductId,
|
||||||
ParentId = item.ParentId,
|
ParentId = item.ParentId == null ? default(Guid) : (Guid)item.ParentId,
|
||||||
Id = item.Id
|
Id = item.Id
|
||||||
});
|
});
|
||||||
i++;
|
i++;
|
||||||
|
@ -582,7 +624,7 @@ namespace NetinaShop.Domain.Mappers
|
||||||
Value = item.Value,
|
Value = item.Value,
|
||||||
IsFeature = item.IsFeature,
|
IsFeature = item.IsFeature,
|
||||||
ProductId = item.ProductId,
|
ProductId = item.ProductId,
|
||||||
ParentId = item.ParentId,
|
ParentId = item.ParentId == null ? default(Guid) : (Guid)item.ParentId,
|
||||||
Id = item.Id
|
Id = item.Id
|
||||||
});
|
});
|
||||||
i++;
|
i++;
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace NetinaShop.Domain.Mappers
|
||||||
Value = p1.Value,
|
Value = p1.Value,
|
||||||
IsFeature = p1.IsFeature,
|
IsFeature = p1.IsFeature,
|
||||||
ProductId = p1.ProductId,
|
ProductId = p1.ProductId,
|
||||||
ParentId = p1.ParentId,
|
ParentId = (Guid?)p1.ParentId,
|
||||||
Id = p1.Id
|
Id = p1.Id
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ namespace NetinaShop.Domain.Mappers
|
||||||
result.Value = p2.Value;
|
result.Value = p2.Value;
|
||||||
result.IsFeature = p2.IsFeature;
|
result.IsFeature = p2.IsFeature;
|
||||||
result.ProductId = p2.ProductId;
|
result.ProductId = p2.ProductId;
|
||||||
result.ParentId = p2.ParentId;
|
result.ParentId = (Guid?)p2.ParentId;
|
||||||
result.Id = p2.Id;
|
result.Id = p2.Id;
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ namespace NetinaShop.Domain.Mappers
|
||||||
Value = p4.Value,
|
Value = p4.Value,
|
||||||
IsFeature = p4.IsFeature,
|
IsFeature = p4.IsFeature,
|
||||||
ProductId = p4.ProductId,
|
ProductId = p4.ProductId,
|
||||||
ParentId = p4.ParentId,
|
ParentId = p4.ParentId == null ? default(Guid) : (Guid)p4.ParentId,
|
||||||
Id = p4.Id
|
Id = p4.Id
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ namespace NetinaShop.Domain.Mappers
|
||||||
result.Value = p5.Value;
|
result.Value = p5.Value;
|
||||||
result.IsFeature = p5.IsFeature;
|
result.IsFeature = p5.IsFeature;
|
||||||
result.ProductId = p5.ProductId;
|
result.ProductId = p5.ProductId;
|
||||||
result.ParentId = p5.ParentId;
|
result.ParentId = p5.ParentId == null ? default(Guid) : (Guid)p5.ParentId;
|
||||||
result.Id = p5.Id;
|
result.Id = p5.Id;
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ namespace NetinaShop.Domain.Mappers
|
||||||
Value = p7.Value,
|
Value = p7.Value,
|
||||||
IsFeature = p7.IsFeature,
|
IsFeature = p7.IsFeature,
|
||||||
ProductId = p7.ProductId,
|
ProductId = p7.ProductId,
|
||||||
ParentId = p7.ParentId,
|
ParentId = p7.ParentId == null ? default(Guid) : (Guid)p7.ParentId,
|
||||||
Id = p7.Id
|
Id = p7.Id
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AWSSDK.S3" Version="3.7.305.17" />
|
||||||
<PackageReference Include="Refit" Version="7.0.0" />
|
<PackageReference Include="Refit" Version="7.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -19,14 +20,16 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Models\" />
|
<Folder Include="Models\" />
|
||||||
<Folder Include="Services\" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Using Include="Amazon.S3" />
|
||||||
|
<Using Include="Amazon.S3.Model" />
|
||||||
<Using Include="Microsoft.Extensions.Hosting" />
|
<Using Include="Microsoft.Extensions.Hosting" />
|
||||||
<Using Include="Microsoft.Extensions.Logging" />
|
<Using Include="Microsoft.Extensions.Logging" />
|
||||||
<Using Include="Microsoft.Extensions.Options" />
|
<Using Include="Microsoft.Extensions.Options" />
|
||||||
|
<Using Include="NetinaShop.Common.Extensions" />
|
||||||
<Using Include="NetinaShop.Common.Models" />
|
<Using Include="NetinaShop.Common.Models" />
|
||||||
<Using Include="NetinaShop.Common.Models.Api" />
|
<Using Include="NetinaShop.Common.Models.Api" />
|
||||||
<Using Include="NetinaShop.Common.Models.Exception" />
|
<Using Include="NetinaShop.Common.Models.Exception" />
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
using NetinaShop.Domain.Dtos.SmallDtos;
|
||||||
|
using NetinaShop.Domain.Enums;
|
||||||
|
|
||||||
|
namespace NetinaShop.Infrastructure.Services;
|
||||||
|
|
||||||
|
public class StorageService : IStorageService
|
||||||
|
{
|
||||||
|
private IAmazonS3? _s3Client;
|
||||||
|
private string _bucketName = "vesmeh-content";
|
||||||
|
private IAmazonS3 GetClientAsync()
|
||||||
|
{
|
||||||
|
var awsCredentials = new Amazon.Runtime.BasicAWSCredentials("979313b7-30fb-40ff-94d8-d0390d3fa876", "d37a1cc6acfea3a6f92c538ef0f6601f1edcdc9143942b6470e5d1032aa6bfe2");
|
||||||
|
var config = new AmazonS3Config { ServiceURL = "https://s3.ir-thr-at1.arvanstorage.ir" };
|
||||||
|
_s3Client = new AmazonS3Client(awsCredentials, config);
|
||||||
|
return _s3Client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> UploadObjectFromFileAsync(string fileName, string filePath, string contentType, byte[] fileBytes)
|
||||||
|
{
|
||||||
|
using var memorySteam = new MemoryStream(fileBytes);
|
||||||
|
|
||||||
|
var client = GetClientAsync();
|
||||||
|
|
||||||
|
var fileEx = fileName.Split('.').Last();
|
||||||
|
fileName = fileName.Split('.').First();
|
||||||
|
|
||||||
|
fileName = fileName + "_" + StringExtensions.GetId(5) + "_" + DateTime.Today.ToString("yyyy-MM-dd-HH-mm-ss") + "." + fileEx;
|
||||||
|
|
||||||
|
var putRequest = new PutObjectRequest
|
||||||
|
{
|
||||||
|
BucketName = _bucketName,
|
||||||
|
Key = Path.Combine(filePath, fileName),
|
||||||
|
ContentType = contentType,
|
||||||
|
InputStream = memorySteam,
|
||||||
|
CannedACL = S3CannedACL.PublicRead
|
||||||
|
};
|
||||||
|
|
||||||
|
putRequest.Metadata.Add("x-amz-meta-title", fileName);
|
||||||
|
|
||||||
|
PutObjectResponse response = await client.PutObjectAsync(putRequest);
|
||||||
|
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> UploadObjectFromFileAsync(string fileName, string filePath, string contentType, Stream fileStream)
|
||||||
|
{
|
||||||
|
|
||||||
|
var client = GetClientAsync();
|
||||||
|
|
||||||
|
var fileEx = fileName.Split('.').Last();
|
||||||
|
fileName = fileName.Split('.').First();
|
||||||
|
|
||||||
|
fileName = fileName + "_" + StringExtensions.GetId(5) + "_" + DateTimeExtensions.DateTimeToUnixTimeStamp(DateTime.Now) + "." + fileEx;
|
||||||
|
|
||||||
|
var putRequest = new PutObjectRequest
|
||||||
|
{
|
||||||
|
BucketName = _bucketName,
|
||||||
|
Key = Path.Combine(filePath, fileName),
|
||||||
|
ContentType = contentType,
|
||||||
|
InputStream = fileStream,
|
||||||
|
CannedACL = S3CannedACL.PublicRead
|
||||||
|
};
|
||||||
|
|
||||||
|
putRequest.Metadata.Add("x-amz-meta-title", fileName);
|
||||||
|
|
||||||
|
PutObjectResponse response = await client.PutObjectAsync(putRequest);
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<StorageFileSDto>> GetStorageFiles(StorageFileType fileType)
|
||||||
|
{
|
||||||
|
var client = GetClientAsync();
|
||||||
|
ListObjectsRequest request = new() { BucketName = _bucketName};
|
||||||
|
switch (fileType)
|
||||||
|
{
|
||||||
|
case StorageFileType.Image:
|
||||||
|
request.Prefix = "Images/Med";
|
||||||
|
break;
|
||||||
|
case StorageFileType.Video:
|
||||||
|
request.Prefix = "Videos";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var files = new List<StorageFileSDto>();
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ListObjectsResponse response = await client.ListObjectsAsync(request);
|
||||||
|
|
||||||
|
// Process the response.
|
||||||
|
foreach (var s3Object in response.S3Objects.Where(s=>s.Size>0))
|
||||||
|
{
|
||||||
|
files.Add(new StorageFileSDto
|
||||||
|
{
|
||||||
|
FileLocation = s3Object.Key,
|
||||||
|
FileName = s3Object.Key.Split('/').Last()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the response is truncated, set the marker to get the next
|
||||||
|
// set of keys.
|
||||||
|
if (response.IsTruncated)
|
||||||
|
{
|
||||||
|
request.Marker = response.NextMarker;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
request = null;
|
||||||
|
}
|
||||||
|
} while (request != null);
|
||||||
|
|
||||||
|
return files.OrderByDescending(o=>o.CreatedAt).ToList();
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,10 @@ public class GetProductsQueryHandler : IRequestHandler<GetProductsQuery, List<Pr
|
||||||
_ => products
|
_ => products
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
products = products.OrderByDescending(p => p.CreatedAt);
|
||||||
|
}
|
||||||
|
|
||||||
var productSDtos = await products.Skip(request.Page * 20)
|
var productSDtos = await products.Skip(request.Page * 20)
|
||||||
.Take(20)
|
.Take(20)
|
||||||
|
|
|
@ -31,7 +31,7 @@ public class UpdateProductCommandHandler : IRequestHandler<UpdateProductCommand,
|
||||||
.Where(s => s.ProductId == ent.Id).ToListAsync(cancellationToken);
|
.Where(s => s.ProductId == ent.Id).ToListAsync(cancellationToken);
|
||||||
foreach (var dbSpecification in dbSpecifications)
|
foreach (var dbSpecification in dbSpecifications)
|
||||||
{
|
{
|
||||||
if (request.Specifications.Any(s => s.Id == dbSpecification.Id))
|
if (request.Specifications.FirstOrDefault(s => s.Id != dbSpecification.Id) == null)
|
||||||
{
|
{
|
||||||
_repositoryWrapper.SetRepository<Specification>().Delete(dbSpecification);
|
_repositoryWrapper.SetRepository<Specification>().Delete(dbSpecification);
|
||||||
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
|
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
|
||||||
|
@ -49,7 +49,7 @@ public class UpdateProductCommandHandler : IRequestHandler<UpdateProductCommand,
|
||||||
.Where(s => s.ProductId == ent.Id).ToListAsync(cancellationToken);
|
.Where(s => s.ProductId == ent.Id).ToListAsync(cancellationToken);
|
||||||
foreach (var dbFile in dbFiles)
|
foreach (var dbFile in dbFiles)
|
||||||
{
|
{
|
||||||
if (request.Files.Any(s => s.Id == dbFile.Id))
|
if (request.Files.FirstOrDefault(s => s.Id == dbFile.Id)==null)
|
||||||
{
|
{
|
||||||
_repositoryWrapper.SetRepository<ProductStorageFile>().Delete(dbFile);
|
_repositoryWrapper.SetRepository<ProductStorageFile>().Delete(dbFile);
|
||||||
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
|
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
|
||||||
|
|
1583
NetinaShop.Repository/Migrations/20240129072040_editSpecificationNullParent.Designer.cs
generated
100644
1583
NetinaShop.Repository/Migrations/20240129072040_editSpecificationNullParent.Designer.cs
generated
100644
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,68 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NetinaShop.Repository.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class editSpecificationNullParent : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Specifications_Specifications_ParentId",
|
||||||
|
schema: "public",
|
||||||
|
table: "Specifications");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<Guid>(
|
||||||
|
name: "ParentId",
|
||||||
|
schema: "public",
|
||||||
|
table: "Specifications",
|
||||||
|
type: "uuid",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(Guid),
|
||||||
|
oldType: "uuid");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Specifications_Specifications_ParentId",
|
||||||
|
schema: "public",
|
||||||
|
table: "Specifications",
|
||||||
|
column: "ParentId",
|
||||||
|
principalSchema: "public",
|
||||||
|
principalTable: "Specifications",
|
||||||
|
principalColumn: "Id");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Specifications_Specifications_ParentId",
|
||||||
|
schema: "public",
|
||||||
|
table: "Specifications");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<Guid>(
|
||||||
|
name: "ParentId",
|
||||||
|
schema: "public",
|
||||||
|
table: "Specifications",
|
||||||
|
type: "uuid",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||||
|
oldClrType: typeof(Guid),
|
||||||
|
oldType: "uuid",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Specifications_Specifications_ParentId",
|
||||||
|
schema: "public",
|
||||||
|
table: "Specifications",
|
||||||
|
column: "ParentId",
|
||||||
|
principalSchema: "public",
|
||||||
|
principalTable: "Specifications",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -802,7 +802,7 @@ namespace NetinaShop.Repository.Migrations
|
||||||
b.Property<string>("ModifiedBy")
|
b.Property<string>("ModifiedBy")
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<Guid>("ParentId")
|
b.Property<Guid?>("ParentId")
|
||||||
.HasColumnType("uuid");
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
b.Property<Guid>("ProductId")
|
b.Property<Guid>("ProductId")
|
||||||
|
@ -1409,9 +1409,7 @@ namespace NetinaShop.Repository.Migrations
|
||||||
{
|
{
|
||||||
b.HasOne("NetinaShop.Domain.Entities.Products.Specification", "Parent")
|
b.HasOne("NetinaShop.Domain.Entities.Products.Specification", "Parent")
|
||||||
.WithMany("Children")
|
.WithMany("Children")
|
||||||
.HasForeignKey("ParentId")
|
.HasForeignKey("ParentId");
|
||||||
.OnDelete(DeleteBehavior.Restrict)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("NetinaShop.Domain.Entities.Products.Product", "Product")
|
b.HasOne("NetinaShop.Domain.Entities.Products.Product", "Product")
|
||||||
.WithMany("Specifications")
|
.WithMany("Specifications")
|
||||||
|
|
Loading…
Reference in New Issue