Dev #15

Merged
bytegrip merged 8 commits from dev into main 2025-06-26 00:06:57 +00:00
27 changed files with 1381 additions and 1179 deletions
Showing only changes of commit 266aa529fa - Show all commits

View File

@@ -1,13 +1,14 @@
using AutoMapper; using AutoMapper;
using Imprink.Application.Dtos; using Imprink.Application.Dtos;
using Imprink.Application.Services;
using Imprink.Domain.Entities; using Imprink.Domain.Entities;
using MediatR; using MediatR;
using Microsoft.Extensions.Logging;
namespace Imprink.Application.Commands.Addresses; namespace Imprink.Application.Commands.Addresses;
public class CreateAddressCommand : IRequest<AddressDto> public class CreateAddressCommand : IRequest<AddressDto>
{ {
public string UserId { get; set; } = null!;
public string AddressType { get; set; } = null!; public string AddressType { get; set; } = null!;
public string? FirstName { get; set; } public string? FirstName { get; set; }
public string? LastName { get; set; } public string? LastName { get; set; }
@@ -27,7 +28,12 @@ public class CreateAddressCommand : IRequest<AddressDto>
public bool IsActive { get; set; } = true; public bool IsActive { get; set; } = true;
} }
public class CreateAddressHandler(IUnitOfWork uw, IMapper mapper) : IRequestHandler<CreateAddressCommand, AddressDto> public class CreateAddressHandler(
IUnitOfWork uw,
IMapper mapper,
ICurrentUserService userService,
ILogger<CreateAddressHandler> logger)
: IRequestHandler<CreateAddressCommand, AddressDto>
{ {
public async Task<AddressDto> Handle(CreateAddressCommand request, CancellationToken cancellationToken) public async Task<AddressDto> Handle(CreateAddressCommand request, CancellationToken cancellationToken)
{ {
@@ -35,6 +41,10 @@ public class CreateAddressHandler(IUnitOfWork uw, IMapper mapper) : IRequestHand
{ {
var address = mapper.Map<Address>(request); var address = mapper.Map<Address>(request);
address.UserId = userService.GetCurrentUserId()!;
address.CreatedAt = DateTime.UtcNow;
address.ModifiedAt = DateTime.UtcNow;
if (address.IsDefault) if (address.IsDefault)
{ {
var currentDefault = await uw.AddressRepository.GetDefaultByUserIdAsync(address.UserId, cancellationToken); var currentDefault = await uw.AddressRepository.GetDefaultByUserIdAsync(address.UserId, cancellationToken);
@@ -46,6 +56,8 @@ public class CreateAddressHandler(IUnitOfWork uw, IMapper mapper) : IRequestHand
} }
var createdAddress = await uw.AddressRepository.AddAsync(address, cancellationToken); var createdAddress = await uw.AddressRepository.AddAsync(address, cancellationToken);
await uw.SaveAsync(cancellationToken);
return mapper.Map<AddressDto>(createdAddress); return mapper.Map<AddressDto>(createdAddress);
}, cancellationToken); }, cancellationToken);
} }

View File

@@ -1,5 +1,7 @@
using AutoMapper; using AutoMapper;
using Imprink.Application.Dtos; using Imprink.Application.Dtos;
using Imprink.Application.Exceptions;
using Imprink.Application.Services;
using Imprink.Domain.Entities; using Imprink.Domain.Entities;
using MediatR; using MediatR;
@@ -7,7 +9,6 @@ namespace Imprink.Application.Commands.Orders;
public class CreateOrderCommand : IRequest<OrderDto> public class CreateOrderCommand : IRequest<OrderDto>
{ {
public string UserId { get; set; } = null!;
public decimal Amount { get; set; } public decimal Amount { get; set; }
public int Quantity { get; set; } public int Quantity { get; set; }
public Guid ProductId { get; set; } public Guid ProductId { get; set; }
@@ -15,29 +16,30 @@ public class CreateOrderCommand : IRequest<OrderDto>
public string? Notes { get; set; } public string? Notes { get; set; }
public string? MerchantId { get; set; } public string? MerchantId { get; set; }
public string? ComposingImageUrl { get; set; } public string? ComposingImageUrl { get; set; }
public string[] OriginalImageUrls { get; set; } = []; public string[]? OriginalImageUrls { get; set; } = [];
public string CustomizationImageUrl { get; set; } = null!; public string? CustomizationImageUrl { get; set; } = null!;
public string CustomizationDescription { get; set; } = null!; public string? CustomizationDescription { get; set; } = null!;
public Guid AddressId { get; set; } public Guid AddressId { get; set; }
} }
public class CreateOrderHandler(IUnitOfWork uw, IMapper mapper) : IRequestHandler<CreateOrderCommand, OrderDto> public class CreateOrderHandler(IUnitOfWork uw, IMapper mapper, ICurrentUserService userService) : IRequestHandler<CreateOrderCommand, OrderDto>
{ {
public async Task<OrderDto> Handle(CreateOrderCommand request, CancellationToken cancellationToken) public async Task<OrderDto> Handle(CreateOrderCommand request, CancellationToken cancellationToken)
{ {
return await uw.TransactAsync(async () => return await uw.TransactAsync(async () =>
{ {
var sourceAddress = await uw.AddressRepository.GetByIdAndUserIdAsync(request.AddressId, request.UserId, cancellationToken); var sourceAddress = await uw.AddressRepository.GetByIdAndUserIdAsync(request.AddressId, userService.GetCurrentUserId()!, cancellationToken);
if (sourceAddress == null) if (sourceAddress == null)
{ {
throw new ArgumentException($"Address with ID {request.AddressId} not found for user {request.UserId}"); throw new NotFoundException($"Address with ID {request.AddressId} not found for user {userService.GetCurrentUserId()!}");
} }
var order = mapper.Map<Order>(request); var order = mapper.Map<Order>(request);
order.UserId = userService.GetCurrentUserId()!;
order.OrderDate = DateTime.UtcNow; order.OrderDate = DateTime.UtcNow;
order.OrderStatusId = 1; order.OrderStatusId = 0;
order.ShippingStatusId = 1; order.ShippingStatusId = 0;
var createdOrder = await uw.OrderRepository.AddAsync(order, cancellationToken); var createdOrder = await uw.OrderRepository.AddAsync(order, cancellationToken);
@@ -70,6 +72,8 @@ public class CreateOrderHandler(IUnitOfWork uw, IMapper mapper) : IRequestHandle
{ {
createdOrder.ProductVariant = await uw.ProductVariantRepository.GetByIdAsync(createdOrder.ProductVariantId.Value, cancellationToken); createdOrder.ProductVariant = await uw.ProductVariantRepository.GetByIdAsync(createdOrder.ProductVariantId.Value, cancellationToken);
} }
await uw.SaveAsync(cancellationToken);
return mapper.Map<OrderDto>(createdOrder); return mapper.Map<OrderDto>(createdOrder);
}, cancellationToken); }, cancellationToken);

View File

@@ -21,6 +21,4 @@ public class AddressDto
public string? Instructions { get; set; } public string? Instructions { get; set; }
public bool IsDefault { get; set; } public bool IsDefault { get; set; }
public bool IsActive { get; set; } public bool IsActive { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
} }

View File

@@ -11,13 +11,11 @@ public class OrderDto
public Guid? ProductVariantId { get; set; } public Guid? ProductVariantId { get; set; }
public int OrderStatusId { get; set; } public int OrderStatusId { get; set; }
public int ShippingStatusId { get; set; } public int ShippingStatusId { get; set; }
public string OrderNumber { get; set; } = null!;
public string? Notes { get; set; } public string? Notes { get; set; }
public string? MerchantId { get; set; } public string? MerchantId { get; set; }
public string? ComposingImageUrl { get; set; } public string? CustomizationImageUrl { get; set; }
public string[] OriginalImageUrls { get; set; } = []; public string[]? OriginalImageUrls { get; set; } = [];
public string CustomizationImageUrl { get; set; } = null!; public string? CustomizationDescription { get; set; } = null!;
public string CustomizationDescription { get; set; } = null!;
public DateTime CreatedAt { get; set; } public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; } public DateTime UpdatedAt { get; set; }

View File

@@ -0,0 +1,23 @@
using AutoMapper;
using Imprink.Application.Commands.Addresses;
using Imprink.Application.Dtos;
using Imprink.Domain.Entities;
namespace Imprink.Application.Mappings;
public class AddressMappingProfile : Profile
{
public AddressMappingProfile()
{
CreateMap<CreateAddressCommand, Address>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.ModifiedAt, opt => opt.Ignore())
.ForMember(dest => dest.User, opt => opt.Ignore());
CreateMap<Address, AddressDto>();
CreateMap<AddressDto, Address>()
.ForMember(dest => dest.User, opt => opt.Ignore());
}
}

View File

@@ -0,0 +1,38 @@
using AutoMapper;
using Imprink.Application.Dtos;
using Imprink.Domain.Entities;
namespace Imprink.Application.Mappings;
public class OrderAddressMappingProfile : Profile
{
public OrderAddressMappingProfile()
{
CreateMap<OrderAddress, OrderAddressDto>();
CreateMap<OrderAddressDto, OrderAddress>()
.ForMember(dest => dest.Order, opt => opt.Ignore());
CreateMap<Address, OrderAddress>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.OrderId, opt => opt.Ignore())
.ForMember(dest => dest.Order, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.ModifiedAt, opt => opt.Ignore())
.ForMember(dest => dest.AddressType, opt => opt.MapFrom(src => src.AddressType))
.ForMember(dest => dest.FirstName, opt => opt.MapFrom(src => src.FirstName))
.ForMember(dest => dest.LastName, opt => opt.MapFrom(src => src.LastName))
.ForMember(dest => dest.Company, opt => opt.MapFrom(src => src.Company))
.ForMember(dest => dest.AddressLine1, opt => opt.MapFrom(src => src.AddressLine1))
.ForMember(dest => dest.AddressLine2, opt => opt.MapFrom(src => src.AddressLine2))
.ForMember(dest => dest.ApartmentNumber, opt => opt.MapFrom(src => src.ApartmentNumber))
.ForMember(dest => dest.BuildingNumber, opt => opt.MapFrom(src => src.BuildingNumber))
.ForMember(dest => dest.Floor, opt => opt.MapFrom(src => src.Floor))
.ForMember(dest => dest.City, opt => opt.MapFrom(src => src.City))
.ForMember(dest => dest.State, opt => opt.MapFrom(src => src.State))
.ForMember(dest => dest.PostalCode, opt => opt.MapFrom(src => src.PostalCode))
.ForMember(dest => dest.Country, opt => opt.MapFrom(src => src.Country))
.ForMember(dest => dest.PhoneNumber, opt => opt.MapFrom(src => src.PhoneNumber))
.ForMember(dest => dest.Instructions, opt => opt.MapFrom(src => src.Instructions));
}
}

View File

@@ -0,0 +1,36 @@
using AutoMapper;
using Imprink.Application.Commands.Orders;
using Imprink.Application.Dtos;
using Imprink.Domain.Entities;
namespace Imprink.Application.Mappings;
public class OrderMappingProfile : Profile
{
public OrderMappingProfile()
{
CreateMap<CreateOrderCommand, Order>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.OrderDate, opt => opt.Ignore())
.ForMember(dest => dest.OrderStatusId, opt => opt.Ignore())
.ForMember(dest => dest.ShippingStatusId, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.ModifiedAt, opt => opt.Ignore())
.ForMember(dest => dest.OrderStatus, opt => opt.Ignore())
.ForMember(dest => dest.User, opt => opt.Ignore())
.ForMember(dest => dest.ShippingStatus, opt => opt.Ignore())
.ForMember(dest => dest.OrderAddress, opt => opt.Ignore())
.ForMember(dest => dest.Product, opt => opt.Ignore())
.ForMember(dest => dest.ProductVariant, opt => opt.Ignore());
CreateMap<Order, OrderDto>();
CreateMap<OrderDto, Order>()
.ForMember(dest => dest.OrderStatus, opt => opt.Ignore())
.ForMember(dest => dest.User, opt => opt.Ignore())
.ForMember(dest => dest.ShippingStatus, opt => opt.Ignore())
.ForMember(dest => dest.OrderAddress, opt => opt.Ignore())
.ForMember(dest => dest.Product, opt => opt.Ignore())
.ForMember(dest => dest.ProductVariant, opt => opt.Ignore());
}
}

View File

@@ -0,0 +1,15 @@
using AutoMapper;
using Imprink.Application.Dtos;
using Imprink.Domain.Entities;
namespace Imprink.Application.Mappings;
public class OrderStatusMappingProfile : Profile
{
public OrderStatusMappingProfile()
{
CreateMap<OrderStatus, OrderStatusDto>();
CreateMap<OrderStatusDto, OrderStatus>();
}
}

View File

@@ -0,0 +1,15 @@
using AutoMapper;
using Imprink.Application.Dtos;
using Imprink.Domain.Entities;
namespace Imprink.Application.Mappings;
public class ShippingStatusMappingProfile : Profile
{
public ShippingStatusMappingProfile()
{
CreateMap<ShippingStatus, ShippingStatusDto>();
CreateMap<ShippingStatusDto, ShippingStatus>();
}
}

View File

@@ -2,7 +2,7 @@ namespace Imprink.Domain.Entities;
public class Order : EntityBase public class Order : EntityBase
{ {
public string UserId { get; set; } = null!; public required string UserId { get; set; }
public DateTime OrderDate { get; set; } public DateTime OrderDate { get; set; }
public decimal Amount { get; set; } public decimal Amount { get; set; }
public int Quantity { get; set; } public int Quantity { get; set; }
@@ -10,18 +10,17 @@ public class Order : EntityBase
public Guid? ProductVariantId { get; set; } public Guid? ProductVariantId { get; set; }
public int OrderStatusId { get; set; } public int OrderStatusId { get; set; }
public int ShippingStatusId { get; set; } public int ShippingStatusId { get; set; }
public string OrderNumber { get; set; } = null!;
public string? Notes { get; set; } public string? Notes { get; set; }
public string? MerchantId { get; set; } public string? MerchantId { get; set; }
public string? ComposingImageUrl { get; set; } public string? CustomizationImageUrl { get; set; }
public string[] OriginalImageUrls { get; set; } = []; public string[] OriginalImageUrls { get; set; } = [];
public string CustomizationImageUrl { get; set; } = null!; public string? CustomizationDescription { get; set; }
public string CustomizationDescription { get; set; } = null!;
public OrderStatus OrderStatus { get; set; } = null!; public virtual OrderStatus OrderStatus { get; set; } = null!;
public User User { get; set; } = null!; public virtual User User { get; set; } = null!;
public ShippingStatus ShippingStatus { get; set; } = null!; public virtual User? Merchant { get; set; }
public OrderAddress OrderAddress { get; set; } = null!; public virtual ShippingStatus ShippingStatus { get; set; } = null!;
public Product Product { get; set; } = null!; public virtual OrderAddress OrderAddress { get; set; } = null!;
public ProductVariant? ProductVariant { get; set; } public virtual Product Product { get; set; } = null!;
public virtual ProductVariant? ProductVariant { get; set; }
} }

View File

@@ -18,6 +18,6 @@ public class OrderAddress : EntityBase
public required string Country { get; set; } public required string Country { get; set; }
public string? PhoneNumber { get; set; } public string? PhoneNumber { get; set; }
public string? Instructions { get; set; } public string? Instructions { get; set; }
public virtual required Order Order { get; set; } public virtual Order Order { get; set; } = null!;
} }

View File

@@ -6,7 +6,7 @@ public interface IAddressRepository
{ {
Task<IEnumerable<Address>> GetByUserIdAsync(string userId, CancellationToken cancellationToken = default); Task<IEnumerable<Address>> GetByUserIdAsync(string userId, CancellationToken cancellationToken = default);
Task<IEnumerable<Address>> GetActiveByUserIdAsync(string userId, CancellationToken cancellationToken = default); Task<IEnumerable<Address>> GetActiveByUserIdAsync(string userId, CancellationToken cancellationToken = default);
Task<Address?> GetDefaultByUserIdAsync(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<IEnumerable<Address>> GetByUserIdAndTypeAsync(string userId, string addressType, CancellationToken cancellationToken = default);
Task<Address?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default); Task<Address?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
Task<Address?> GetByIdAndUserIdAsync(Guid id, string userId, CancellationToken cancellationToken = default); Task<Address?> GetByIdAndUserIdAsync(Guid id, string userId, CancellationToken cancellationToken = default);
@@ -14,7 +14,7 @@ public interface IAddressRepository
Task<Address> UpdateAsync(Address address, CancellationToken cancellationToken = default); Task<Address> UpdateAsync(Address address, CancellationToken cancellationToken = default);
Task<bool> DeleteAsync(Guid id, CancellationToken cancellationToken = default); Task<bool> DeleteAsync(Guid id, CancellationToken cancellationToken = default);
Task<bool> DeleteByUserIdAsync(Guid id, string userId, CancellationToken cancellationToken = default); Task<bool> DeleteByUserIdAsync(Guid id, string userId, CancellationToken cancellationToken = default);
Task SetDefaultAddressAsync(string userId, Guid addressId, CancellationToken cancellationToken = default); Task SetDefaultAddressAsync(string? userId, Guid addressId, CancellationToken cancellationToken = default);
Task DeactivateAddressAsync(Guid addressId, CancellationToken cancellationToken = default); Task DeactivateAddressAsync(Guid addressId, CancellationToken cancellationToken = default);
Task ActivateAddressAsync(Guid addressId, CancellationToken cancellationToken = default); Task ActivateAddressAsync(Guid addressId, CancellationToken cancellationToken = default);
Task<bool> ExistsAsync(Guid id, CancellationToken cancellationToken = default); Task<bool> ExistsAsync(Guid id, CancellationToken cancellationToken = default);

View File

@@ -7,7 +7,6 @@ public interface IOrderRepository
{ {
Task<Order?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default); Task<Order?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
Task<Order?> GetByIdWithDetailsAsync(Guid id, CancellationToken cancellationToken = default); Task<Order?> GetByIdWithDetailsAsync(Guid id, CancellationToken cancellationToken = default);
Task<Order?> GetByOrderNumberAsync(string orderNumber, CancellationToken cancellationToken = default);
Task<IEnumerable<Order>> GetByUserIdAsync(string userId, CancellationToken cancellationToken = default); Task<IEnumerable<Order>> GetByUserIdAsync(string userId, CancellationToken cancellationToken = default);
Task<IEnumerable<Order>> GetByUserIdWithDetailsAsync(string userId, CancellationToken cancellationToken = default); Task<IEnumerable<Order>> GetByUserIdWithDetailsAsync(string userId, CancellationToken cancellationToken = default);
Task<IEnumerable<Order>> GetByMerchantIdAsync(string merchantId, CancellationToken cancellationToken = default); Task<IEnumerable<Order>> GetByMerchantIdAsync(string merchantId, CancellationToken cancellationToken = default);
@@ -19,9 +18,6 @@ public interface IOrderRepository
Task<Order> UpdateAsync(Order order, CancellationToken cancellationToken = default); Task<Order> UpdateAsync(Order order, CancellationToken cancellationToken = default);
Task<bool> DeleteAsync(Guid id, CancellationToken cancellationToken = default); Task<bool> DeleteAsync(Guid id, CancellationToken cancellationToken = default);
Task<bool> ExistsAsync(Guid id, CancellationToken cancellationToken = default); Task<bool> ExistsAsync(Guid id, 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 UpdateStatusAsync(Guid orderId, int statusId, CancellationToken cancellationToken = default);
Task UpdateShippingStatusAsync(Guid orderId, int shippingStatusId, CancellationToken cancellationToken = default); Task UpdateShippingStatusAsync(Guid orderId, int shippingStatusId, CancellationToken cancellationToken = default);
Task AssignMerchantAsync(Guid orderId, string merchantId, CancellationToken cancellationToken = default); Task AssignMerchantAsync(Guid orderId, string merchantId, CancellationToken cancellationToken = default);

View File

@@ -75,7 +75,6 @@ public class AddressConfiguration : EntityBaseConfiguration<Address>
builder.HasOne(a => a.User) builder.HasOne(a => a.User)
.WithMany(u => u.Addresses) .WithMany(u => u.Addresses)
.HasForeignKey(a => a.UserId) .HasForeignKey(a => a.UserId)
.HasPrincipalKey(u => u.Id)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
builder.HasIndex(a => a.UserId) builder.HasIndex(a => a.UserId)
@@ -87,4 +86,4 @@ public class AddressConfiguration : EntityBaseConfiguration<Address>
builder.HasIndex(a => new { a.UserId, a.IsDefault }) builder.HasIndex(a => new { a.UserId, a.IsDefault })
.HasDatabaseName("IX_Address_User_Default"); .HasDatabaseName("IX_Address_User_Default");
} }
} }

View File

@@ -2,7 +2,7 @@ using Imprink.Domain.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Imprink.Infrastructure; namespace Imprink.Infrastructure.Configuration;
public class EntityBaseConfiguration<T> : IEntityTypeConfiguration<T> where T : EntityBase public class EntityBaseConfiguration<T> : IEntityTypeConfiguration<T> where T : EntityBase
{ {
@@ -12,20 +12,16 @@ public class EntityBaseConfiguration<T> : IEntityTypeConfiguration<T> where T :
builder.Property(e => e.Id) builder.Property(e => e.Id)
.HasDefaultValueSql("NEWID()"); .HasDefaultValueSql("NEWID()");
builder.Property(e => e.CreatedAt) builder.Property(e => e.CreatedAt);
.IsRequired();
builder.Property(e => e.ModifiedAt) builder.Property(e => e.ModifiedAt)
.IsRequired()
.HasDefaultValueSql("GETUTCDATE()"); .HasDefaultValueSql("GETUTCDATE()");
builder.Property(e => e.CreatedBy) builder.Property(e => e.CreatedBy)
.IsRequired()
.HasMaxLength(450); .HasMaxLength(450);
builder.Property(e => e.ModifiedBy) builder.Property(e => e.ModifiedBy)
.IsRequired()
.HasMaxLength(450); .HasMaxLength(450);
builder.HasIndex(e => e.CreatedAt) builder.HasIndex(e => e.CreatedAt)

View File

@@ -35,19 +35,12 @@ public class OrderConfiguration : EntityBaseConfiguration<Order>
builder.Property(o => o.ShippingStatusId) builder.Property(o => o.ShippingStatusId)
.IsRequired(); .IsRequired();
builder.Property(o => o.OrderNumber)
.IsRequired()
.HasMaxLength(50);
builder.Property(o => o.Notes) builder.Property(o => o.Notes)
.HasMaxLength(1000); .HasMaxLength(1000);
builder.Property(o => o.MerchantId) builder.Property(o => o.MerchantId)
.HasMaxLength(450); .HasMaxLength(450);
builder.Property(o => o.ComposingImageUrl)
.HasMaxLength(1000);
builder.Property(o => o.OriginalImageUrls) builder.Property(o => o.OriginalImageUrls)
.HasConversion( .HasConversion(
v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null), v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
@@ -55,11 +48,9 @@ public class OrderConfiguration : EntityBaseConfiguration<Order>
.HasColumnType("nvarchar(max)"); .HasColumnType("nvarchar(max)");
builder.Property(o => o.CustomizationImageUrl) builder.Property(o => o.CustomizationImageUrl)
.IsRequired()
.HasMaxLength(1000); .HasMaxLength(1000);
builder.Property(o => o.CustomizationDescription) builder.Property(o => o.CustomizationDescription)
.IsRequired()
.HasMaxLength(2000); .HasMaxLength(2000);
builder.HasOne(o => o.OrderStatus) builder.HasOne(o => o.OrderStatus)
@@ -80,13 +71,11 @@ public class OrderConfiguration : EntityBaseConfiguration<Order>
builder.HasOne(o => o.User) builder.HasOne(o => o.User)
.WithMany(u => u.Orders) .WithMany(u => u.Orders)
.HasForeignKey(o => o.UserId) .HasForeignKey(o => o.UserId)
.HasPrincipalKey(u => u.Id)
.OnDelete(DeleteBehavior.Restrict); .OnDelete(DeleteBehavior.Restrict);
builder.HasOne<User>() builder.HasOne(o => o.Merchant)
.WithMany(u => u.MerchantOrders) .WithMany(u => u.MerchantOrders)
.HasForeignKey(o => o.MerchantId) .HasForeignKey(o => o.MerchantId)
.HasPrincipalKey(u => u.Id)
.OnDelete(DeleteBehavior.SetNull); .OnDelete(DeleteBehavior.SetNull);
builder.HasOne(o => o.Product) builder.HasOne(o => o.Product)
@@ -102,10 +91,6 @@ public class OrderConfiguration : EntityBaseConfiguration<Order>
builder.HasIndex(o => o.UserId) builder.HasIndex(o => o.UserId)
.HasDatabaseName("IX_Order_UserId"); .HasDatabaseName("IX_Order_UserId");
builder.HasIndex(o => o.OrderNumber)
.IsUnique()
.HasDatabaseName("IX_Order_OrderNumber");
builder.HasIndex(o => o.OrderDate) builder.HasIndex(o => o.OrderDate)
.HasDatabaseName("IX_Order_OrderDate"); .HasDatabaseName("IX_Order_OrderDate");
@@ -133,4 +118,4 @@ public class OrderConfiguration : EntityBaseConfiguration<Order>
builder.HasIndex(o => new { o.ProductId, o.OrderDate }) builder.HasIndex(o => new { o.ProductId, o.OrderDate })
.HasDatabaseName("IX_Order_Product_Date"); .HasDatabaseName("IX_Order_Product_Date");
} }
} }

View File

@@ -34,18 +34,6 @@ public class ProductConfiguration : EntityBaseConfiguration<Product>
builder.Property(p => p.CategoryId) builder.Property(p => p.CategoryId)
.IsRequired(false); .IsRequired(false);
builder.Property(c => c.CreatedAt)
.IsRequired(false);
builder.Property(c => c.CreatedBy)
.IsRequired(false);
builder.Property(c => c.ModifiedAt)
.IsRequired(false);
builder.Property(c => c.ModifiedBy)
.IsRequired(false);
builder.HasOne(p => p.Category) builder.HasOne(p => p.Category)
.WithMany(c => c.Products) .WithMany(c => c.Products)

View File

@@ -37,18 +37,6 @@ public class ProductVariantConfiguration : EntityBaseConfiguration<ProductVarian
builder.Property(pv => pv.IsActive) builder.Property(pv => pv.IsActive)
.IsRequired() .IsRequired()
.HasDefaultValue(true); .HasDefaultValue(true);
builder.Property(c => c.CreatedAt)
.IsRequired(false);
builder.Property(c => c.CreatedBy)
.IsRequired(false);
builder.Property(c => c.ModifiedAt)
.IsRequired(false);
builder.Property(c => c.ModifiedBy)
.IsRequired(false);
builder.HasOne(pv => pv.Product) builder.HasOne(pv => pv.Product)
.WithMany(p => p.ProductVariants) .WithMany(p => p.ProductVariants)

View File

@@ -8,6 +8,8 @@ public class UserConfiguration : IEntityTypeConfiguration<User>
{ {
public void Configure(EntityTypeBuilder<User> builder) public void Configure(EntityTypeBuilder<User> builder)
{ {
builder.HasKey(u => u.Id);
builder.Property(u => u.Id) builder.Property(u => u.Id)
.HasMaxLength(450) .HasMaxLength(450)
.ValueGeneratedNever(); .ValueGeneratedNever();
@@ -47,12 +49,6 @@ public class UserConfiguration : IEntityTypeConfiguration<User>
builder.HasIndex(u => u.IsActive) builder.HasIndex(u => u.IsActive)
.HasDatabaseName("IX_User_IsActive"); .HasDatabaseName("IX_User_IsActive");
builder.HasMany(u => u.Addresses)
.WithOne()
.HasForeignKey(a => a.UserId)
.HasPrincipalKey(u => u.Id)
.OnDelete(DeleteBehavior.Cascade);
builder.Ignore(u => u.DefaultAddress); builder.Ignore(u => u.DefaultAddress);
builder.Ignore(u => u.Roles); builder.Ignore(u => u.Roles);
} }

View File

@@ -84,7 +84,7 @@ namespace Imprink.Infrastructure.Migrations
Name = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false), Name = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
Nickname = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false), Nickname = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
Email = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false), Email = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
EmailVerified = table.Column<bool>(type: "bit", maxLength: 100, nullable: false), EmailVerified = table.Column<bool>(type: "bit", nullable: false),
FirstName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true), FirstName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
LastName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true), LastName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
PhoneNumber = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true), PhoneNumber = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
@@ -130,17 +130,26 @@ namespace Imprink.Infrastructure.Migrations
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWID()"), Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWID()"),
UserId = table.Column<string>(type: "nvarchar(450)", maxLength: 450, nullable: false), UserId = table.Column<string>(type: "nvarchar(450)", maxLength: 450, nullable: false),
AddressType = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false), AddressType = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
Street = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false), FirstName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
LastName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
Company = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
AddressLine1 = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
AddressLine2 = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
ApartmentNumber = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
BuildingNumber = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
Floor = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
City = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false), City = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
State = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false), State = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
PostalCode = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: false), PostalCode = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: false),
Country = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false), Country = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
PhoneNumber = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
Instructions = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
IsDefault = table.Column<bool>(type: "bit", nullable: false, defaultValue: false), IsDefault = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
IsActive = table.Column<bool>(type: "bit", nullable: false, defaultValue: true), IsActive = table.Column<bool>(type: "bit", nullable: false, defaultValue: true),
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false), CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
ModifiedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()"), ModifiedAt = table.Column<DateTime>(type: "datetime2", nullable: true, defaultValueSql: "GETUTCDATE()"),
CreatedBy = table.Column<string>(type: "nvarchar(450)", maxLength: 450, nullable: false), CreatedBy = table.Column<string>(type: "nvarchar(450)", maxLength: 450, nullable: true),
ModifiedBy = table.Column<string>(type: "nvarchar(450)", maxLength: 450, nullable: false) ModifiedBy = table.Column<string>(type: "nvarchar(450)", maxLength: 450, nullable: true)
}, },
constraints: table => constraints: table =>
{ {
@@ -153,46 +162,6 @@ namespace Imprink.Infrastructure.Migrations
onDelete: ReferentialAction.Cascade); onDelete: ReferentialAction.Cascade);
}); });
migrationBuilder.CreateTable(
name: "Orders",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWID()"),
UserId = table.Column<string>(type: "nvarchar(450)", maxLength: 450, nullable: false),
OrderDate = table.Column<DateTime>(type: "datetime2", nullable: false),
TotalPrice = table.Column<decimal>(type: "decimal(18,2)", nullable: false),
OrderStatusId = table.Column<int>(type: "int", nullable: false),
ShippingStatusId = table.Column<int>(type: "int", nullable: false),
OrderNumber = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
Notes = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: false),
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
ModifiedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()"),
CreatedBy = table.Column<string>(type: "nvarchar(450)", maxLength: 450, nullable: false),
ModifiedBy = table.Column<string>(type: "nvarchar(450)", maxLength: 450, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Orders", x => x.Id);
table.ForeignKey(
name: "FK_Orders_OrderStatuses_OrderStatusId",
column: x => x.OrderStatusId,
principalTable: "OrderStatuses",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_Orders_ShippingStatuses_ShippingStatusId",
column: x => x.ShippingStatusId,
principalTable: "ShippingStatuses",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_Orders_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "UserRole", name: "UserRole",
columns: table => new columns: table => new
@@ -246,21 +215,95 @@ namespace Imprink.Infrastructure.Migrations
onDelete: ReferentialAction.Cascade); onDelete: ReferentialAction.Cascade);
}); });
migrationBuilder.CreateTable(
name: "Orders",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWID()"),
UserId = table.Column<string>(type: "nvarchar(450)", maxLength: 450, nullable: false),
OrderDate = table.Column<DateTime>(type: "datetime2", nullable: false),
Amount = table.Column<decimal>(type: "decimal(18,2)", nullable: false),
Quantity = table.Column<int>(type: "int", nullable: false, defaultValue: 1),
ProductId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
ProductVariantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
OrderStatusId = table.Column<int>(type: "int", nullable: false),
ShippingStatusId = table.Column<int>(type: "int", nullable: false),
Notes = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
MerchantId = table.Column<string>(type: "nvarchar(450)", maxLength: 450, nullable: true),
CustomizationImageUrl = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
OriginalImageUrls = table.Column<string>(type: "nvarchar(max)", nullable: false),
CustomizationDescription = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: true),
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
ModifiedAt = table.Column<DateTime>(type: "datetime2", nullable: true, defaultValueSql: "GETUTCDATE()"),
CreatedBy = table.Column<string>(type: "nvarchar(450)", maxLength: 450, nullable: true),
ModifiedBy = table.Column<string>(type: "nvarchar(450)", maxLength: 450, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Orders", x => x.Id);
table.ForeignKey(
name: "FK_Orders_OrderStatuses_OrderStatusId",
column: x => x.OrderStatusId,
principalTable: "OrderStatuses",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_Orders_ProductVariants_ProductVariantId",
column: x => x.ProductVariantId,
principalTable: "ProductVariants",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_Orders_Products_ProductId",
column: x => x.ProductId,
principalTable: "Products",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_Orders_ShippingStatuses_ShippingStatusId",
column: x => x.ShippingStatusId,
principalTable: "ShippingStatuses",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_Orders_Users_MerchantId",
column: x => x.MerchantId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "FK_Orders_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "OrderAddresses", name: "OrderAddresses",
columns: table => new columns: table => new
{ {
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWID()"), Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWID()"),
OrderId = table.Column<Guid>(type: "uniqueidentifier", nullable: false), OrderId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Street = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false), AddressType = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
FirstName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
LastName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
Company = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
AddressLine1 = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
AddressLine2 = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
ApartmentNumber = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
BuildingNumber = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
Floor = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
City = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false), City = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
State = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false), State = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
PostalCode = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: false), PostalCode = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: false),
Country = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false), Country = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false), PhoneNumber = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
ModifiedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()"), Instructions = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
CreatedBy = table.Column<string>(type: "nvarchar(450)", maxLength: 450, nullable: false), CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
ModifiedBy = table.Column<string>(type: "nvarchar(450)", maxLength: 450, nullable: false) ModifiedAt = table.Column<DateTime>(type: "datetime2", nullable: true, defaultValueSql: "GETUTCDATE()"),
CreatedBy = table.Column<string>(type: "nvarchar(450)", maxLength: 450, nullable: true),
ModifiedBy = table.Column<string>(type: "nvarchar(450)", maxLength: 450, nullable: true)
}, },
constraints: table => constraints: table =>
{ {
@@ -273,47 +316,6 @@ namespace Imprink.Infrastructure.Migrations
onDelete: ReferentialAction.Cascade); onDelete: ReferentialAction.Cascade);
}); });
migrationBuilder.CreateTable(
name: "OrderItems",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWID()"),
OrderId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
ProductId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
ProductVariantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Quantity = table.Column<int>(type: "int", nullable: false, defaultValue: 1),
UnitPrice = table.Column<decimal>(type: "decimal(18,2)", nullable: false),
TotalPrice = table.Column<decimal>(type: "decimal(18,2)", nullable: false),
CustomizationImageUrl = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: false),
CustomizationDescription = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: false),
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
ModifiedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()"),
CreatedBy = table.Column<string>(type: "nvarchar(450)", maxLength: 450, nullable: false),
ModifiedBy = table.Column<string>(type: "nvarchar(450)", maxLength: 450, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_OrderItems", x => x.Id);
table.ForeignKey(
name: "FK_OrderItems_Orders_OrderId",
column: x => x.OrderId,
principalTable: "Orders",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_OrderItems_ProductVariants_ProductVariantId",
column: x => x.ProductVariantId,
principalTable: "ProductVariants",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_OrderItems_Products_ProductId",
column: x => x.ProductId,
principalTable: "Products",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.InsertData( migrationBuilder.InsertData(
table: "Categories", table: "Categories",
columns: new[] { "Id", "CreatedAt", "CreatedBy", "Description", "ImageUrl", "IsActive", "ModifiedAt", "ModifiedBy", "Name", "ParentCategoryId", "SortOrder" }, columns: new[] { "Id", "CreatedAt", "CreatedBy", "Description", "ImageUrl", "IsActive", "ModifiedAt", "ModifiedBy", "Name", "ParentCategoryId", "SortOrder" },
@@ -446,41 +448,6 @@ namespace Imprink.Infrastructure.Migrations
column: "OrderId", column: "OrderId",
unique: true); unique: true);
migrationBuilder.CreateIndex(
name: "IX_OrderItem_CreatedAt",
table: "OrderItems",
column: "CreatedAt");
migrationBuilder.CreateIndex(
name: "IX_OrderItem_CreatedBy",
table: "OrderItems",
column: "CreatedBy");
migrationBuilder.CreateIndex(
name: "IX_OrderItem_ModifiedAt",
table: "OrderItems",
column: "ModifiedAt");
migrationBuilder.CreateIndex(
name: "IX_OrderItem_Order_Product",
table: "OrderItems",
columns: new[] { "OrderId", "ProductId" });
migrationBuilder.CreateIndex(
name: "IX_OrderItem_OrderId",
table: "OrderItems",
column: "OrderId");
migrationBuilder.CreateIndex(
name: "IX_OrderItem_ProductId",
table: "OrderItems",
column: "ProductId");
migrationBuilder.CreateIndex(
name: "IX_OrderItem_ProductVariantId",
table: "OrderItems",
column: "ProductVariantId");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_Order_CreatedAt", name: "IX_Order_CreatedAt",
table: "Orders", table: "Orders",
@@ -491,6 +458,16 @@ namespace Imprink.Infrastructure.Migrations
table: "Orders", table: "Orders",
column: "CreatedBy"); column: "CreatedBy");
migrationBuilder.CreateIndex(
name: "IX_Order_Merchant_Date",
table: "Orders",
columns: new[] { "MerchantId", "OrderDate" });
migrationBuilder.CreateIndex(
name: "IX_Order_MerchantId",
table: "Orders",
column: "MerchantId");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_Order_ModifiedAt", name: "IX_Order_ModifiedAt",
table: "Orders", table: "Orders",
@@ -501,17 +478,26 @@ namespace Imprink.Infrastructure.Migrations
table: "Orders", table: "Orders",
column: "OrderDate"); column: "OrderDate");
migrationBuilder.CreateIndex(
name: "IX_Order_OrderNumber",
table: "Orders",
column: "OrderNumber",
unique: true);
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_Order_OrderStatusId", name: "IX_Order_OrderStatusId",
table: "Orders", table: "Orders",
column: "OrderStatusId"); column: "OrderStatusId");
migrationBuilder.CreateIndex(
name: "IX_Order_Product_Date",
table: "Orders",
columns: new[] { "ProductId", "OrderDate" });
migrationBuilder.CreateIndex(
name: "IX_Order_ProductId",
table: "Orders",
column: "ProductId");
migrationBuilder.CreateIndex(
name: "IX_Order_ProductVariantId",
table: "Orders",
column: "ProductVariantId");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_Order_ShippingStatusId", name: "IX_Order_ShippingStatusId",
table: "Orders", table: "Orders",
@@ -659,24 +645,21 @@ namespace Imprink.Infrastructure.Migrations
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "OrderAddresses"); name: "OrderAddresses");
migrationBuilder.DropTable(
name: "OrderItems");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "UserRole"); name: "UserRole");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "Orders"); name: "Orders");
migrationBuilder.DropTable(
name: "ProductVariants");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "Roles"); name: "Roles");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "OrderStatuses"); name: "OrderStatuses");
migrationBuilder.DropTable(
name: "ProductVariants");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "ShippingStatuses"); name: "ShippingStatuses");

View File

@@ -25,7 +25,7 @@ public class AddressRepository(ApplicationDbContext context) : IAddressRepositor
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
} }
public async Task<Address?> GetDefaultByUserIdAsync(string userId, CancellationToken cancellationToken = default) public async Task<Address?> GetDefaultByUserIdAsync(string? userId, CancellationToken cancellationToken = default)
{ {
return await context.Addresses return await context.Addresses
.FirstOrDefaultAsync(a => a.UserId == userId && a.IsDefault && a.IsActive, cancellationToken); .FirstOrDefaultAsync(a => a.UserId == userId && a.IsDefault && a.IsActive, cancellationToken);
@@ -103,7 +103,7 @@ public class AddressRepository(ApplicationDbContext context) : IAddressRepositor
return true; return true;
} }
public async Task SetDefaultAddressAsync(string userId, Guid addressId, CancellationToken cancellationToken = default) public async Task SetDefaultAddressAsync(string? userId, Guid addressId, CancellationToken cancellationToken = default)
{ {
await UnsetDefaultAddressesAsync(userId, cancellationToken); await UnsetDefaultAddressesAsync(userId, cancellationToken);
@@ -154,7 +154,7 @@ public class AddressRepository(ApplicationDbContext context) : IAddressRepositor
.AnyAsync(a => a.Id == id && a.UserId == userId, cancellationToken); .AnyAsync(a => a.Id == id && a.UserId == userId, cancellationToken);
} }
private async Task UnsetDefaultAddressesAsync(string userId, CancellationToken cancellationToken = default) private async Task UnsetDefaultAddressesAsync(string? userId, CancellationToken cancellationToken = default)
{ {
var defaultAddresses = await context.Addresses var defaultAddresses = await context.Addresses
.Where(a => a.UserId == userId && a.IsDefault) .Where(a => a.UserId == userId && a.IsDefault)

View File

@@ -25,12 +25,6 @@ public class OrderRepository(ApplicationDbContext context) : IOrderRepository
.FirstOrDefaultAsync(o => o.Id == id, cancellationToken); .FirstOrDefaultAsync(o => o.Id == id, cancellationToken);
} }
public async Task<Order?> GetByOrderNumberAsync(string orderNumber, CancellationToken cancellationToken = default)
{
return await context.Orders
.FirstOrDefaultAsync(o => o.OrderNumber == orderNumber, cancellationToken);
}
public async Task<IEnumerable<Order>> GetByUserIdAsync(string userId, CancellationToken cancellationToken = default) public async Task<IEnumerable<Order>> GetByUserIdAsync(string userId, CancellationToken cancellationToken = default)
{ {
return await context.Orders return await context.Orders
@@ -134,37 +128,6 @@ public class OrderRepository(ApplicationDbContext context) : IOrderRepository
.AnyAsync(o => o.Id == id, cancellationToken); .AnyAsync(o => o.Id == id, cancellationToken);
} }
public async Task<bool> IsOrderNumberUniqueAsync(string orderNumber, CancellationToken cancellationToken = default)
{
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
{
// 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 orderNumber;
}
public async Task UpdateStatusAsync(Guid orderId, int statusId, CancellationToken cancellationToken = default) public async Task UpdateStatusAsync(Guid orderId, int statusId, CancellationToken cancellationToken = default)
{ {
var order = await context.Orders var order = await context.Orders

View File

@@ -52,7 +52,6 @@ public class UnitOfWork(
try try
{ {
var result = await operation(); var result = await operation();
await SaveAsync(cancellationToken);
await CommitTransactionAsync(cancellationToken); await CommitTransactionAsync(cancellationToken);
return result; return result;
} }
@@ -69,7 +68,6 @@ public class UnitOfWork(
try try
{ {
await operation(); await operation();
await SaveAsync(cancellationToken);
await CommitTransactionAsync(cancellationToken); await CommitTransactionAsync(cancellationToken);
} }
catch catch

View File

@@ -0,0 +1,60 @@
using Imprink.Application.Commands.Addresses;
using Imprink.Application.Dtos;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Imprink.WebApi.Controllers;
[ApiController]
[Route("/api/addresses")]
public class AddressesController(IMediator mediator) : ControllerBase
{
[HttpGet("{id:guid}")]
[Authorize]
public async Task<ActionResult<AddressDto>> GetAddressById(
Guid id,
[FromQuery] string? userId = null,
CancellationToken cancellationToken = default)
{
var result = await mediator.Send(new GetAddressByIdQuery
{
Id = id,
UserId = userId
}, cancellationToken);
if (result == null)
return NotFound();
return Ok(result);
}
[HttpGet("user/{userId}")]
[Authorize]
public async Task<ActionResult<IEnumerable<AddressDto>>> GetAddressesByUserId(
string userId,
[FromQuery] bool activeOnly = false,
[FromQuery] string? addressType = null,
CancellationToken cancellationToken = default)
{
var result = await mediator.Send(new GetAddressesByUserIdQuery
{
UserId = userId,
ActiveOnly = activeOnly,
AddressType = addressType
}, cancellationToken);
return Ok(result);
}
[HttpPost]
[Authorize]
public async Task<ActionResult<AddressDto>> CreateAddress(
[FromBody] CreateAddressCommand command,
CancellationToken cancellationToken = default)
{
var result = await mediator.Send(command, cancellationToken);
return CreatedAtAction(nameof(GetAddressById), new { id = result.Id }, result);
}
}

View File

@@ -0,0 +1,74 @@
using Imprink.Application.Commands.Orders;
using Imprink.Application.Dtos;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Imprink.WebApi.Controllers;
[ApiController]
[Route("/api/orders")]
public class OrdersController(IMediator mediator) : ControllerBase
{
[HttpGet("{id:guid}")]
[Authorize]
public async Task<ActionResult<OrderDto>> GetOrderById(
Guid id,
[FromQuery] bool includeDetails = false,
CancellationToken cancellationToken = default)
{
var result = await mediator.Send(new GetOrderByIdQuery
{
Id = id,
IncludeDetails = includeDetails
}, cancellationToken);
if (result == null)
return NotFound();
return Ok(result);
}
[HttpGet("user/{userId}")]
[Authorize]
public async Task<ActionResult<IEnumerable<OrderDto>>> GetOrdersByUserId(
string userId,
[FromQuery] bool includeDetails = false,
CancellationToken cancellationToken = default)
{
var result = await mediator.Send(new GetOrdersByUserIdQuery
{
UserId = userId,
IncludeDetails = includeDetails
}, cancellationToken);
return Ok(result);
}
[HttpGet("merchant/{merchantId}")]
[Authorize(Roles = "Admin,Merchant")]
public async Task<ActionResult<IEnumerable<OrderDto>>> GetOrdersByMerchantId(
string merchantId,
[FromQuery] bool includeDetails = false,
CancellationToken cancellationToken = default)
{
var result = await mediator.Send(new GetOrdersByMerchantIdQuery
{
MerchantId = merchantId,
IncludeDetails = includeDetails
}, cancellationToken);
return Ok(result);
}
[HttpPost]
[Authorize]
public async Task<ActionResult<OrderDto>> CreateOrder(
[FromBody] CreateOrderCommand command,
CancellationToken cancellationToken = default)
{
var result = await mediator.Send(command, cancellationToken);
return CreatedAtAction(nameof(GetOrderById), new { id = result.Id }, result);
}
}