feat: enhance user club feature management with sorting and creation timestamps
This commit is contained in:
+4
-1
@@ -31,8 +31,11 @@ public class GetUserClubFeaturesQueryHandler : IRequestHandler<GetUserClubFeatur
|
||||
FeatureDescription = ucf.ClubFeature.Description,
|
||||
IsActive = ucf.IsActive,
|
||||
GrantedAt = ucf.GrantedAt,
|
||||
Notes = ucf.Notes
|
||||
Notes = ucf.Notes,
|
||||
SortOrder = ucf.ClubFeature.SortOrder,
|
||||
CreatedAt = ucf.Created
|
||||
})
|
||||
.OrderBy(ucf => ucf.SortOrder)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return userClubFeatures;
|
||||
|
||||
+12
-2
@@ -31,7 +31,7 @@ public class UserClubFeatureDto
|
||||
public string FeatureTitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// توضیحات ویژگی
|
||||
/// توضیحات کوتاه ویژگی
|
||||
/// </summary>
|
||||
public string? FeatureDescription { get; set; }
|
||||
|
||||
@@ -46,7 +46,17 @@ public class UserClubFeatureDto
|
||||
public DateTime GrantedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// یادداشت
|
||||
/// یادداشت (برای نمایش در مدال جزئیات)
|
||||
/// </summary>
|
||||
public string? Notes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ترتیب نمایش
|
||||
/// </summary>
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// تاریخ ایجاد رکورد
|
||||
/// </summary>
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ public record UpdateUserAddressCommand : IRequest<Unit>
|
||||
//شناسه
|
||||
public long Id { get; init; }
|
||||
//شناسه کاربر
|
||||
public long UserId { get; init; }
|
||||
|
||||
//عنوان
|
||||
public string Title { get; init; }
|
||||
//آدرس
|
||||
|
||||
+1
-2
@@ -5,8 +5,7 @@ public class UpdateUserAddressCommandValidator : AbstractValidator<UpdateUserAdd
|
||||
{
|
||||
RuleFor(model => model.Id)
|
||||
.NotNull();
|
||||
RuleFor(model => model.UserId)
|
||||
.NotNull();
|
||||
|
||||
RuleFor(model => model.Title)
|
||||
.NotEmpty();
|
||||
RuleFor(model => model.Address)
|
||||
|
||||
+40
@@ -32,6 +32,34 @@ public class
|
||||
if (user.UserCarts.Count == 0)
|
||||
throw new NotFoundException("UserCart", request.UserId);
|
||||
|
||||
// چک موجودی محصولات
|
||||
var outOfStockProducts = new List<string>();
|
||||
var insufficientStockProducts = new List<(string Title, int Requested, int Available)>();
|
||||
|
||||
foreach (var cartItem in user.UserCarts)
|
||||
{
|
||||
if (cartItem.Product.RemainingCount <= 0)
|
||||
{
|
||||
outOfStockProducts.Add(cartItem.Product.Title);
|
||||
}
|
||||
else if (cartItem.Product.RemainingCount < cartItem.Count)
|
||||
{
|
||||
insufficientStockProducts.Add((cartItem.Product.Title, cartItem.Count, cartItem.Product.RemainingCount));
|
||||
}
|
||||
}
|
||||
|
||||
if (outOfStockProducts.Any())
|
||||
{
|
||||
throw new Exception($"محصولات زیر ناموجود شدهاند: {string.Join("، ", outOfStockProducts)}");
|
||||
}
|
||||
|
||||
if (insufficientStockProducts.Any())
|
||||
{
|
||||
var messages = insufficientStockProducts.Select(p =>
|
||||
$"«{p.Title}»: درخواست {p.Requested} عدد، موجودی {p.Available} عدد");
|
||||
throw new Exception($"موجودی محصولات زیر کافی نیست: {string.Join(" | ", messages)}");
|
||||
}
|
||||
|
||||
if (user.UserCarts.Sum(s => s.Count * s.Product.Price) != request.TotalAmount)
|
||||
throw new Exception("مبلغ سفارش با مجموع سبد خرید مطابقت ندارد.");
|
||||
|
||||
@@ -100,8 +128,20 @@ public class
|
||||
OrderId = newOrder.Id
|
||||
});
|
||||
await _context.FactorDetails.AddRangeAsync(factorDetailsList, cancellationToken);
|
||||
|
||||
// کاهش موجودی محصولات و افزایش تعداد فروش
|
||||
foreach (var cartItem in user.UserCarts)
|
||||
{
|
||||
cartItem.Product.RemainingCount -= cartItem.Count;
|
||||
cartItem.Product.SaleCount += cartItem.Count;
|
||||
}
|
||||
|
||||
user.UserCarts.Clear();
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
_logger.LogInformation("Order {OrderId} completed. Stock updated for {ProductCount} products.",
|
||||
newOrder.Id, factorDetailsList.Count());
|
||||
|
||||
var finalResult = new SubmitShopBuyOrderResponseDto()
|
||||
{
|
||||
Id = newOrder.Id,
|
||||
|
||||
@@ -11,19 +11,17 @@ public class ClubFeature : BaseAuditableEntity
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// توضیحات
|
||||
/// توضیحات کوتاه
|
||||
/// </summary>
|
||||
public string? Description { get; set; }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// وضعیت فعال/غیرفعال
|
||||
/// </summary>
|
||||
public bool IsActive { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// امتیاز لازم برای دریافت (اختیاری)
|
||||
/// </summary>
|
||||
public int? RequiredPoints { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ترتیب نمایش
|
||||
|
||||
-1
@@ -19,7 +19,6 @@ public class ClubFeatureConfiguration : IEntityTypeConfiguration<ClubFeature>
|
||||
builder.Property(entity => entity.Title).IsRequired().HasMaxLength(200);
|
||||
builder.Property(entity => entity.Description).IsRequired(false).HasMaxLength(1000);
|
||||
builder.Property(entity => entity.IsActive).IsRequired();
|
||||
builder.Property(entity => entity.RequiredPoints).IsRequired(false);
|
||||
builder.Property(entity => entity.SortOrder).IsRequired();
|
||||
|
||||
// Index برای IsActive و SortOrder
|
||||
|
||||
+3520
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class u15 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "RequiredPoints",
|
||||
schema: "CMS",
|
||||
table: "ClubFeatures");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "RequiredPoints",
|
||||
schema: "CMS",
|
||||
table: "ClubFeatures",
|
||||
type: "int",
|
||||
nullable: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
-3
@@ -106,9 +106,6 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
||||
b.Property<string>("LastModifiedBy")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int?>("RequiredPoints")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
||||
@@ -249,6 +249,8 @@ message UserClubFeatureModel
|
||||
bool is_active = 7;
|
||||
google.protobuf.Timestamp granted_at = 8;
|
||||
string notes = 9;
|
||||
int32 sort_order = 10;
|
||||
google.protobuf.Timestamp created_at = 11;
|
||||
}
|
||||
|
||||
// ToggleUserClubFeature Command
|
||||
|
||||
@@ -60,7 +60,8 @@ message DeactivateConfigurationRequest
|
||||
// GetByKey Query
|
||||
message GetConfigurationByKeyRequest
|
||||
{
|
||||
string key = 1;
|
||||
int32 scope = 1;
|
||||
string key = 2;
|
||||
}
|
||||
|
||||
message GetConfigurationByKeyResponse
|
||||
@@ -77,11 +78,17 @@ message GetConfigurationByKeyResponse
|
||||
|
||||
// GetAll Query
|
||||
message GetAllConfigurationsRequest
|
||||
{
|
||||
messages.PaginationState pagination_state = 1;
|
||||
google.protobuf.StringValue sort_by = 2;
|
||||
GetAllConfigurationsFilter filter = 3;
|
||||
}
|
||||
|
||||
message GetAllConfigurationsFilter
|
||||
{
|
||||
google.protobuf.Int32Value scope = 1;
|
||||
google.protobuf.BoolValue is_active = 2;
|
||||
int32 page_index = 3;
|
||||
int32 page_size = 4;
|
||||
google.protobuf.StringValue key_contains = 2;
|
||||
google.protobuf.BoolValue is_active = 3;
|
||||
}
|
||||
|
||||
message GetAllConfigurationsResponse
|
||||
|
||||
@@ -26,7 +26,9 @@ public class ClubFeatureProfile : IRegister
|
||||
.Map(dest => dest.FeatureDescription, src => src.FeatureDescription ?? "")
|
||||
.Map(dest => dest.IsActive, src => src.IsActive)
|
||||
.Map(dest => dest.GrantedAt, src => Timestamp.FromDateTime(DateTime.SpecifyKind(src.GrantedAt, DateTimeKind.Utc)))
|
||||
.Map(dest => dest.Notes, src => src.Notes ?? "");
|
||||
.Map(dest => dest.Notes, src => src.Notes ?? "")
|
||||
.Map(dest => dest.SortOrder, src => src.SortOrder)
|
||||
.Map(dest => dest.CreatedAt, src => Timestamp.FromDateTime(DateTime.SpecifyKind(src.CreatedAt, DateTimeKind.Utc)));
|
||||
|
||||
// List<UserClubFeatureDto> → GetUserClubFeaturesResponse
|
||||
config.NewConfig<List<UserClubFeatureDto>, GetUserClubFeaturesResponse>()
|
||||
|
||||
Reference in New Issue
Block a user