Generator Changes at 9/28/2025 6:19:21 AM

This commit is contained in:
MeysamMoghaddam
2025-09-28 06:30:13 +03:30
parent 1a99a88552
commit a3b7302d90
16 changed files with 534 additions and 6 deletions

View File

@@ -7,7 +7,7 @@ public interface IApplicationDbContext
DbSet<UserOrder> UserOrders { get; }
DbSet<Role> Roles { get; }
DbSet<UserRole> UserRoles { get; }
DbSet<User> Users { get; }
DbSet<OtpToken> OtpTokens { get; }
DbSet<User> Users { get; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
}

View File

@@ -14,7 +14,9 @@ public class CreateNewOtpTokenCommandHandler : IRequestHandler<CreateNewOtpToken
_cfg = cfg;
}
const int CodeLength = 6; // 4-6 مناسب است
const int CodeLength = 6;
const int MaxAttempts = 5; // محدودیت تلاش
static readonly TimeSpan Ttl = TimeSpan.FromMinutes(2);
static readonly TimeSpan Cooldown = TimeSpan.FromSeconds(60); // فاصله ارسال مجدد
public async Task<CreateNewOtpTokenResponseDto> Handle(CreateNewOtpTokenCommand request,
@@ -57,7 +59,9 @@ public class CreateNewOtpTokenCommandHandler : IRequestHandler<CreateNewOtpToken
{
Success = true,
Message = "کد ارسال شد.",
Code = code
Code = code,
RemainingAttempts = MaxAttempts,
RemainingSeconds = Ttl.Seconds
};
}

View File

@@ -7,5 +7,9 @@ public class CreateNewOtpTokenResponseDto
public string Message { get; set; }
//کد
public string? Code { get; set; }
//تلاش باقی مانده
public int RemainingAttempts { get; set; }
//ثانیه باقی مانده
public int RemainingSeconds { get; set; }
}

View File

@@ -51,7 +51,9 @@ public class VerifyOtpTokenCommandHandler : IRequestHandler<VerifyOtpTokenComman
Mobile = mobile,
ReferralCode = UtilExtensions.Generate(digits: 10, firstDigitNonZero: true),
IsMobileVerified = true,
MobileVerifiedAt = now
MobileVerifiedAt = now,
IsRulesAccepted = true,
RulesAcceptedAt = now,
};
await _context.Users.AddAsync(user, cancellationToken);
user.AddDomainEvent(new CreateNewUserEvent(user));
@@ -71,7 +73,9 @@ public class VerifyOtpTokenCommandHandler : IRequestHandler<VerifyOtpTokenComman
{
Success = true,
Message = "اعتبارسنجی موفق.",
UserId = user.Id
UserId = user.Id,
RemainingAttempts = MaxAttempts,
RemainingSeconds = (otp.ExpiresAt - now).Seconds
};
}

View File

@@ -7,5 +7,9 @@ public class VerifyOtpTokenResponseDto
public string Message { get; set; }
//شناسه کاربر
public long? UserId { get; set; }
//تلاش باقی مانده
public int RemainingAttempts { get; set; }
//ثانیه باقی مانده
public int RemainingSeconds { get; set; }
}

View File

@@ -11,5 +11,9 @@ public record UpdateUserCommand : IRequest<Unit>
public string? NationalCode { get; init; }
//آدرس آواتار
public string? AvatarPath { get; init; }
//قوانین پذیرفته شده؟
public bool IsRulesAccepted { get; init; }
//تاریخ پذیرش قوانین
public DateTime? RulesAcceptedAt { get; init; }
}

View File

@@ -5,6 +5,8 @@ public class UpdateUserCommandValidator : AbstractValidator<UpdateUserCommand>
{
RuleFor(model => model.Id)
.NotNull();
RuleFor(model => model.IsRulesAccepted)
.NotNull();
}
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
{

View File

@@ -22,6 +22,10 @@ public class User : BaseAuditableEntity
public bool IsMobileVerified { get; set; }
//تاریخ فعال سازی موبایل
public DateTime? MobileVerifiedAt { get; set; }
//قوانین پذیرفته شده؟
public bool IsRulesAccepted { get; set; }
//تاریخ پذیرش قوانین
public DateTime? RulesAcceptedAt { get; set; }
//UserAddress Collection Navigation Reference
public virtual ICollection<UserAddress> UserAddresss { get; set; }
//UserOrder Collection Navigation Reference

View File

@@ -44,7 +44,7 @@ public class ApplicationDbContext : DbContext, IApplicationDbContext
public DbSet<UserOrder> UserOrders => Set<UserOrder>();
public DbSet<Role> Roles => Set<Role>();
public DbSet<UserRole> UserRoles => Set<UserRole>();
public DbSet<User> Users => Set<User>();
public DbSet<OtpToken> OtpTokens => Set<OtpToken>();
public DbSet<User> Users => Set<User>();
}

View File

@@ -25,5 +25,7 @@ public class UserConfiguration : IEntityTypeConfiguration<User>
builder.Property(entity => entity.ReferralCode).IsRequired(true);
builder.Property(entity => entity.IsMobileVerified ).IsRequired(true);
builder.Property(entity => entity.MobileVerifiedAt).IsRequired(false);
builder.Property(entity => entity.IsRulesAccepted).IsRequired(true);
builder.Property(entity => entity.RulesAcceptedAt).IsRequired(false);
}
}

View File

@@ -0,0 +1,442 @@
// <auto-generated />
using System;
using CMSMicroservice.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20250928025307_u03")]
partial class u03
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("CMS")
.HasAnnotation("ProductVersion", "7.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("CMSMicroservice.Domain.Entities.OtpToken", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<int>("Attempts")
.HasColumnType("int");
b.Property<string>("CodeHash")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("ExpiresAt")
.HasColumnType("datetime2");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<bool>("IsUsed")
.HasColumnType("bit");
b.Property<DateTime?>("LastModified")
.HasColumnType("datetime2");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("Mobile")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Purpose")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("OtpTokens", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Package", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ImagePath")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<DateTime?>("LastModified")
.HasColumnType("datetime2");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<long>("Price")
.HasColumnType("bigint");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Packages", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Role", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<DateTime?>("LastModified")
.HasColumnType("datetime2");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Roles", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.User", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<string>("AvatarPath")
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("FirstName")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<bool>("IsMobileVerified")
.HasColumnType("bit");
b.Property<bool>("IsRulesAccepted")
.HasColumnType("bit");
b.Property<DateTime?>("LastModified")
.HasColumnType("datetime2");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.HasColumnType("nvarchar(max)");
b.Property<string>("Mobile")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime?>("MobileVerifiedAt")
.HasColumnType("datetime2");
b.Property<string>("NationalCode")
.HasColumnType("nvarchar(max)");
b.Property<long?>("ParentId")
.HasColumnType("bigint");
b.Property<string>("ReferralCode")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime?>("RulesAcceptedAt")
.HasColumnType("datetime2");
b.HasKey("Id");
b.HasIndex("ParentId");
b.ToTable("Users", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserAddress", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<string>("Address")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<long>("CityId")
.HasColumnType("bigint");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsDefault")
.HasColumnType("bit");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<DateTime?>("LastModified")
.HasColumnType("datetime2");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("PostalCode")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<long>("UserId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserAddresss", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserOrder", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<DateTime?>("LastModified")
.HasColumnType("datetime2");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<long>("PackageId")
.HasColumnType("bigint");
b.Property<DateTime?>("PaymentDate")
.HasColumnType("datetime2");
b.Property<bool>("PaymentStatus")
.HasColumnType("bit");
b.Property<long>("Price")
.HasColumnType("bigint");
b.Property<long?>("TransactionId")
.HasColumnType("bigint");
b.Property<long>("UserId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("PackageId");
b.HasIndex("UserId");
b.ToTable("UserOrders", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserRole", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<DateTime?>("LastModified")
.HasColumnType("datetime2");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<long>("RoleId")
.HasColumnType("bigint");
b.Property<long>("UserId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("RoleId");
b.HasIndex("UserId");
b.ToTable("UserRoles", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.User", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.User", "Parent")
.WithMany("Users")
.HasForeignKey("ParentId");
b.Navigation("Parent");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserAddress", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.User", "User")
.WithMany("UserAddresss")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserOrder", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package")
.WithMany("UserOrders")
.HasForeignKey("PackageId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CMSMicroservice.Domain.Entities.User", "User")
.WithMany("UserOrders")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Package");
b.Navigation("User");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserRole", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Role", "Role")
.WithMany("UserRoles")
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CMSMicroservice.Domain.Entities.User", "User")
.WithMany("UserRoles")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Role");
b.Navigation("User");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Package", b =>
{
b.Navigation("UserOrders");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Role", b =>
{
b.Navigation("UserRoles");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.User", b =>
{
b.Navigation("UserAddresss");
b.Navigation("UserOrders");
b.Navigation("UserRoles");
b.Navigation("Users");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class u03 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsRulesAccepted",
schema: "CMS",
table: "Users",
type: "bit",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<DateTime>(
name: "RulesAcceptedAt",
schema: "CMS",
table: "Users",
type: "datetime2",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsRulesAccepted",
schema: "CMS",
table: "Users");
migrationBuilder.DropColumn(
name: "RulesAcceptedAt",
schema: "CMS",
table: "Users");
}
}
}

View File

@@ -177,6 +177,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Property<bool>("IsMobileVerified")
.HasColumnType("bit");
b.Property<bool>("IsRulesAccepted")
.HasColumnType("bit");
b.Property<DateTime?>("LastModified")
.HasColumnType("datetime2");
@@ -203,6 +206,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime?>("RulesAcceptedAt")
.HasColumnType("datetime2");
b.HasKey("Id");
b.HasIndex("ParentId");

View File

@@ -42,6 +42,8 @@ message CreateNewOtpTokenResponse
bool success = 1;
string message = 2;
google.protobuf.StringValue code = 3;
int32 remaining_attempts = 4;
int32 remaining_seconds = 5;
}
message VerifyOtpTokenRequest
{
@@ -54,6 +56,8 @@ message VerifyOtpTokenResponse
bool success = 1;
string message = 2;
google.protobuf.Int64Value user_id = 3;
int32 remaining_attempts = 4;
int32 remaining_seconds = 5;
}
message GetAllOtpTokenByFilterRequest
{

View File

@@ -70,6 +70,8 @@ message UpdateUserRequest
google.protobuf.StringValue last_name = 3;
google.protobuf.StringValue national_code = 4;
google.protobuf.StringValue avatar_path = 5;
bool is_rules_accepted = 6;
google.protobuf.Timestamp rules_accepted_at = 7;
}
message DeleteUserRequest
{

View File

@@ -8,6 +8,8 @@ public class UpdateUserRequestValidator : AbstractValidator<UpdateUserRequest>
{
RuleFor(model => model.Id)
.NotNull();
RuleFor(model => model.IsRulesAccepted)
.NotNull();
}
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
{