Add address/order handling
This commit is contained in:
@@ -11,7 +11,7 @@ public interface IUnitOfWork
|
||||
public IUserRoleRepository UserRoleRepository { get; }
|
||||
public IRoleRepository RoleRepository { get; }
|
||||
public IOrderRepository OrderRepository { get; }
|
||||
public IOrderItemRepository OrderItemRepository { get; }
|
||||
public IAddressRepository AddressRepository { get; }
|
||||
|
||||
Task SaveAsync(CancellationToken cancellationToken = default);
|
||||
Task BeginTransactionAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
@@ -16,17 +16,15 @@ public class ProductMappingProfile: Profile
|
||||
.ForMember(dest => dest.ModifiedAt, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.ModifiedBy, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Product, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.OrderItems, opt => opt.Ignore());
|
||||
.ForMember(dest => dest.Product, opt => opt.Ignore());
|
||||
|
||||
CreateMap<ProductVariant, ProductVariantDto>();
|
||||
CreateMap<ProductVariantDto, ProductVariant>()
|
||||
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.ModifiedBy, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.OrderItems, opt => opt.Ignore());
|
||||
.ForMember(dest => dest.ModifiedBy, opt => opt.Ignore());
|
||||
|
||||
CreateMap<Product, ProductDto>();
|
||||
|
||||
|
||||
CreateMap<CreateProductCommand, Product>()
|
||||
.ForMember(dest => dest.Id, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
|
||||
@@ -34,8 +32,7 @@ public class ProductMappingProfile: Profile
|
||||
.ForMember(dest => dest.CreatedBy, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.ModifiedBy, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Category, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.ProductVariants, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.OrderItems, opt => opt.Ignore());
|
||||
.ForMember(dest => dest.ProductVariants, opt => opt.Ignore());
|
||||
|
||||
CreateMap<Category, CategoryDto>();
|
||||
}
|
||||
|
||||
@@ -4,11 +4,22 @@ public class Address : EntityBase
|
||||
{
|
||||
public required string UserId { get; set; }
|
||||
public required string AddressType { get; set; }
|
||||
public required string Street { get; set; }
|
||||
public string? FirstName { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
public string? Company { get; set; }
|
||||
public required string AddressLine1 { get; set; }
|
||||
public string? AddressLine2 { get; set; }
|
||||
public string? ApartmentNumber { get; set; }
|
||||
public string? BuildingNumber { get; set; }
|
||||
public string? Floor { get; set; }
|
||||
public required string City { get; set; }
|
||||
public required string State { get; set; }
|
||||
public required string PostalCode { get; set; }
|
||||
public required string Country { get; set; }
|
||||
public string? PhoneNumber { get; set; }
|
||||
public string? Instructions { get; set; }
|
||||
public required bool IsDefault { get; set; }
|
||||
public required bool IsActive { get; set; }
|
||||
|
||||
public virtual User User { get; set; } = null!;
|
||||
}
|
||||
@@ -4,15 +4,24 @@ public class Order : EntityBase
|
||||
{
|
||||
public string UserId { get; set; } = null!;
|
||||
public DateTime OrderDate { get; set; }
|
||||
public decimal TotalPrice { get; set; }
|
||||
public decimal Amount { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public Guid ProductId { get; set; }
|
||||
public Guid? ProductVariantId { get; set; }
|
||||
public int OrderStatusId { get; set; }
|
||||
public int ShippingStatusId { get; set; }
|
||||
public string OrderNumber { get; set; } = null!;
|
||||
public string Notes { get; set; } = null!;
|
||||
public string? Notes { get; set; }
|
||||
public string? MerchantId { get; set; }
|
||||
public string? ComposingImageUrl { get; set; }
|
||||
public string[] OriginalImageUrls { get; set; } = [];
|
||||
public string CustomizationImageUrl { get; set; } = null!;
|
||||
public string CustomizationDescription { get; set; } = null!;
|
||||
|
||||
public OrderStatus OrderStatus { get; set; } = null!;
|
||||
public User User { get; set; } = null!;
|
||||
public ShippingStatus ShippingStatus { get; set; } = null!;
|
||||
public OrderAddress OrderAddress { get; set; } = null!;
|
||||
public virtual ICollection<OrderItem> OrderItems { get; set; } = new List<OrderItem>();
|
||||
public Product Product { get; set; } = null!;
|
||||
public ProductVariant? ProductVariant { get; set; }
|
||||
}
|
||||
@@ -3,11 +3,21 @@ namespace Imprink.Domain.Entities;
|
||||
public class OrderAddress : EntityBase
|
||||
{
|
||||
public Guid OrderId { get; set; }
|
||||
public required string Street { get; set; }
|
||||
public required string AddressType { get; set; }
|
||||
public string? FirstName { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
public string? Company { get; set; }
|
||||
public required string AddressLine1 { get; set; }
|
||||
public string? AddressLine2 { get; set; }
|
||||
public string? ApartmentNumber { get; set; }
|
||||
public string? BuildingNumber { get; set; }
|
||||
public string? Floor { get; set; }
|
||||
public required string City { get; set; }
|
||||
public required string State { get; set; }
|
||||
public required string PostalCode { get; set; }
|
||||
public required string Country { get; set; }
|
||||
public string? PhoneNumber { get; set; }
|
||||
public string? Instructions { get; set; }
|
||||
|
||||
public virtual required Order Order { get; set; }
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
namespace Imprink.Domain.Entities;
|
||||
|
||||
public class OrderItem : EntityBase
|
||||
{
|
||||
public Guid OrderId { get; set; }
|
||||
public Guid ProductId { get; set; }
|
||||
public Guid? ProductVariantId { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public decimal UnitPrice { get; set; }
|
||||
public decimal TotalPrice { get; set; }
|
||||
public string CustomizationImageUrl { get; set; } = null!;
|
||||
public string CustomizationDescription { get; set; } = null!;
|
||||
|
||||
public Order Order { get; set; } = null!;
|
||||
public Product Product { get; set; } = null!;
|
||||
public ProductVariant ProductVariant { get; set; } = null!;
|
||||
}
|
||||
@@ -12,5 +12,5 @@ public class Product : EntityBase
|
||||
|
||||
public virtual required Category Category { get; set; }
|
||||
public virtual ICollection<ProductVariant> ProductVariants { get; set; } = new List<ProductVariant>();
|
||||
public virtual ICollection<OrderItem> OrderItems { get; set; } = new List<OrderItem>();
|
||||
public virtual ICollection<Order> Orders { get; set; } = new List<Order>();
|
||||
}
|
||||
@@ -12,5 +12,5 @@ public class ProductVariant : EntityBase
|
||||
public bool IsActive { get; set; }
|
||||
|
||||
public virtual required Product Product { get; set; }
|
||||
public virtual ICollection<OrderItem> OrderItems { get; set; } = new List<OrderItem>();
|
||||
public virtual ICollection<Order> Orders { get; set; } = new List<Order>();
|
||||
}
|
||||
@@ -8,14 +8,15 @@ public class User
|
||||
public required string Email { get; set; }
|
||||
public bool EmailVerified { get; set; }
|
||||
|
||||
public string? FirstName { get; set; } = null!;
|
||||
public string? LastName { get; set; } = null!;
|
||||
public string? FirstName { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
public string? PhoneNumber { get; set; }
|
||||
public required bool IsActive { get; set; }
|
||||
|
||||
public virtual ICollection<Address> Addresses { get; set; } = new List<Address>();
|
||||
public virtual ICollection<UserRole> UserRoles { get; set; } = new List<UserRole>();
|
||||
public virtual ICollection<Order> Orders { get; set; } = new List<Order>();
|
||||
public virtual ICollection<Order> MerchantOrders { get; set; } = new List<Order>();
|
||||
|
||||
public Address? DefaultAddress => Addresses.FirstOrDefault(a => a is { IsDefault: true, IsActive: true });
|
||||
public IEnumerable<Role> Roles => UserRoles.Select(ur => ur.Role);
|
||||
|
||||
22
src/Imprink.Domain/Repositories/IAddressRepository.cs
Normal file
22
src/Imprink.Domain/Repositories/IAddressRepository.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Imprink.Domain.Entities;
|
||||
|
||||
namespace Imprink.Domain.Repositories;
|
||||
|
||||
public interface IAddressRepository
|
||||
{
|
||||
Task<IEnumerable<Address>> GetByUserIdAsync(string userId, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<Address>> GetActiveByUserIdAsync(string userId, CancellationToken cancellationToken = default);
|
||||
Task<Address?> GetDefaultByUserIdAsync(string userId, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<Address>> GetByUserIdAndTypeAsync(string userId, string addressType, CancellationToken cancellationToken = default);
|
||||
Task<Address?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
|
||||
Task<Address?> GetByIdAndUserIdAsync(Guid id, string userId, CancellationToken cancellationToken = default);
|
||||
Task<Address> AddAsync(Address address, CancellationToken cancellationToken = default);
|
||||
Task<Address> UpdateAsync(Address address, CancellationToken cancellationToken = default);
|
||||
Task<bool> DeleteAsync(Guid id, CancellationToken cancellationToken = default);
|
||||
Task<bool> DeleteByUserIdAsync(Guid id, string userId, CancellationToken cancellationToken = default);
|
||||
Task SetDefaultAddressAsync(string userId, Guid addressId, CancellationToken cancellationToken = default);
|
||||
Task DeactivateAddressAsync(Guid addressId, CancellationToken cancellationToken = default);
|
||||
Task ActivateAddressAsync(Guid addressId, CancellationToken cancellationToken = default);
|
||||
Task<bool> ExistsAsync(Guid id, CancellationToken cancellationToken = default);
|
||||
Task<bool> IsUserAddressAsync(Guid id, string userId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using Imprink.Domain.Entities;
|
||||
|
||||
namespace Imprink.Domain.Repositories;
|
||||
|
||||
public interface IOrderItemRepository
|
||||
{
|
||||
Task<OrderItem?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
|
||||
Task<OrderItem?> GetByIdWithProductAsync(Guid id, CancellationToken cancellationToken = default);
|
||||
Task<OrderItem?> GetByIdWithVariantAsync(Guid id, CancellationToken cancellationToken = default);
|
||||
Task<OrderItem?> GetByIdFullAsync(Guid id, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<OrderItem>> GetByOrderIdAsync(Guid orderId, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<OrderItem>> GetByOrderIdWithProductsAsync(Guid orderId, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<OrderItem>> GetByProductIdAsync(Guid productId, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<OrderItem>> GetByProductVariantIdAsync(Guid productVariantId, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<OrderItem>> GetCustomizedItemsAsync(CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<OrderItem>> GetByDateRangeAsync(DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default);
|
||||
Task<OrderItem> AddAsync(OrderItem orderItem, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<OrderItem>> AddRangeAsync(IEnumerable<OrderItem> orderItems, CancellationToken cancellationToken = default);
|
||||
Task<OrderItem> UpdateAsync(OrderItem orderItem, CancellationToken cancellationToken = default);
|
||||
Task DeleteAsync(Guid id, CancellationToken cancellationToken = default);
|
||||
Task DeleteByOrderIdAsync(Guid orderId, CancellationToken cancellationToken = default);
|
||||
Task<bool> ExistsAsync(Guid id, CancellationToken cancellationToken = default);
|
||||
Task<decimal> GetTotalValueByOrderIdAsync(Guid orderId, CancellationToken cancellationToken = default);
|
||||
Task<int> GetQuantityByProductIdAsync(Guid productId, CancellationToken cancellationToken = default);
|
||||
Task<int> GetQuantityByVariantIdAsync(Guid productVariantId, CancellationToken cancellationToken = default);
|
||||
Task<Dictionary<Guid, int>> GetProductSalesCountAsync(DateTime? startDate = null, DateTime? endDate = null, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -6,22 +6,24 @@ namespace Imprink.Domain.Repositories;
|
||||
public interface IOrderRepository
|
||||
{
|
||||
Task<Order?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
|
||||
Task<Order?> GetByIdWithItemsAsync(Guid id, CancellationToken cancellationToken = default);
|
||||
Task<Order?> GetByIdWithAddressAsync(Guid id, CancellationToken cancellationToken = default);
|
||||
Task<Order?> GetByIdFullAsync(Guid id, CancellationToken cancellationToken = default);
|
||||
Task<Order?> GetByIdWithDetailsAsync(Guid id, CancellationToken cancellationToken = default);
|
||||
Task<Order?> GetByOrderNumberAsync(string orderNumber, CancellationToken cancellationToken = default);
|
||||
Task<PagedResult<Order>> GetPagedAsync(OrderFilterParameters filterParameters, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<Order>> GetByUserIdAsync(string userId, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<Order>> GetByUserIdPagedAsync(string userId, int pageNumber, int pageSize, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<Order>> GetByOrderStatusAsync(int orderStatusId, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<Order>> GetByShippingStatusAsync(int shippingStatusId, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<Order>> GetByUserIdWithDetailsAsync(string userId, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<Order>> GetByMerchantIdAsync(string merchantId, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<Order>> GetByMerchantIdWithDetailsAsync(string merchantId, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<Order>> GetByDateRangeAsync(DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<Order>> GetByStatusAsync(int statusId, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<Order>> GetByShippingStatusAsync(int shippingStatusId, CancellationToken cancellationToken = default);
|
||||
Task<Order> AddAsync(Order order, CancellationToken cancellationToken = default);
|
||||
Task<Order> UpdateAsync(Order order, CancellationToken cancellationToken = default);
|
||||
Task DeleteAsync(Guid id, CancellationToken cancellationToken = default);
|
||||
Task<bool> DeleteAsync(Guid id, CancellationToken cancellationToken = default);
|
||||
Task<bool> ExistsAsync(Guid id, CancellationToken cancellationToken = default);
|
||||
Task<bool> OrderNumberExistsAsync(string orderNumber, Guid? excludeId = null, CancellationToken cancellationToken = default);
|
||||
Task<decimal> GetTotalRevenueAsync(CancellationToken cancellationToken = default);
|
||||
Task<decimal> GetTotalRevenueByDateRangeAsync(DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default);
|
||||
Task<int> GetOrderCountByStatusAsync(int orderStatusId, CancellationToken cancellationToken = default);
|
||||
Task<bool> IsOrderNumberUniqueAsync(string orderNumber, CancellationToken cancellationToken = default);
|
||||
Task<bool> IsOrderNumberUniqueAsync(string orderNumber, Guid excludeOrderId, CancellationToken cancellationToken = default);
|
||||
Task<string> GenerateOrderNumberAsync(CancellationToken cancellationToken = default);
|
||||
Task UpdateStatusAsync(Guid orderId, int statusId, CancellationToken cancellationToken = default);
|
||||
Task UpdateShippingStatusAsync(Guid orderId, int shippingStatusId, CancellationToken cancellationToken = default);
|
||||
Task AssignMerchantAsync(Guid orderId, string merchantId, CancellationToken cancellationToken = default);
|
||||
Task UnassignMerchantAsync(Guid orderId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -17,10 +17,31 @@ public class AddressConfiguration : EntityBaseConfiguration<Address>
|
||||
builder.Property(a => a.AddressType)
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.Property(a => a.FirstName)
|
||||
.HasMaxLength(100);
|
||||
|
||||
builder.Property(a => a.LastName)
|
||||
.HasMaxLength(100);
|
||||
|
||||
builder.Property(a => a.Company)
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(a => a.Street)
|
||||
builder.Property(a => a.AddressLine1)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(a => a.AddressLine2)
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(a => a.ApartmentNumber)
|
||||
.HasMaxLength(20);
|
||||
|
||||
builder.Property(a => a.BuildingNumber)
|
||||
.HasMaxLength(20);
|
||||
|
||||
builder.Property(a => a.Floor)
|
||||
.HasMaxLength(20);
|
||||
|
||||
builder.Property(a => a.City)
|
||||
.IsRequired()
|
||||
@@ -36,6 +57,12 @@ public class AddressConfiguration : EntityBaseConfiguration<Address>
|
||||
builder.Property(a => a.Country)
|
||||
.IsRequired()
|
||||
.HasMaxLength(100);
|
||||
|
||||
builder.Property(a => a.PhoneNumber)
|
||||
.HasMaxLength(20);
|
||||
|
||||
builder.Property(a => a.Instructions)
|
||||
.HasMaxLength(500);
|
||||
|
||||
builder.Property(a => a.IsDefault)
|
||||
.IsRequired()
|
||||
@@ -45,6 +72,12 @@ public class AddressConfiguration : EntityBaseConfiguration<Address>
|
||||
.IsRequired()
|
||||
.HasDefaultValue(true);
|
||||
|
||||
builder.HasOne(a => a.User)
|
||||
.WithMany(u => u.Addresses)
|
||||
.HasForeignKey(a => a.UserId)
|
||||
.HasPrincipalKey(u => u.Id)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.HasIndex(a => a.UserId)
|
||||
.HasDatabaseName("IX_Address_UserId");
|
||||
|
||||
@@ -54,4 +87,4 @@ public class AddressConfiguration : EntityBaseConfiguration<Address>
|
||||
builder.HasIndex(a => new { a.UserId, a.IsDefault })
|
||||
.HasDatabaseName("IX_Address_User_Default");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,35 @@ public class OrderAddressConfiguration : EntityBaseConfiguration<OrderAddress>
|
||||
|
||||
builder.Property(oa => oa.OrderId)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(oa => oa.AddressType)
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.Property(oa => oa.FirstName)
|
||||
.HasMaxLength(100);
|
||||
|
||||
builder.Property(oa => oa.LastName)
|
||||
.HasMaxLength(100);
|
||||
|
||||
builder.Property(oa => oa.Company)
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(oa => oa.Street)
|
||||
builder.Property(oa => oa.AddressLine1)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(oa => oa.AddressLine2)
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(oa => oa.ApartmentNumber)
|
||||
.HasMaxLength(20);
|
||||
|
||||
builder.Property(oa => oa.BuildingNumber)
|
||||
.HasMaxLength(20);
|
||||
|
||||
builder.Property(oa => oa.Floor)
|
||||
.HasMaxLength(20);
|
||||
|
||||
builder.Property(oa => oa.City)
|
||||
.IsRequired()
|
||||
@@ -32,6 +57,12 @@ public class OrderAddressConfiguration : EntityBaseConfiguration<OrderAddress>
|
||||
.IsRequired()
|
||||
.HasMaxLength(100);
|
||||
|
||||
builder.Property(oa => oa.PhoneNumber)
|
||||
.HasMaxLength(20);
|
||||
|
||||
builder.Property(oa => oa.Instructions)
|
||||
.HasMaxLength(500);
|
||||
|
||||
builder.HasIndex(oa => oa.OrderId)
|
||||
.IsUnique()
|
||||
.HasDatabaseName("IX_OrderAddress_OrderId");
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Text.Json;
|
||||
using Imprink.Domain.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
@@ -5,73 +6,131 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
namespace Imprink.Infrastructure.Configuration;
|
||||
|
||||
public class OrderConfiguration : EntityBaseConfiguration<Order>
|
||||
{
|
||||
public override void Configure(EntityTypeBuilder<Order> builder)
|
||||
{
|
||||
public override void Configure(EntityTypeBuilder<Order> builder)
|
||||
{
|
||||
base.Configure(builder);
|
||||
base.Configure(builder);
|
||||
|
||||
builder.Property(o => o.UserId)
|
||||
.IsRequired()
|
||||
.HasMaxLength(450);
|
||||
|
||||
builder.Property(o => o.OrderDate)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(o => o.UserId)
|
||||
.IsRequired()
|
||||
.HasMaxLength(450);
|
||||
builder.Property(o => o.Amount)
|
||||
.IsRequired()
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
builder.Property(o => o.OrderDate)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(o => o.TotalPrice)
|
||||
.IsRequired()
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
builder.Property(o => o.OrderStatusId)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(o => o.ShippingStatusId)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(o => o.OrderNumber)
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.Property(o => o.Notes)
|
||||
.HasMaxLength(1000);
|
||||
builder.Property(o => o.Quantity)
|
||||
.IsRequired()
|
||||
.HasDefaultValue(1);
|
||||
|
||||
builder.HasOne(o => o.OrderStatus)
|
||||
.WithMany(os => os.Orders)
|
||||
.HasForeignKey(o => o.OrderStatusId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasOne(o => o.ShippingStatus)
|
||||
.WithMany(ss => ss.Orders)
|
||||
.HasForeignKey(o => o.ShippingStatusId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasOne(o => o.OrderAddress)
|
||||
.WithOne(oa => oa.Order)
|
||||
.HasForeignKey<OrderAddress>(oa => oa.OrderId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
builder.Property(o => o.ProductId)
|
||||
.IsRequired();
|
||||
|
||||
builder.HasOne(o => o.User)
|
||||
.WithMany(u => u.Orders)
|
||||
.HasForeignKey(o => o.UserId)
|
||||
.HasPrincipalKey(u => u.Id)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
builder.Property(o => o.OrderStatusId)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(o => o.ShippingStatusId)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(o => o.OrderNumber)
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.Property(o => o.Notes)
|
||||
.HasMaxLength(1000);
|
||||
|
||||
builder.HasIndex(o => o.UserId)
|
||||
.HasDatabaseName("IX_Order_UserId");
|
||||
|
||||
builder.HasIndex(o => o.OrderNumber)
|
||||
.IsUnique()
|
||||
.HasDatabaseName("IX_Order_OrderNumber");
|
||||
|
||||
builder.HasIndex(o => o.OrderDate)
|
||||
.HasDatabaseName("IX_Order_OrderDate");
|
||||
|
||||
builder.HasIndex(o => o.OrderStatusId)
|
||||
.HasDatabaseName("IX_Order_OrderStatusId");
|
||||
|
||||
builder.HasIndex(o => o.ShippingStatusId)
|
||||
.HasDatabaseName("IX_Order_ShippingStatusId");
|
||||
|
||||
builder.HasIndex(o => new { o.UserId, o.OrderDate })
|
||||
.HasDatabaseName("IX_Order_User_Date");
|
||||
}
|
||||
}
|
||||
builder.Property(o => o.MerchantId)
|
||||
.HasMaxLength(450);
|
||||
|
||||
builder.Property(o => o.ComposingImageUrl)
|
||||
.HasMaxLength(1000);
|
||||
|
||||
builder.Property(o => o.OriginalImageUrls)
|
||||
.HasConversion(
|
||||
v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
|
||||
v => JsonSerializer.Deserialize<string[]>(v, (JsonSerializerOptions?)null) ?? Array.Empty<string>())
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
builder.Property(o => o.CustomizationImageUrl)
|
||||
.IsRequired()
|
||||
.HasMaxLength(1000);
|
||||
|
||||
builder.Property(o => o.CustomizationDescription)
|
||||
.IsRequired()
|
||||
.HasMaxLength(2000);
|
||||
|
||||
builder.HasOne(o => o.OrderStatus)
|
||||
.WithMany(os => os.Orders)
|
||||
.HasForeignKey(o => o.OrderStatusId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasOne(o => o.ShippingStatus)
|
||||
.WithMany(ss => ss.Orders)
|
||||
.HasForeignKey(o => o.ShippingStatusId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasOne(o => o.OrderAddress)
|
||||
.WithOne(oa => oa.Order)
|
||||
.HasForeignKey<OrderAddress>(oa => oa.OrderId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.HasOne(o => o.User)
|
||||
.WithMany(u => u.Orders)
|
||||
.HasForeignKey(o => o.UserId)
|
||||
.HasPrincipalKey(u => u.Id)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasOne<User>()
|
||||
.WithMany(u => u.MerchantOrders)
|
||||
.HasForeignKey(o => o.MerchantId)
|
||||
.HasPrincipalKey(u => u.Id)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
builder.HasOne(o => o.Product)
|
||||
.WithMany(p => p.Orders)
|
||||
.HasForeignKey(o => o.ProductId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasOne(o => o.ProductVariant)
|
||||
.WithMany(pv => pv.Orders)
|
||||
.HasForeignKey(o => o.ProductVariantId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasIndex(o => o.UserId)
|
||||
.HasDatabaseName("IX_Order_UserId");
|
||||
|
||||
builder.HasIndex(o => o.OrderNumber)
|
||||
.IsUnique()
|
||||
.HasDatabaseName("IX_Order_OrderNumber");
|
||||
|
||||
builder.HasIndex(o => o.OrderDate)
|
||||
.HasDatabaseName("IX_Order_OrderDate");
|
||||
|
||||
builder.HasIndex(o => o.OrderStatusId)
|
||||
.HasDatabaseName("IX_Order_OrderStatusId");
|
||||
|
||||
builder.HasIndex(o => o.ShippingStatusId)
|
||||
.HasDatabaseName("IX_Order_ShippingStatusId");
|
||||
|
||||
builder.HasIndex(o => o.MerchantId)
|
||||
.HasDatabaseName("IX_Order_MerchantId");
|
||||
|
||||
builder.HasIndex(o => o.ProductId)
|
||||
.HasDatabaseName("IX_Order_ProductId");
|
||||
|
||||
builder.HasIndex(o => o.ProductVariantId)
|
||||
.HasDatabaseName("IX_Order_ProductVariantId");
|
||||
|
||||
builder.HasIndex(o => new { o.UserId, o.OrderDate })
|
||||
.HasDatabaseName("IX_Order_User_Date");
|
||||
|
||||
builder.HasIndex(o => new { o.MerchantId, o.OrderDate })
|
||||
.HasDatabaseName("IX_Order_Merchant_Date");
|
||||
|
||||
builder.HasIndex(o => new { o.ProductId, o.OrderDate })
|
||||
.HasDatabaseName("IX_Order_Product_Date");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
using Imprink.Domain.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace Imprink.Infrastructure.Configuration;
|
||||
|
||||
public class OrderItemConfiguration : EntityBaseConfiguration<OrderItem>
|
||||
{
|
||||
public override void Configure(EntityTypeBuilder<OrderItem> builder)
|
||||
{
|
||||
base.Configure(builder);
|
||||
|
||||
builder.Property(oi => oi.OrderId)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(oi => oi.ProductId)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(oi => oi.Quantity)
|
||||
.IsRequired()
|
||||
.HasDefaultValue(1);
|
||||
|
||||
builder.Property(oi => oi.UnitPrice)
|
||||
.IsRequired()
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
builder.Property(oi => oi.TotalPrice)
|
||||
.IsRequired()
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
builder.Property(oi => oi.CustomizationImageUrl)
|
||||
.HasMaxLength(500);
|
||||
|
||||
builder.Property(oi => oi.CustomizationDescription)
|
||||
.HasMaxLength(2000);
|
||||
|
||||
builder.HasOne(oi => oi.Order)
|
||||
.WithMany(o => o.OrderItems)
|
||||
.HasForeignKey(oi => oi.OrderId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.HasOne(oi => oi.Product)
|
||||
.WithMany(p => p.OrderItems)
|
||||
.HasForeignKey(oi => oi.ProductId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasOne(oi => oi.ProductVariant)
|
||||
.WithMany(pv => pv.OrderItems)
|
||||
.HasForeignKey(oi => oi.ProductVariantId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasIndex(oi => oi.OrderId)
|
||||
.HasDatabaseName("IX_OrderItem_OrderId");
|
||||
|
||||
builder.HasIndex(oi => oi.ProductId)
|
||||
.HasDatabaseName("IX_OrderItem_ProductId");
|
||||
|
||||
builder.HasIndex(oi => oi.ProductVariantId)
|
||||
.HasDatabaseName("IX_OrderItem_ProductVariantId");
|
||||
|
||||
builder.HasIndex(oi => new { oi.OrderId, oi.ProductId })
|
||||
.HasDatabaseName("IX_OrderItem_Order_Product");
|
||||
}
|
||||
}
|
||||
@@ -23,10 +23,9 @@ public class UserConfiguration : IEntityTypeConfiguration<User>
|
||||
builder.Property(u => u.Nickname)
|
||||
.IsRequired()
|
||||
.HasMaxLength(100);
|
||||
|
||||
|
||||
builder.Property(u => u.EmailVerified)
|
||||
.IsRequired()
|
||||
.HasMaxLength(100);
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(u => u.FirstName)
|
||||
.HasMaxLength(100);
|
||||
|
||||
@@ -11,7 +11,6 @@ public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options
|
||||
public DbSet<Product> Products { get; set; }
|
||||
public DbSet<ProductVariant> ProductVariants { get; set; }
|
||||
public DbSet<Order> Orders { get; set; }
|
||||
public DbSet<OrderItem> OrderItems { get; set; }
|
||||
public DbSet<OrderAddress> OrderAddresses { get; set; }
|
||||
public DbSet<Address> Addresses { get; set; }
|
||||
public DbSet<OrderStatus> OrderStatuses { get; set; }
|
||||
@@ -28,7 +27,6 @@ public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options
|
||||
modelBuilder.ApplyConfiguration(new ProductConfiguration());
|
||||
modelBuilder.ApplyConfiguration(new ProductVariantConfiguration());
|
||||
modelBuilder.ApplyConfiguration(new OrderConfiguration());
|
||||
modelBuilder.ApplyConfiguration(new OrderItemConfiguration());
|
||||
modelBuilder.ApplyConfiguration(new OrderAddressConfiguration());
|
||||
modelBuilder.ApplyConfiguration(new AddressConfiguration());
|
||||
modelBuilder.ApplyConfiguration(new OrderStatusConfiguration());
|
||||
|
||||
168
src/Imprink.Infrastructure/Repositories/AddressRepository.cs
Normal file
168
src/Imprink.Infrastructure/Repositories/AddressRepository.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
using Imprink.Domain.Entities;
|
||||
using Imprink.Domain.Repositories;
|
||||
using Imprink.Infrastructure.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Imprink.Infrastructure.Repositories;
|
||||
|
||||
public class AddressRepository(ApplicationDbContext context) : IAddressRepository
|
||||
{
|
||||
public async Task<IEnumerable<Address>> GetByUserIdAsync(string userId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Addresses
|
||||
.Where(a => a.UserId == userId)
|
||||
.OrderByDescending(a => a.IsDefault)
|
||||
.ThenBy(a => a.AddressType)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Address>> GetActiveByUserIdAsync(string userId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Addresses
|
||||
.Where(a => a.UserId == userId && a.IsActive)
|
||||
.OrderByDescending(a => a.IsDefault)
|
||||
.ThenBy(a => a.AddressType)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<Address?> GetDefaultByUserIdAsync(string userId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Addresses
|
||||
.FirstOrDefaultAsync(a => a.UserId == userId && a.IsDefault && a.IsActive, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Address>> GetByUserIdAndTypeAsync(string userId, string addressType, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Addresses
|
||||
.Where(a => a.UserId == userId && a.AddressType == addressType && a.IsActive)
|
||||
.OrderByDescending(a => a.IsDefault)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<Address?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Addresses
|
||||
.FirstOrDefaultAsync(a => a.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<Address?> GetByIdAndUserIdAsync(Guid id, string userId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Addresses
|
||||
.FirstOrDefaultAsync(a => a.Id == id && a.UserId == userId, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<Address> AddAsync(Address address, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (address.IsDefault)
|
||||
{
|
||||
await UnsetDefaultAddressesAsync(address.UserId, cancellationToken);
|
||||
}
|
||||
|
||||
context.Addresses.Add(address);
|
||||
return address;
|
||||
}
|
||||
|
||||
public async Task<Address> UpdateAsync(Address address, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var existingAddress = await context.Addresses
|
||||
.FirstOrDefaultAsync(a => a.Id == address.Id, cancellationToken);
|
||||
|
||||
if (existingAddress == null)
|
||||
throw new InvalidOperationException($"Address with ID {address.Id} not found");
|
||||
|
||||
if (address.IsDefault && !existingAddress.IsDefault)
|
||||
{
|
||||
await UnsetDefaultAddressesAsync(address.UserId, cancellationToken);
|
||||
}
|
||||
|
||||
context.Entry(existingAddress).CurrentValues.SetValues(address);
|
||||
return existingAddress;
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var address = await context.Addresses
|
||||
.FirstOrDefaultAsync(a => a.Id == id, cancellationToken);
|
||||
|
||||
if (address == null)
|
||||
return false;
|
||||
|
||||
context.Addresses.Remove(address);
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteByUserIdAsync(Guid id, string userId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var address = await context.Addresses
|
||||
.FirstOrDefaultAsync(a => a.Id == id && a.UserId == userId, cancellationToken);
|
||||
|
||||
if (address == null)
|
||||
return false;
|
||||
|
||||
context.Addresses.Remove(address);
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task SetDefaultAddressAsync(string userId, Guid addressId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await UnsetDefaultAddressesAsync(userId, cancellationToken);
|
||||
|
||||
var address = await context.Addresses
|
||||
.FirstOrDefaultAsync(a => a.Id == addressId && a.UserId == userId, cancellationToken);
|
||||
|
||||
if (address != null)
|
||||
{
|
||||
address.IsDefault = true;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeactivateAddressAsync(Guid addressId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var address = await context.Addresses
|
||||
.FirstOrDefaultAsync(a => a.Id == addressId, cancellationToken);
|
||||
|
||||
if (address != null)
|
||||
{
|
||||
address.IsActive = false;
|
||||
if (address.IsDefault)
|
||||
{
|
||||
address.IsDefault = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ActivateAddressAsync(Guid addressId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var address = await context.Addresses
|
||||
.FirstOrDefaultAsync(a => a.Id == addressId, cancellationToken);
|
||||
|
||||
if (address != null)
|
||||
{
|
||||
address.IsActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ExistsAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Addresses
|
||||
.AnyAsync(a => a.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<bool> IsUserAddressAsync(Guid id, string userId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Addresses
|
||||
.AnyAsync(a => a.Id == id && a.UserId == userId, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task UnsetDefaultAddressesAsync(string userId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var defaultAddresses = await context.Addresses
|
||||
.Where(a => a.UserId == userId && a.IsDefault)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
foreach (var addr in defaultAddresses)
|
||||
{
|
||||
addr.IsDefault = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
using Imprink.Domain.Entities;
|
||||
using Imprink.Domain.Repositories;
|
||||
using Imprink.Infrastructure.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Imprink.Infrastructure.Repositories;
|
||||
|
||||
public class OrderItemRepository(ApplicationDbContext context) : IOrderItemRepository
|
||||
{
|
||||
public async Task<OrderItem?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.OrderItems
|
||||
.FirstOrDefaultAsync(oi => oi.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<OrderItem?> GetByIdWithProductAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.OrderItems
|
||||
.Include(oi => oi.Product)
|
||||
.ThenInclude(p => p.Category)
|
||||
.FirstOrDefaultAsync(oi => oi.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<OrderItem?> GetByIdWithVariantAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.OrderItems
|
||||
.Include(oi => oi.ProductVariant)
|
||||
.FirstOrDefaultAsync(oi => oi.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<OrderItem?> GetByIdFullAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.OrderItems
|
||||
.Include(oi => oi.Order)
|
||||
.ThenInclude(o => o.User)
|
||||
.Include(oi => oi.Product)
|
||||
.ThenInclude(p => p.Category)
|
||||
.Include(oi => oi.ProductVariant)
|
||||
.FirstOrDefaultAsync(oi => oi.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<OrderItem>> GetByOrderIdAsync(Guid orderId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.OrderItems
|
||||
.Where(oi => oi.OrderId == orderId)
|
||||
.OrderBy(oi => oi.CreatedAt)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<OrderItem>> GetByOrderIdWithProductsAsync(Guid orderId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.OrderItems
|
||||
.Include(oi => oi.Product)
|
||||
.ThenInclude(p => p.Category)
|
||||
.Include(oi => oi.ProductVariant)
|
||||
.Where(oi => oi.OrderId == orderId)
|
||||
.OrderBy(oi => oi.CreatedAt)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<OrderItem>> GetByProductIdAsync(Guid productId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.OrderItems
|
||||
.Include(oi => oi.Order)
|
||||
.Where(oi => oi.ProductId == productId)
|
||||
.OrderByDescending(oi => oi.CreatedAt)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<OrderItem>> GetByProductVariantIdAsync(Guid productVariantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.OrderItems
|
||||
.Include(oi => oi.Order)
|
||||
.Where(oi => oi.ProductVariantId == productVariantId)
|
||||
.OrderByDescending(oi => oi.CreatedAt)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<OrderItem>> GetCustomizedItemsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.OrderItems
|
||||
.Include(oi => oi.Product)
|
||||
.Include(oi => oi.Order)
|
||||
.Where(oi => !string.IsNullOrEmpty(oi.CustomizationImageUrl) || !string.IsNullOrEmpty(oi.CustomizationDescription))
|
||||
.OrderByDescending(oi => oi.CreatedAt)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<OrderItem>> GetByDateRangeAsync(DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.OrderItems
|
||||
.Include(oi => oi.Order)
|
||||
.Include(oi => oi.Product)
|
||||
.Where(oi => oi.Order.OrderDate >= startDate && oi.Order.OrderDate <= endDate)
|
||||
.OrderByDescending(oi => oi.Order.OrderDate)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public Task<OrderItem> AddAsync(OrderItem orderItem, CancellationToken cancellationToken = default)
|
||||
{
|
||||
orderItem.Id = Guid.NewGuid();
|
||||
orderItem.CreatedAt = DateTime.UtcNow;
|
||||
orderItem.ModifiedAt = DateTime.UtcNow;
|
||||
|
||||
context.OrderItems.Add(orderItem);
|
||||
return Task.FromResult(orderItem);
|
||||
}
|
||||
|
||||
public Task<IEnumerable<OrderItem>> AddRangeAsync(IEnumerable<OrderItem> orderItems, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var items = orderItems.ToList();
|
||||
var utcNow = DateTime.UtcNow;
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
item.Id = Guid.NewGuid();
|
||||
item.CreatedAt = utcNow;
|
||||
item.ModifiedAt = utcNow;
|
||||
}
|
||||
|
||||
context.OrderItems.AddRange(items);
|
||||
return Task.FromResult<IEnumerable<OrderItem>>(items);
|
||||
}
|
||||
|
||||
public Task<OrderItem> UpdateAsync(OrderItem orderItem, CancellationToken cancellationToken = default)
|
||||
{
|
||||
orderItem.ModifiedAt = DateTime.UtcNow;
|
||||
context.OrderItems.Update(orderItem);
|
||||
return Task.FromResult(orderItem);
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var orderItem = await GetByIdAsync(id, cancellationToken);
|
||||
if (orderItem != null)
|
||||
{
|
||||
context.OrderItems.Remove(orderItem);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteByOrderIdAsync(Guid orderId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var orderItems = await context.OrderItems
|
||||
.Where(oi => oi.OrderId == orderId)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (orderItems.Any())
|
||||
{
|
||||
context.OrderItems.RemoveRange(orderItems);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ExistsAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.OrderItems
|
||||
.AnyAsync(oi => oi.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<decimal> GetTotalValueByOrderIdAsync(Guid orderId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.OrderItems
|
||||
.Where(oi => oi.OrderId == orderId)
|
||||
.SumAsync(oi => oi.TotalPrice, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<int> GetQuantityByProductIdAsync(Guid productId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.OrderItems
|
||||
.Where(oi => oi.ProductId == productId)
|
||||
.SumAsync(oi => oi.Quantity, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<int> GetQuantityByVariantIdAsync(Guid productVariantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.OrderItems
|
||||
.Where(oi => oi.ProductVariantId == productVariantId)
|
||||
.SumAsync(oi => oi.Quantity, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<Dictionary<Guid, int>> GetProductSalesCountAsync(
|
||||
DateTime? startDate = null,
|
||||
DateTime? endDate = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = context.OrderItems
|
||||
.Include(oi => oi.Order)
|
||||
.AsQueryable();
|
||||
|
||||
if (startDate.HasValue)
|
||||
{
|
||||
query = query.Where(oi => oi.Order.OrderDate >= startDate.Value);
|
||||
}
|
||||
|
||||
if (endDate.HasValue)
|
||||
{
|
||||
query = query.Where(oi => oi.Order.OrderDate <= endDate.Value);
|
||||
}
|
||||
|
||||
return await query
|
||||
.GroupBy(oi => oi.ProductId)
|
||||
.Select(g => new { ProductId = g.Key, TotalQuantity = g.Sum(oi => oi.Quantity) })
|
||||
.ToDictionaryAsync(x => x.ProductId, x => x.TotalQuantity, cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using Imprink.Domain.Entities;
|
||||
using Imprink.Domain.Models;
|
||||
using Imprink.Domain.Repositories;
|
||||
using Imprink.Infrastructure.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -14,159 +13,79 @@ public class OrderRepository(ApplicationDbContext context) : IOrderRepository
|
||||
.FirstOrDefaultAsync(o => o.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<Order?> GetByIdWithItemsAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
public async Task<Order?> GetByIdWithDetailsAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Orders
|
||||
.Include(o => o.OrderItems)
|
||||
.ThenInclude(oi => oi.Product)
|
||||
.Include(o => o.OrderItems)
|
||||
.ThenInclude(oi => oi.ProductVariant)
|
||||
.FirstOrDefaultAsync(o => o.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<Order?> GetByIdWithAddressAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Orders
|
||||
.Include(o => o.OrderAddress)
|
||||
.FirstOrDefaultAsync(o => o.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<Order?> GetByIdFullAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Orders
|
||||
.Include(o => o.User)
|
||||
.Include(o => o.OrderStatus)
|
||||
.Include(o => o.ShippingStatus)
|
||||
.Include(o => o.OrderAddress)
|
||||
.Include(o => o.OrderItems)
|
||||
.ThenInclude(oi => oi.Product)
|
||||
.ThenInclude(p => p.Category)
|
||||
.Include(o => o.OrderItems)
|
||||
.ThenInclude(oi => oi.ProductVariant)
|
||||
.Include(o => o.Product)
|
||||
.Include(o => o.ProductVariant)
|
||||
.Include(o => o.User)
|
||||
.FirstOrDefaultAsync(o => o.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<Order?> GetByOrderNumberAsync(string orderNumber, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Orders
|
||||
.Include(o => o.OrderStatus)
|
||||
.Include(o => o.ShippingStatus)
|
||||
.FirstOrDefaultAsync(o => o.OrderNumber == orderNumber, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<PagedResult<Order>> GetPagedAsync(
|
||||
OrderFilterParameters filterParameters,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = context.Orders
|
||||
.Include(o => o.User)
|
||||
.Include(o => o.OrderStatus)
|
||||
.Include(o => o.ShippingStatus)
|
||||
.AsQueryable();
|
||||
|
||||
if (!string.IsNullOrEmpty(filterParameters.UserId))
|
||||
{
|
||||
query = query.Where(o => o.UserId == filterParameters.UserId);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(filterParameters.OrderNumber))
|
||||
{
|
||||
query = query.Where(o => o.OrderNumber.Contains(filterParameters.OrderNumber));
|
||||
}
|
||||
|
||||
if (filterParameters.OrderStatusId.HasValue)
|
||||
{
|
||||
query = query.Where(o => o.OrderStatusId == filterParameters.OrderStatusId.Value);
|
||||
}
|
||||
|
||||
if (filterParameters.ShippingStatusId.HasValue)
|
||||
{
|
||||
query = query.Where(o => o.ShippingStatusId == filterParameters.ShippingStatusId.Value);
|
||||
}
|
||||
|
||||
if (filterParameters.StartDate.HasValue)
|
||||
{
|
||||
query = query.Where(o => o.OrderDate >= filterParameters.StartDate.Value);
|
||||
}
|
||||
|
||||
if (filterParameters.EndDate.HasValue)
|
||||
{
|
||||
query = query.Where(o => o.OrderDate <= filterParameters.EndDate.Value);
|
||||
}
|
||||
|
||||
if (filterParameters.MinTotalPrice.HasValue)
|
||||
{
|
||||
query = query.Where(o => o.TotalPrice >= filterParameters.MinTotalPrice.Value);
|
||||
}
|
||||
|
||||
if (filterParameters.MaxTotalPrice.HasValue)
|
||||
{
|
||||
query = query.Where(o => o.TotalPrice <= filterParameters.MaxTotalPrice.Value);
|
||||
}
|
||||
|
||||
query = filterParameters.SortBy.ToLower() switch
|
||||
{
|
||||
"orderdate" => filterParameters.SortDirection.Equals("DESC", StringComparison.CurrentCultureIgnoreCase)
|
||||
? query.OrderByDescending(o => o.OrderDate)
|
||||
: query.OrderBy(o => o.OrderDate),
|
||||
"totalprice" => filterParameters.SortDirection.Equals("DESC", StringComparison.CurrentCultureIgnoreCase)
|
||||
? query.OrderByDescending(o => o.TotalPrice)
|
||||
: query.OrderBy(o => o.TotalPrice),
|
||||
"ordernumber" => filterParameters.SortDirection.Equals("DESC", StringComparison.CurrentCultureIgnoreCase)
|
||||
? query.OrderByDescending(o => o.OrderNumber)
|
||||
: query.OrderBy(o => o.OrderNumber),
|
||||
_ => query.OrderByDescending(o => o.OrderDate)
|
||||
};
|
||||
|
||||
var totalCount = await query.CountAsync(cancellationToken);
|
||||
|
||||
var items = await query
|
||||
.Skip((filterParameters.PageNumber - 1) * filterParameters.PageSize)
|
||||
.Take(filterParameters.PageSize)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return new PagedResult<Order>
|
||||
{
|
||||
Items = items,
|
||||
TotalCount = totalCount,
|
||||
PageNumber = filterParameters.PageNumber,
|
||||
PageSize = filterParameters.PageSize
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Order>> GetByUserIdAsync(string userId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Orders
|
||||
.Include(o => o.OrderStatus)
|
||||
.Include(o => o.ShippingStatus)
|
||||
.Where(o => o.UserId == userId)
|
||||
.OrderByDescending(o => o.OrderDate)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Order>> GetByUserIdPagedAsync(
|
||||
string userId,
|
||||
int pageNumber,
|
||||
int pageSize,
|
||||
CancellationToken cancellationToken = default)
|
||||
public async Task<IEnumerable<Order>> GetByUserIdWithDetailsAsync(string userId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Orders
|
||||
.Include(o => o.OrderStatus)
|
||||
.Include(o => o.ShippingStatus)
|
||||
.Include(o => o.OrderAddress)
|
||||
.Include(o => o.Product)
|
||||
.Include(o => o.ProductVariant)
|
||||
.Where(o => o.UserId == userId)
|
||||
.OrderByDescending(o => o.OrderDate)
|
||||
.Skip((pageNumber - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Order>> GetByOrderStatusAsync(int orderStatusId, CancellationToken cancellationToken = default)
|
||||
public async Task<IEnumerable<Order>> GetByMerchantIdAsync(string merchantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Orders
|
||||
.Where(o => o.MerchantId == merchantId)
|
||||
.OrderByDescending(o => o.OrderDate)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Order>> GetByMerchantIdWithDetailsAsync(string merchantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Orders
|
||||
.Include(o => o.OrderStatus)
|
||||
.Include(o => o.ShippingStatus)
|
||||
.Include(o => o.OrderAddress)
|
||||
.Include(o => o.Product)
|
||||
.Include(o => o.ProductVariant)
|
||||
.Include(o => o.User)
|
||||
.Include(o => o.OrderStatus)
|
||||
.Include(o => o.ShippingStatus)
|
||||
.Where(o => o.OrderStatusId == orderStatusId)
|
||||
.Where(o => o.MerchantId == merchantId)
|
||||
.OrderByDescending(o => o.OrderDate)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Order>> GetByDateRangeAsync(DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Orders
|
||||
.Where(o => o.OrderDate >= startDate && o.OrderDate <= endDate)
|
||||
.OrderByDescending(o => o.OrderDate)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Order>> GetByStatusAsync(int statusId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Orders
|
||||
.Where(o => o.OrderStatusId == statusId)
|
||||
.OrderByDescending(o => o.OrderDate)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
@@ -174,52 +93,39 @@ public class OrderRepository(ApplicationDbContext context) : IOrderRepository
|
||||
public async Task<IEnumerable<Order>> GetByShippingStatusAsync(int shippingStatusId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Orders
|
||||
.Include(o => o.User)
|
||||
.Include(o => o.OrderStatus)
|
||||
.Include(o => o.ShippingStatus)
|
||||
.Where(o => o.ShippingStatusId == shippingStatusId)
|
||||
.OrderByDescending(o => o.OrderDate)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Order>> GetByDateRangeAsync(
|
||||
DateTime startDate,
|
||||
DateTime endDate,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Orders
|
||||
.Include(o => o.User)
|
||||
.Include(o => o.OrderStatus)
|
||||
.Include(o => o.ShippingStatus)
|
||||
.Where(o => o.OrderDate >= startDate && o.OrderDate <= endDate)
|
||||
.OrderByDescending(o => o.OrderDate)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public Task<Order> AddAsync(Order order, CancellationToken cancellationToken = default)
|
||||
{
|
||||
order.Id = Guid.NewGuid();
|
||||
order.CreatedAt = DateTime.UtcNow;
|
||||
order.ModifiedAt = DateTime.UtcNow;
|
||||
|
||||
context.Orders.Add(order);
|
||||
return Task.FromResult(order);
|
||||
}
|
||||
|
||||
public Task<Order> UpdateAsync(Order order, CancellationToken cancellationToken = default)
|
||||
public async Task<Order> UpdateAsync(Order order, CancellationToken cancellationToken = default)
|
||||
{
|
||||
order.ModifiedAt = DateTime.UtcNow;
|
||||
context.Orders.Update(order);
|
||||
return Task.FromResult(order);
|
||||
var existingOrder = await context.Orders
|
||||
.FirstOrDefaultAsync(o => o.Id == order.Id, cancellationToken);
|
||||
|
||||
if (existingOrder == null)
|
||||
throw new InvalidOperationException($"Order with ID {order.Id} not found");
|
||||
|
||||
context.Entry(existingOrder).CurrentValues.SetValues(order);
|
||||
return existingOrder;
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
public async Task<bool> DeleteAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var order = await GetByIdAsync(id, cancellationToken);
|
||||
if (order != null)
|
||||
{
|
||||
context.Orders.Remove(order);
|
||||
}
|
||||
var order = await context.Orders
|
||||
.FirstOrDefaultAsync(o => o.Id == id, cancellationToken);
|
||||
|
||||
if (order == null)
|
||||
return false;
|
||||
|
||||
context.Orders.Remove(order);
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> ExistsAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
@@ -228,38 +134,78 @@ public class OrderRepository(ApplicationDbContext context) : IOrderRepository
|
||||
.AnyAsync(o => o.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<bool> OrderNumberExistsAsync(string orderNumber, Guid? excludeId = null, CancellationToken cancellationToken = default)
|
||||
public async Task<bool> IsOrderNumberUniqueAsync(string orderNumber, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = context.Orders.Where(o => o.OrderNumber == orderNumber);
|
||||
|
||||
if (excludeId.HasValue)
|
||||
return !await context.Orders
|
||||
.AnyAsync(o => o.OrderNumber == orderNumber, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<bool> IsOrderNumberUniqueAsync(string orderNumber, Guid excludeOrderId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return !await context.Orders
|
||||
.AnyAsync(o => o.OrderNumber == orderNumber && o.Id != excludeOrderId, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<string> GenerateOrderNumberAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
string orderNumber;
|
||||
bool isUnique;
|
||||
|
||||
do
|
||||
{
|
||||
query = query.Where(o => o.Id != excludeId.Value);
|
||||
// Generate order number format: ORD-YYYYMMDD-XXXXXX (where X is random)
|
||||
var datePart = DateTime.UtcNow.ToString("yyyyMMdd");
|
||||
var randomPart = Random.Shared.Next(100000, 999999).ToString();
|
||||
orderNumber = $"ORD-{datePart}-{randomPart}";
|
||||
|
||||
isUnique = await IsOrderNumberUniqueAsync(orderNumber, cancellationToken);
|
||||
}
|
||||
while (!isUnique);
|
||||
|
||||
return await query.AnyAsync(cancellationToken);
|
||||
return orderNumber;
|
||||
}
|
||||
|
||||
public async Task<decimal> GetTotalRevenueAsync(CancellationToken cancellationToken = default)
|
||||
public async Task UpdateStatusAsync(Guid orderId, int statusId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Orders
|
||||
.Where(o => o.OrderStatusId != 5) // Assuming 5 is cancelled status
|
||||
.SumAsync(o => o.TotalPrice, cancellationToken);
|
||||
var order = await context.Orders
|
||||
.FirstOrDefaultAsync(o => o.Id == orderId, cancellationToken);
|
||||
|
||||
if (order != null)
|
||||
{
|
||||
order.OrderStatusId = statusId;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<decimal> GetTotalRevenueByDateRangeAsync(
|
||||
DateTime startDate,
|
||||
DateTime endDate,
|
||||
CancellationToken cancellationToken = default)
|
||||
public async Task UpdateShippingStatusAsync(Guid orderId, int shippingStatusId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Orders
|
||||
.Where(o => o.OrderDate >= startDate && o.OrderDate <= endDate && o.OrderStatusId != 5)
|
||||
.SumAsync(o => o.TotalPrice, cancellationToken);
|
||||
var order = await context.Orders
|
||||
.FirstOrDefaultAsync(o => o.Id == orderId, cancellationToken);
|
||||
|
||||
if (order != null)
|
||||
{
|
||||
order.ShippingStatusId = shippingStatusId;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<int> GetOrderCountByStatusAsync(int orderStatusId, CancellationToken cancellationToken = default)
|
||||
public async Task AssignMerchantAsync(Guid orderId, string merchantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.Orders
|
||||
.CountAsync(o => o.OrderStatusId == orderStatusId, cancellationToken);
|
||||
var order = await context.Orders
|
||||
.FirstOrDefaultAsync(o => o.Id == orderId, cancellationToken);
|
||||
|
||||
if (order != null)
|
||||
{
|
||||
order.MerchantId = merchantId;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UnassignMerchantAsync(Guid orderId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var order = await context.Orders
|
||||
.FirstOrDefaultAsync(o => o.Id == orderId, cancellationToken);
|
||||
|
||||
if (order != null)
|
||||
{
|
||||
order.MerchantId = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ public class UnitOfWork(
|
||||
IUserRoleRepository userRoleRepository,
|
||||
IRoleRepository roleRepository,
|
||||
IOrderRepository orderRepository,
|
||||
IOrderItemRepository orderItemRepository) : IUnitOfWork
|
||||
IAddressRepository addressRepository) : IUnitOfWork
|
||||
{
|
||||
public IProductRepository ProductRepository => productRepository;
|
||||
public IProductVariantRepository ProductVariantRepository => productVariantRepository;
|
||||
@@ -22,7 +22,7 @@ public class UnitOfWork(
|
||||
public IUserRoleRepository UserRoleRepository => userRoleRepository;
|
||||
public IRoleRepository RoleRepository => roleRepository;
|
||||
public IOrderRepository OrderRepository => orderRepository;
|
||||
public IOrderItemRepository OrderItemRepository => orderItemRepository;
|
||||
public IAddressRepository AddressRepository => addressRepository;
|
||||
|
||||
public async Task SaveAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ public static class StartupApplicationExtensions
|
||||
services.AddScoped<IUserRepository, UserRepository>();
|
||||
services.AddScoped<IUserRoleRepository, UserRoleRepository>();
|
||||
services.AddScoped<IOrderRepository, OrderRepository>();
|
||||
services.AddScoped<IOrderItemRepository, OrderItemRepository>();
|
||||
services.AddScoped<IAddressRepository, AddressRepository>();
|
||||
services.AddScoped<IUnitOfWork, UnitOfWork>();
|
||||
services.AddScoped<ICurrentUserService, CurrentUserService>();
|
||||
services.AddScoped<Seeder>();
|
||||
|
||||
Reference in New Issue
Block a user