refactor: clean up Member module and update Console

- Remove redundant PointsRule repositories (use single PointsRuleRepository)
- Clean up Member migrations and consolidate to single Init migration
- Update Console frontend API and components for Tenant
- Add H5LinkService for member H5 integration
This commit is contained in:
movingsam 2026-02-18 23:34:40 +08:00
parent daf8bc9e24
commit 489f02cb5f
23 changed files with 482 additions and 1930 deletions

View File

@ -13,7 +13,6 @@ public class PointsProcessingService : IPointsProcessingService
{ {
private readonly IPointsAccountCache _cache; private readonly IPointsAccountCache _cache;
private readonly ICodeDistributedLock _lock; private readonly ICodeDistributedLock _lock;
private readonly IPointsHistoryRepository _historyRepository;
private readonly IMediator _mediator; private readonly IMediator _mediator;
private readonly ITenantAccessor _tenantAccessor; private readonly ITenantAccessor _tenantAccessor;
private readonly ILogger<PointsProcessingService> _logger; private readonly ILogger<PointsProcessingService> _logger;
@ -21,14 +20,12 @@ public class PointsProcessingService : IPointsProcessingService
public PointsProcessingService( public PointsProcessingService(
IPointsAccountCache cache, IPointsAccountCache cache,
ICodeDistributedLock distributedLock, ICodeDistributedLock distributedLock,
IPointsHistoryRepository historyRepository,
IMediator mediator, IMediator mediator,
ITenantAccessor tenantAccessor, ITenantAccessor tenantAccessor,
ILogger<PointsProcessingService> logger) ILogger<PointsProcessingService> logger)
{ {
_cache = cache; _cache = cache;
_lock = distributedLock; _lock = distributedLock;
_historyRepository = historyRepository;
_mediator = mediator; _mediator = mediator;
_tenantAccessor = tenantAccessor; _tenantAccessor = tenantAccessor;
_logger = logger; _logger = logger;

View File

@ -1,7 +1,7 @@
using Fengling.Member.Application.Dtos; using Fengling.Member.Application.Dtos;
using Fengling.Member.Domain.Aggregates.PointsRuleModel; using Fengling.Member.Domain.Aggregates.PointsRuleModel;
using Fengling.Member.Domain.Aggregates.PointsRuleModel.Enums; using Fengling.Member.Domain.Aggregates.PointsRuleModel.Enums;
using Fengling.Member.Domain.Repositories; using Fengling.Member.Infrastructure.Repositories;
using NetCorePal.Extensions.Domain; using NetCorePal.Extensions.Domain;
namespace Fengling.Member.Application.Services; namespace Fengling.Member.Application.Services;

View File

@ -1,10 +0,0 @@
using Fengling.Member.Domain.Aggregates.PointsRuleModel;
namespace Fengling.Member.Domain.Repositories;
public interface IPointsRuleConditionRepository
{
Task<List<PointsRuleCondition>> GetByRuleIdAsync(PointsRuleId ruleId);
Task AddAsync(PointsRuleCondition condition);
Task DeleteByRuleIdAsync(PointsRuleId ruleId);
}

View File

@ -1,15 +0,0 @@
using Fengling.Member.Domain.Aggregates.PointsRuleModel;
using Fengling.Member.Domain.Aggregates.PointsRuleModel.Enums;
namespace Fengling.Member.Domain.Repositories;
public interface IPointsRuleRepository
{
Task<PointsRule?> GetByIdAsync(PointsRuleId id);
Task<PointsRule?> GetByCodeAsync(string code);
Task<List<PointsRule>> GetActiveRulesAsync();
Task<List<PointsRule>> GetActiveRulesByDimensionAsync(DimensionType dimensionType, string dimensionValue);
Task AddAsync(PointsRule rule);
Task UpdateAsync(PointsRule rule);
Task DeleteAsync(PointsRuleId id);
}

View File

@ -14,7 +14,7 @@ public class DesignTimeApplicationDbContextFactory: IDesignTimeDbContextFactory<
services.AddDbContext<ApplicationDbContext>(options => services.AddDbContext<ApplicationDbContext>(options =>
{ {
// change connectionstring if you want to run command “dotnet ef database update” // change connectionstring if you want to run command “dotnet ef database update”
options.UseNpgsql("Host=any;Database=any;Username=any;Password=any", options.UseNpgsql("Host=192.168.100.10;Database=fengling_member;Username=movingsam;Password=sl52788542",
b => b =>
{ {
b.MigrationsAssembly(typeof(DesignTimeApplicationDbContextFactory).Assembly.FullName); b.MigrationsAssembly(typeof(DesignTimeApplicationDbContextFactory).Assembly.FullName);

View File

@ -1,140 +0,0 @@
// <auto-generated />
using System;
using Fengling.Member.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Fengling.Member.Infrastructure.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20260122054728_Init")]
partial class Init
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("NetCorePal.Extensions.DistributedTransactions.CAP.Persistence.CapLock", b =>
{
b.Property<string>("Key")
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<string>("Instance")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<DateTime?>("LastLockTime")
.HasColumnType("TIMESTAMP");
b.HasKey("Key");
b.ToTable("CAPLock", (string)null);
});
modelBuilder.Entity("NetCorePal.Extensions.DistributedTransactions.CAP.Persistence.PublishedMessage", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("Added")
.HasColumnType("TIMESTAMP");
b.Property<string>("Content")
.HasColumnType("TEXT");
b.Property<DateTime?>("ExpiresAt")
.HasColumnType("TIMESTAMP");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<int?>("Retries")
.HasColumnType("integer");
b.Property<string>("StatusName")
.IsRequired()
.HasMaxLength(40)
.HasColumnType("character varying(40)");
b.Property<string>("Version")
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.HasKey("Id");
b.HasIndex(new[] { "ExpiresAt", "StatusName" }, "IX_ExpiresAt_StatusName");
b.HasIndex(new[] { "Version", "ExpiresAt", "StatusName" }, "IX_Version_ExpiresAt_StatusName");
b.ToTable("CAPPublishedMessage", (string)null);
});
modelBuilder.Entity("NetCorePal.Extensions.DistributedTransactions.CAP.Persistence.ReceivedMessage", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("Added")
.HasColumnType("TIMESTAMP");
b.Property<string>("Content")
.HasColumnType("TEXT");
b.Property<DateTime?>("ExpiresAt")
.HasColumnType("TIMESTAMP");
b.Property<string>("Group")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.Property<int?>("Retries")
.HasColumnType("integer");
b.Property<string>("StatusName")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Version")
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.HasKey("Id");
b.HasIndex(new[] { "ExpiresAt", "StatusName" }, "IX_ExpiresAt_StatusName")
.HasDatabaseName("IX_ExpiresAt_StatusName1");
b.HasIndex(new[] { "Version", "ExpiresAt", "StatusName" }, "IX_Version_ExpiresAt_StatusName")
.HasDatabaseName("IX_Version_ExpiresAt_StatusName1");
b.ToTable("CAPReceivedMessage", (string)null);
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,101 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Fengling.Member.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class Init : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "CAPLock",
columns: table => new
{
Key = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
Instance = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
LastLockTime = table.Column<DateTime>(type: "TIMESTAMP", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_CAPLock", x => x.Key);
});
migrationBuilder.CreateTable(
name: "CAPPublishedMessage",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Version = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: true),
Name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
Content = table.Column<string>(type: "TEXT", nullable: true),
Retries = table.Column<int>(type: "integer", nullable: true),
Added = table.Column<DateTime>(type: "TIMESTAMP", nullable: false),
ExpiresAt = table.Column<DateTime>(type: "TIMESTAMP", nullable: true),
StatusName = table.Column<string>(type: "character varying(40)", maxLength: 40, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CAPPublishedMessage", x => x.Id);
});
migrationBuilder.CreateTable(
name: "CAPReceivedMessage",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Version = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: true),
Name = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
Group = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
Content = table.Column<string>(type: "TEXT", nullable: true),
Retries = table.Column<int>(type: "integer", nullable: true),
Added = table.Column<DateTime>(type: "TIMESTAMP", nullable: false),
ExpiresAt = table.Column<DateTime>(type: "TIMESTAMP", nullable: true),
StatusName = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CAPReceivedMessage", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_ExpiresAt_StatusName",
table: "CAPPublishedMessage",
columns: new[] { "ExpiresAt", "StatusName" });
migrationBuilder.CreateIndex(
name: "IX_Version_ExpiresAt_StatusName",
table: "CAPPublishedMessage",
columns: new[] { "Version", "ExpiresAt", "StatusName" });
migrationBuilder.CreateIndex(
name: "IX_ExpiresAt_StatusName1",
table: "CAPReceivedMessage",
columns: new[] { "ExpiresAt", "StatusName" });
migrationBuilder.CreateIndex(
name: "IX_Version_ExpiresAt_StatusName1",
table: "CAPReceivedMessage",
columns: new[] { "Version", "ExpiresAt", "StatusName" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "CAPLock");
migrationBuilder.DropTable(
name: "CAPPublishedMessage");
migrationBuilder.DropTable(
name: "CAPReceivedMessage");
}
}
}

View File

@ -1,429 +0,0 @@
// <auto-generated />
using System;
using Fengling.Member.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Fengling.Member.Infrastructure.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20260205051658_AddMemberAndPointsEntities")]
partial class AddMemberAndPointsEntities
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsModel.PointsAccount", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("created_at");
b.Property<int>("FrozenPoints")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasDefaultValue(0)
.HasColumnName("frozen_points");
b.Property<long>("MemberId")
.HasColumnType("bigint")
.HasColumnName("user_id");
b.Property<long>("TenantId")
.HasColumnType("bigint")
.HasColumnName("tenant_id");
b.Property<int>("TotalPoints")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasDefaultValue(0)
.HasColumnName("points");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("updated_at");
b.Property<int>("Version")
.IsConcurrencyToken()
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasDefaultValue(1)
.HasColumnName("version");
b.HasKey("Id");
b.HasIndex("MemberId")
.IsUnique()
.HasDatabaseName("idx_points_account_memberid");
b.HasIndex("MemberId", "TenantId")
.HasDatabaseName("idx_points_account_member_tenant");
b.ToTable("mka_integraldetails", (string)null);
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsModel.PointsTransaction", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<long>("MemberId")
.HasColumnType("bigint");
b.Property<int>("Points")
.HasColumnType("integer");
b.Property<long>("PointsAccountId")
.HasColumnType("bigint");
b.Property<string>("Remark")
.HasColumnType("text");
b.Property<string>("SourceId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("TransactionType")
.IsRequired()
.HasColumnType("text");
b.Property<int>("TransactionTypeCategory")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("PointsAccountId");
b.ToTable("PointsTransaction");
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.MemberEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("created_at");
b.Property<string>("OpenId")
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasColumnName("open_id");
b.Property<string>("PhoneNumber")
.HasMaxLength(20)
.HasColumnType("character varying(20)")
.HasColumnName("phone_number");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)")
.HasColumnName("status");
b.Property<long>("TenantId")
.HasColumnType("bigint")
.HasColumnName("tenant_id");
b.Property<string>("UnionId")
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasColumnName("union_id");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("updated_at");
b.Property<int>("Version")
.IsConcurrencyToken()
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasDefaultValue(1)
.HasColumnName("version");
b.HasKey("Id");
b.HasIndex("OpenId")
.HasDatabaseName("idx_member_openid");
b.HasIndex("TenantId")
.HasDatabaseName("idx_member_tenantid");
b.HasIndex("UnionId")
.HasDatabaseName("idx_member_unionid");
b.HasIndex("TenantId", "PhoneNumber")
.HasDatabaseName("idx_member_tenant_phone");
b.ToTable("fls_member", (string)null);
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.MemberTag", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("created_at");
b.Property<long>("MemberId")
.HasColumnType("bigint")
.HasColumnName("member_id");
b.Property<string>("TagId")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)")
.HasColumnName("tag_id");
b.Property<string>("TagName")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("tag_name");
b.HasKey("Id");
b.HasIndex("TagId")
.HasDatabaseName("idx_membertag_tagid");
b.HasIndex("MemberId", "TagId")
.IsUnique()
.HasDatabaseName("idx_membertag_member_tag");
b.ToTable("fls_member_tag", (string)null);
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.WechatAuthorization", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("AuthorizedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("authorized_at");
b.Property<DateTime?>("LastLoginAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("last_login_at");
b.Property<long>("MemberId")
.HasColumnType("bigint")
.HasColumnName("member_id");
b.Property<string>("OpenId")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasColumnName("open_id");
b.Property<string>("UnionId")
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasColumnName("union_id");
b.HasKey("Id");
b.HasIndex("MemberId")
.HasDatabaseName("idx_wechat_auth_memberid");
b.HasIndex("OpenId")
.IsUnique()
.HasDatabaseName("idx_wechat_auth_openid");
b.HasIndex("UnionId")
.HasDatabaseName("idx_wechat_auth_unionid");
b.ToTable("fls_wechat_authorization", (string)null);
});
modelBuilder.Entity("NetCorePal.Extensions.DistributedTransactions.CAP.Persistence.CapLock", b =>
{
b.Property<string>("Key")
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<string>("Instance")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<DateTime?>("LastLockTime")
.HasColumnType("TIMESTAMP");
b.HasKey("Key");
b.ToTable("CAPLock", (string)null);
});
modelBuilder.Entity("NetCorePal.Extensions.DistributedTransactions.CAP.Persistence.PublishedMessage", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("Added")
.HasColumnType("TIMESTAMP");
b.Property<string>("Content")
.HasColumnType("TEXT");
b.Property<DateTime?>("ExpiresAt")
.HasColumnType("TIMESTAMP");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<int?>("Retries")
.HasColumnType("integer");
b.Property<string>("StatusName")
.IsRequired()
.HasMaxLength(40)
.HasColumnType("character varying(40)");
b.Property<string>("Version")
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.HasKey("Id");
b.HasIndex(new[] { "ExpiresAt", "StatusName" }, "IX_ExpiresAt_StatusName");
b.HasIndex(new[] { "Version", "ExpiresAt", "StatusName" }, "IX_Version_ExpiresAt_StatusName");
b.ToTable("CAPPublishedMessage", (string)null);
});
modelBuilder.Entity("NetCorePal.Extensions.DistributedTransactions.CAP.Persistence.ReceivedMessage", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("Added")
.HasColumnType("TIMESTAMP");
b.Property<string>("Content")
.HasColumnType("TEXT");
b.Property<DateTime?>("ExpiresAt")
.HasColumnType("TIMESTAMP");
b.Property<string>("Group")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.Property<int?>("Retries")
.HasColumnType("integer");
b.Property<string>("StatusName")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Version")
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.HasKey("Id");
b.HasIndex(new[] { "ExpiresAt", "StatusName" }, "IX_ExpiresAt_StatusName")
.HasDatabaseName("IX_ExpiresAt_StatusName1");
b.HasIndex(new[] { "Version", "ExpiresAt", "StatusName" }, "IX_Version_ExpiresAt_StatusName")
.HasDatabaseName("IX_Version_ExpiresAt_StatusName1");
b.ToTable("CAPReceivedMessage", (string)null);
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsModel.PointsTransaction", b =>
{
b.HasOne("Fengling.Member.Domain.Aggregates.PointsModel.PointsAccount", null)
.WithMany("Transactions")
.HasForeignKey("PointsAccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.MemberTag", b =>
{
b.HasOne("Fengling.Member.Domain.Aggregates.Users.MemberEntity", null)
.WithMany("Tags")
.HasForeignKey("MemberId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.WechatAuthorization", b =>
{
b.HasOne("Fengling.Member.Domain.Aggregates.Users.MemberEntity", null)
.WithMany("WechatAuthorizations")
.HasForeignKey("MemberId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsModel.PointsAccount", b =>
{
b.Navigation("Transactions");
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.MemberEntity", b =>
{
b.Navigation("Tags");
b.Navigation("WechatAuthorizations");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,208 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Fengling.Member.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class AddMemberAndPointsEntities : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "fls_member",
columns: table => new
{
id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
tenant_id = table.Column<long>(type: "bigint", nullable: false),
phone_number = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: true),
open_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
union_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
created_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
updated_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
version = table.Column<int>(type: "integer", nullable: false, defaultValue: 1)
},
constraints: table =>
{
table.PrimaryKey("PK_fls_member", x => x.id);
});
migrationBuilder.CreateTable(
name: "mka_integraldetails",
columns: table => new
{
id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
user_id = table.Column<long>(type: "bigint", nullable: false),
tenant_id = table.Column<long>(type: "bigint", nullable: false),
points = table.Column<int>(type: "integer", nullable: false, defaultValue: 0),
frozen_points = table.Column<int>(type: "integer", nullable: false, defaultValue: 0),
version = table.Column<int>(type: "integer", nullable: false, defaultValue: 1),
created_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
updated_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_mka_integraldetails", x => x.id);
});
migrationBuilder.CreateTable(
name: "fls_member_tag",
columns: table => new
{
id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
member_id = table.Column<long>(type: "bigint", nullable: false),
tag_id = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
tag_name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
created_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_fls_member_tag", x => x.id);
table.ForeignKey(
name: "FK_fls_member_tag_fls_member_member_id",
column: x => x.member_id,
principalTable: "fls_member",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "fls_wechat_authorization",
columns: table => new
{
id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
member_id = table.Column<long>(type: "bigint", nullable: false),
open_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
union_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
authorized_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
last_login_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_fls_wechat_authorization", x => x.id);
table.ForeignKey(
name: "FK_fls_wechat_authorization_fls_member_member_id",
column: x => x.member_id,
principalTable: "fls_member",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "PointsTransaction",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
PointsAccountId = table.Column<long>(type: "bigint", nullable: false),
MemberId = table.Column<long>(type: "bigint", nullable: false),
Points = table.Column<int>(type: "integer", nullable: false),
TransactionType = table.Column<string>(type: "text", nullable: false),
SourceId = table.Column<string>(type: "text", nullable: false),
TransactionTypeCategory = table.Column<int>(type: "integer", nullable: false),
Remark = table.Column<string>(type: "text", nullable: true),
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PointsTransaction", x => x.Id);
table.ForeignKey(
name: "FK_PointsTransaction_mka_integraldetails_PointsAccountId",
column: x => x.PointsAccountId,
principalTable: "mka_integraldetails",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "idx_member_openid",
table: "fls_member",
column: "open_id");
migrationBuilder.CreateIndex(
name: "idx_member_tenant_phone",
table: "fls_member",
columns: new[] { "tenant_id", "phone_number" });
migrationBuilder.CreateIndex(
name: "idx_member_tenantid",
table: "fls_member",
column: "tenant_id");
migrationBuilder.CreateIndex(
name: "idx_member_unionid",
table: "fls_member",
column: "union_id");
migrationBuilder.CreateIndex(
name: "idx_membertag_member_tag",
table: "fls_member_tag",
columns: new[] { "member_id", "tag_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "idx_membertag_tagid",
table: "fls_member_tag",
column: "tag_id");
migrationBuilder.CreateIndex(
name: "idx_wechat_auth_memberid",
table: "fls_wechat_authorization",
column: "member_id");
migrationBuilder.CreateIndex(
name: "idx_wechat_auth_openid",
table: "fls_wechat_authorization",
column: "open_id",
unique: true);
migrationBuilder.CreateIndex(
name: "idx_wechat_auth_unionid",
table: "fls_wechat_authorization",
column: "union_id");
migrationBuilder.CreateIndex(
name: "idx_points_account_member_tenant",
table: "mka_integraldetails",
columns: new[] { "user_id", "tenant_id" });
migrationBuilder.CreateIndex(
name: "idx_points_account_memberid",
table: "mka_integraldetails",
column: "user_id",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_PointsTransaction_PointsAccountId",
table: "PointsTransaction",
column: "PointsAccountId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "fls_member_tag");
migrationBuilder.DropTable(
name: "fls_wechat_authorization");
migrationBuilder.DropTable(
name: "PointsTransaction");
migrationBuilder.DropTable(
name: "fls_member");
migrationBuilder.DropTable(
name: "mka_integraldetails");
}
}
}

View File

@ -1,598 +0,0 @@
// <auto-generated />
using System;
using Fengling.Member.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Fengling.Member.Infrastructure.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20260209110951_AddPointsRuleSystem")]
partial class AddPointsRuleSystem
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsModel.PointsAccount", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("created_at");
b.Property<int>("FrozenPoints")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasDefaultValue(0)
.HasColumnName("frozen_points");
b.Property<long>("MemberId")
.HasColumnType("bigint")
.HasColumnName("user_id");
b.Property<long>("TenantId")
.HasColumnType("bigint")
.HasColumnName("tenant_id");
b.Property<int>("TotalPoints")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasDefaultValue(0)
.HasColumnName("points");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("updated_at");
b.Property<int>("Version")
.IsConcurrencyToken()
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasDefaultValue(1)
.HasColumnName("version");
b.HasKey("Id");
b.HasIndex("MemberId")
.IsUnique()
.HasDatabaseName("idx_points_account_memberid");
b.HasIndex("MemberId", "TenantId")
.HasDatabaseName("idx_points_account_member_tenant");
b.ToTable("mka_integraldetails", (string)null);
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsModel.PointsTransaction", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("ExpireAt")
.HasColumnType("timestamp with time zone");
b.Property<long>("MemberId")
.HasColumnType("bigint");
b.Property<int>("Points")
.HasColumnType("integer");
b.Property<long>("PointsAccountId")
.HasColumnType("bigint");
b.Property<string>("Remark")
.HasColumnType("text");
b.Property<string>("SourceId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("TransactionType")
.IsRequired()
.HasColumnType("text");
b.Property<int>("TransactionTypeCategory")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("PointsAccountId");
b.ToTable("PointsTransactions");
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsRuleModel.PointsRule", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid")
.HasComment("规则标识");
b.Property<int>("BasePoints")
.HasColumnType("integer")
.HasComment("基础积分");
b.Property<int>("CalculationMode")
.HasColumnType("integer")
.HasComment("计算模式");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)")
.HasComment("规则编码");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("创建时间");
b.Property<DateTime>("EffectiveFrom")
.HasColumnType("timestamp with time zone")
.HasComment("生效开始时间");
b.Property<DateTime?>("EffectiveTo")
.HasColumnType("timestamp with time zone")
.HasComment("生效结束时间");
b.Property<bool>("IsActive")
.HasColumnType("boolean")
.HasComment("是否启用");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("character varying(200)")
.HasComment("规则名称");
b.Property<int>("Priority")
.HasColumnType("integer")
.HasComment("优先级");
b.Property<int>("RuleType")
.HasColumnType("integer")
.HasComment("规则类型");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("更新时间");
b.Property<int>("ValidityDays")
.HasColumnType("integer")
.HasComment("有效期天数");
b.Property<decimal?>("WeightFactor")
.HasPrecision(18, 4)
.HasColumnType("numeric(18,4)")
.HasComment("权重因子");
b.HasKey("Id");
b.HasIndex("Code")
.IsUnique();
b.HasIndex("IsActive");
b.ToTable("PointsRules", (string)null);
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsRuleModel.PointsRuleCondition", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid")
.HasComment("条件标识");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("创建时间");
b.Property<int>("DimensionType")
.HasColumnType("integer")
.HasComment("维度类型");
b.Property<string>("DimensionValue")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("character varying(200)")
.HasComment("维度值");
b.Property<string>("Operator")
.HasMaxLength(20)
.HasColumnType("character varying(20)")
.HasComment("操作符");
b.Property<Guid>("RuleId")
.HasColumnType("uuid")
.HasComment("关联规则标识");
b.HasKey("Id");
b.HasIndex("RuleId", "DimensionType");
b.ToTable("PointsRuleConditions", (string)null);
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.MemberEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("created_at");
b.Property<string>("OpenId")
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasColumnName("open_id");
b.Property<string>("PhoneNumber")
.HasMaxLength(20)
.HasColumnType("character varying(20)")
.HasColumnName("phone_number");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)")
.HasColumnName("status");
b.Property<long>("TenantId")
.HasColumnType("bigint")
.HasColumnName("tenant_id");
b.Property<string>("UnionId")
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasColumnName("union_id");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("updated_at");
b.Property<int>("Version")
.IsConcurrencyToken()
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasDefaultValue(1)
.HasColumnName("version");
b.HasKey("Id");
b.HasIndex("OpenId")
.HasDatabaseName("idx_member_openid");
b.HasIndex("TenantId")
.HasDatabaseName("idx_member_tenantid");
b.HasIndex("UnionId")
.HasDatabaseName("idx_member_unionid");
b.HasIndex("TenantId", "PhoneNumber")
.HasDatabaseName("idx_member_tenant_phone");
b.ToTable("fls_member", (string)null);
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.MemberTag", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("created_at");
b.Property<long>("MemberId")
.HasColumnType("bigint")
.HasColumnName("member_id");
b.Property<string>("TagId")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)")
.HasColumnName("tag_id");
b.Property<string>("TagName")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("tag_name");
b.HasKey("Id");
b.HasIndex("TagId")
.HasDatabaseName("idx_membertag_tagid");
b.HasIndex("MemberId", "TagId")
.IsUnique()
.HasDatabaseName("idx_membertag_member_tag");
b.ToTable("fls_member_tag", (string)null);
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.OAuthAuthorization", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("AccessToken")
.HasColumnType("text");
b.Property<DateTime>("AuthorizedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("LastLoginAt")
.HasColumnType("timestamp with time zone");
b.Property<long>("MemberId")
.HasColumnType("bigint");
b.Property<string>("OpenId")
.IsRequired()
.HasColumnType("text");
b.Property<int>("Provider")
.HasColumnType("integer");
b.Property<string>("RefreshToken")
.HasColumnType("text");
b.Property<DateTime?>("TokenExpiredAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("UnionId")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("MemberId");
b.ToTable("OAuthAuthorization");
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.WechatAuthorization", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("AuthorizedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("authorized_at");
b.Property<DateTime?>("LastLoginAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("last_login_at");
b.Property<long>("MemberId")
.HasColumnType("bigint")
.HasColumnName("member_id");
b.Property<string>("OpenId")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasColumnName("open_id");
b.Property<string>("UnionId")
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasColumnName("union_id");
b.HasKey("Id");
b.HasIndex("MemberId")
.HasDatabaseName("idx_wechat_auth_memberid");
b.HasIndex("OpenId")
.IsUnique()
.HasDatabaseName("idx_wechat_auth_openid");
b.HasIndex("UnionId")
.HasDatabaseName("idx_wechat_auth_unionid");
b.ToTable("fls_wechat_authorization", (string)null);
});
modelBuilder.Entity("NetCorePal.Extensions.DistributedTransactions.CAP.Persistence.CapLock", b =>
{
b.Property<string>("Key")
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<string>("Instance")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<DateTime?>("LastLockTime")
.HasColumnType("TIMESTAMP");
b.HasKey("Key");
b.ToTable("CAPLock", (string)null);
});
modelBuilder.Entity("NetCorePal.Extensions.DistributedTransactions.CAP.Persistence.PublishedMessage", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("Added")
.HasColumnType("TIMESTAMP");
b.Property<string>("Content")
.HasColumnType("TEXT");
b.Property<DateTime?>("ExpiresAt")
.HasColumnType("TIMESTAMP");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<int?>("Retries")
.HasColumnType("integer");
b.Property<string>("StatusName")
.IsRequired()
.HasMaxLength(40)
.HasColumnType("character varying(40)");
b.Property<string>("Version")
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.HasKey("Id");
b.HasIndex(new[] { "ExpiresAt", "StatusName" }, "IX_ExpiresAt_StatusName");
b.HasIndex(new[] { "Version", "ExpiresAt", "StatusName" }, "IX_Version_ExpiresAt_StatusName");
b.ToTable("CAPPublishedMessage", (string)null);
});
modelBuilder.Entity("NetCorePal.Extensions.DistributedTransactions.CAP.Persistence.ReceivedMessage", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("Added")
.HasColumnType("TIMESTAMP");
b.Property<string>("Content")
.HasColumnType("TEXT");
b.Property<DateTime?>("ExpiresAt")
.HasColumnType("TIMESTAMP");
b.Property<string>("Group")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.Property<int?>("Retries")
.HasColumnType("integer");
b.Property<string>("StatusName")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Version")
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.HasKey("Id");
b.HasIndex(new[] { "ExpiresAt", "StatusName" }, "IX_ExpiresAt_StatusName")
.HasDatabaseName("IX_ExpiresAt_StatusName1");
b.HasIndex(new[] { "Version", "ExpiresAt", "StatusName" }, "IX_Version_ExpiresAt_StatusName")
.HasDatabaseName("IX_Version_ExpiresAt_StatusName1");
b.ToTable("CAPReceivedMessage", (string)null);
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsModel.PointsTransaction", b =>
{
b.HasOne("Fengling.Member.Domain.Aggregates.PointsModel.PointsAccount", null)
.WithMany("Transactions")
.HasForeignKey("PointsAccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsRuleModel.PointsRuleCondition", b =>
{
b.HasOne("Fengling.Member.Domain.Aggregates.PointsRuleModel.PointsRule", null)
.WithMany("Conditions")
.HasForeignKey("RuleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.MemberTag", b =>
{
b.HasOne("Fengling.Member.Domain.Aggregates.Users.MemberEntity", null)
.WithMany("Tags")
.HasForeignKey("MemberId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.OAuthAuthorization", b =>
{
b.HasOne("Fengling.Member.Domain.Aggregates.Users.MemberEntity", null)
.WithMany("OAuthAuthorizations")
.HasForeignKey("MemberId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsModel.PointsAccount", b =>
{
b.Navigation("Transactions");
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsRuleModel.PointsRule", b =>
{
b.Navigation("Conditions");
});
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.MemberEntity", b =>
{
b.Navigation("OAuthAuthorizations");
b.Navigation("Tags");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,206 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Fengling.Member.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class AddPointsRuleSystem : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_fls_wechat_authorization_fls_member_member_id",
table: "fls_wechat_authorization");
migrationBuilder.DropForeignKey(
name: "FK_PointsTransaction_mka_integraldetails_PointsAccountId",
table: "PointsTransaction");
migrationBuilder.DropPrimaryKey(
name: "PK_PointsTransaction",
table: "PointsTransaction");
migrationBuilder.RenameTable(
name: "PointsTransaction",
newName: "PointsTransactions");
migrationBuilder.RenameIndex(
name: "IX_PointsTransaction_PointsAccountId",
table: "PointsTransactions",
newName: "IX_PointsTransactions_PointsAccountId");
migrationBuilder.AddColumn<DateTime>(
name: "ExpireAt",
table: "PointsTransactions",
type: "timestamp with time zone",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.AddPrimaryKey(
name: "PK_PointsTransactions",
table: "PointsTransactions",
column: "Id");
migrationBuilder.CreateTable(
name: "OAuthAuthorization",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
MemberId = table.Column<long>(type: "bigint", nullable: false),
Provider = table.Column<int>(type: "integer", nullable: false),
OpenId = table.Column<string>(type: "text", nullable: false),
UnionId = table.Column<string>(type: "text", nullable: true),
AccessToken = table.Column<string>(type: "text", nullable: true),
RefreshToken = table.Column<string>(type: "text", nullable: true),
TokenExpiredAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
AuthorizedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
LastLoginAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_OAuthAuthorization", x => x.Id);
table.ForeignKey(
name: "FK_OAuthAuthorization_fls_member_MemberId",
column: x => x.MemberId,
principalTable: "fls_member",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "PointsRules",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false, comment: "规则标识"),
Name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false, comment: "规则名称"),
Code = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false, comment: "规则编码"),
RuleType = table.Column<int>(type: "integer", nullable: false, comment: "规则类型"),
BasePoints = table.Column<int>(type: "integer", nullable: false, comment: "基础积分"),
WeightFactor = table.Column<decimal>(type: "numeric(18,4)", precision: 18, scale: 4, nullable: true, comment: "权重因子"),
ValidityDays = table.Column<int>(type: "integer", nullable: false, comment: "有效期天数"),
Priority = table.Column<int>(type: "integer", nullable: false, comment: "优先级"),
CalculationMode = table.Column<int>(type: "integer", nullable: false, comment: "计算模式"),
IsActive = table.Column<bool>(type: "boolean", nullable: false, comment: "是否启用"),
EffectiveFrom = table.Column<DateTime>(type: "timestamp with time zone", nullable: false, comment: "生效开始时间"),
EffectiveTo = table.Column<DateTime>(type: "timestamp with time zone", nullable: true, comment: "生效结束时间"),
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false, comment: "创建时间"),
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false, comment: "更新时间")
},
constraints: table =>
{
table.PrimaryKey("PK_PointsRules", x => x.Id);
});
migrationBuilder.CreateTable(
name: "PointsRuleConditions",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false, comment: "条件标识"),
RuleId = table.Column<Guid>(type: "uuid", nullable: false, comment: "关联规则标识"),
DimensionType = table.Column<int>(type: "integer", nullable: false, comment: "维度类型"),
DimensionValue = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false, comment: "维度值"),
Operator = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: true, comment: "操作符"),
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false, comment: "创建时间")
},
constraints: table =>
{
table.PrimaryKey("PK_PointsRuleConditions", x => x.Id);
table.ForeignKey(
name: "FK_PointsRuleConditions_PointsRules_RuleId",
column: x => x.RuleId,
principalTable: "PointsRules",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_OAuthAuthorization_MemberId",
table: "OAuthAuthorization",
column: "MemberId");
migrationBuilder.CreateIndex(
name: "IX_PointsRuleConditions_RuleId_DimensionType",
table: "PointsRuleConditions",
columns: new[] { "RuleId", "DimensionType" });
migrationBuilder.CreateIndex(
name: "IX_PointsRules_Code",
table: "PointsRules",
column: "Code",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_PointsRules_IsActive",
table: "PointsRules",
column: "IsActive");
migrationBuilder.AddForeignKey(
name: "FK_PointsTransactions_mka_integraldetails_PointsAccountId",
table: "PointsTransactions",
column: "PointsAccountId",
principalTable: "mka_integraldetails",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_PointsTransactions_mka_integraldetails_PointsAccountId",
table: "PointsTransactions");
migrationBuilder.DropTable(
name: "OAuthAuthorization");
migrationBuilder.DropTable(
name: "PointsRuleConditions");
migrationBuilder.DropTable(
name: "PointsRules");
migrationBuilder.DropPrimaryKey(
name: "PK_PointsTransactions",
table: "PointsTransactions");
migrationBuilder.DropColumn(
name: "ExpireAt",
table: "PointsTransactions");
migrationBuilder.RenameTable(
name: "PointsTransactions",
newName: "PointsTransaction");
migrationBuilder.RenameIndex(
name: "IX_PointsTransactions_PointsAccountId",
table: "PointsTransaction",
newName: "IX_PointsTransaction_PointsAccountId");
migrationBuilder.AddPrimaryKey(
name: "PK_PointsTransaction",
table: "PointsTransaction",
column: "Id");
migrationBuilder.AddForeignKey(
name: "FK_fls_wechat_authorization_fls_member_member_id",
table: "fls_wechat_authorization",
column: "member_id",
principalTable: "fls_member",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_PointsTransaction_mka_integraldetails_PointsAccountId",
table: "PointsTransaction",
column: "PointsAccountId",
principalTable: "mka_integraldetails",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
}
}
}

View File

@ -1,72 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Fengling.Member.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class ChangeMemberIdToGuid : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<Guid>(
name: "MemberId",
table: "OAuthAuthorization",
type: "uuid",
nullable: false,
oldClrType: typeof(long),
oldType: "bigint");
migrationBuilder.AlterColumn<Guid>(
name: "member_id",
table: "fls_member_tag",
type: "uuid",
nullable: false,
oldClrType: typeof(long),
oldType: "bigint");
migrationBuilder.AlterColumn<Guid>(
name: "id",
table: "fls_member",
type: "uuid",
nullable: false,
comment: "会员标识",
oldClrType: typeof(long),
oldType: "bigint")
.OldAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<long>(
name: "MemberId",
table: "OAuthAuthorization",
type: "bigint",
nullable: false,
oldClrType: typeof(Guid),
oldType: "uuid");
migrationBuilder.AlterColumn<long>(
name: "member_id",
table: "fls_member_tag",
type: "bigint",
nullable: false,
oldClrType: typeof(Guid),
oldType: "uuid");
migrationBuilder.AlterColumn<long>(
name: "id",
table: "fls_member",
type: "bigint",
nullable: false,
oldClrType: typeof(Guid),
oldType: "uuid",
oldComment: "会员标识")
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
}
}
}

View File

@ -12,8 +12,8 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Fengling.Member.Infrastructure.Migrations namespace Fengling.Member.Infrastructure.Migrations
{ {
[DbContext(typeof(ApplicationDbContext))] [DbContext(typeof(ApplicationDbContext))]
[Migration("20260209163416_ChangeMemberIdToGuid")] [Migration("20260217154010_Init")]
partial class ChangeMemberIdToGuid partial class Init
{ {
/// <inheritdoc /> /// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -27,17 +27,18 @@ namespace Fengling.Member.Infrastructure.Migrations
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsModel.PointsAccount", b => modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsModel.PointsAccount", b =>
{ {
b.Property<long>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd() .HasColumnType("uuid")
.HasColumnType("bigint") .HasColumnName("id")
.HasColumnName("id"); .HasComment("积分账户标识");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedAt") b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone") .HasColumnType("timestamp with time zone")
.HasColumnName("created_at"); .HasColumnName("created_at");
b.Property<bool>("Deleted")
.HasColumnType("boolean");
b.Property<int>("FrozenPoints") b.Property<int>("FrozenPoints")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("integer") .HasColumnType("integer")
@ -48,6 +49,10 @@ namespace Fengling.Member.Infrastructure.Migrations
.HasColumnType("bigint") .HasColumnType("bigint")
.HasColumnName("user_id"); .HasColumnName("user_id");
b.Property<int>("RowVersion")
.IsConcurrencyToken()
.HasColumnType("integer");
b.Property<long>("TenantId") b.Property<long>("TenantId")
.HasColumnType("bigint") .HasColumnType("bigint")
.HasColumnName("tenant_id"); .HasColumnName("tenant_id");
@ -101,8 +106,8 @@ namespace Fengling.Member.Infrastructure.Migrations
b.Property<int>("Points") b.Property<int>("Points")
.HasColumnType("integer"); .HasColumnType("integer");
b.Property<long>("PointsAccountId") b.Property<Guid>("PointsAccountId")
.HasColumnType("bigint"); .HasColumnType("uuid");
b.Property<string>("Remark") b.Property<string>("Remark")
.HasColumnType("text"); .HasColumnType("text");
@ -111,6 +116,9 @@ namespace Fengling.Member.Infrastructure.Migrations
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasColumnType("text");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.Property<string>("TransactionType") b.Property<string>("TransactionType")
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasColumnType("text");
@ -245,6 +253,9 @@ namespace Fengling.Member.Infrastructure.Migrations
.HasColumnType("timestamp with time zone") .HasColumnType("timestamp with time zone")
.HasColumnName("created_at"); .HasColumnName("created_at");
b.Property<bool>("Deleted")
.HasColumnType("boolean");
b.Property<string>("OpenId") b.Property<string>("OpenId")
.HasMaxLength(64) .HasMaxLength(64)
.HasColumnType("character varying(64)") .HasColumnType("character varying(64)")
@ -255,6 +266,10 @@ namespace Fengling.Member.Infrastructure.Migrations
.HasColumnType("character varying(20)") .HasColumnType("character varying(20)")
.HasColumnName("phone_number"); .HasColumnName("phone_number");
b.Property<int>("RowVersion")
.IsConcurrencyToken()
.HasColumnType("integer");
b.Property<string>("Status") b.Property<string>("Status")
.IsRequired() .IsRequired()
.HasMaxLength(20) .HasMaxLength(20)

View File

@ -0,0 +1,390 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Fengling.Member.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class Init : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "CAPLock",
columns: table => new
{
Key = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
Instance = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
LastLockTime = table.Column<DateTime>(type: "TIMESTAMP", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_CAPLock", x => x.Key);
});
migrationBuilder.CreateTable(
name: "CAPPublishedMessage",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Version = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: true),
Name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
Content = table.Column<string>(type: "TEXT", nullable: true),
Retries = table.Column<int>(type: "integer", nullable: true),
Added = table.Column<DateTime>(type: "TIMESTAMP", nullable: false),
ExpiresAt = table.Column<DateTime>(type: "TIMESTAMP", nullable: true),
StatusName = table.Column<string>(type: "character varying(40)", maxLength: 40, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CAPPublishedMessage", x => x.Id);
});
migrationBuilder.CreateTable(
name: "CAPReceivedMessage",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Version = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: true),
Name = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
Group = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
Content = table.Column<string>(type: "TEXT", nullable: true),
Retries = table.Column<int>(type: "integer", nullable: true),
Added = table.Column<DateTime>(type: "TIMESTAMP", nullable: false),
ExpiresAt = table.Column<DateTime>(type: "TIMESTAMP", nullable: true),
StatusName = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CAPReceivedMessage", x => x.Id);
});
migrationBuilder.CreateTable(
name: "fls_member",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, comment: "会员标识"),
tenant_id = table.Column<long>(type: "bigint", nullable: false),
phone_number = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: true),
open_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
union_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
created_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
updated_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
version = table.Column<int>(type: "integer", nullable: false, defaultValue: 1),
Deleted = table.Column<bool>(type: "boolean", nullable: false),
RowVersion = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_fls_member", x => x.id);
});
migrationBuilder.CreateTable(
name: "fls_wechat_authorization",
columns: table => new
{
id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
member_id = table.Column<long>(type: "bigint", nullable: false),
open_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
union_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
authorized_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
last_login_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_fls_wechat_authorization", x => x.id);
});
migrationBuilder.CreateTable(
name: "mka_integraldetails",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, comment: "积分账户标识"),
user_id = table.Column<long>(type: "bigint", nullable: false),
tenant_id = table.Column<long>(type: "bigint", nullable: false),
points = table.Column<int>(type: "integer", nullable: false, defaultValue: 0),
frozen_points = table.Column<int>(type: "integer", nullable: false, defaultValue: 0),
version = table.Column<int>(type: "integer", nullable: false, defaultValue: 1),
created_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
updated_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
Deleted = table.Column<bool>(type: "boolean", nullable: false),
RowVersion = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_mka_integraldetails", x => x.id);
});
migrationBuilder.CreateTable(
name: "PointsRules",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false, comment: "规则标识"),
Name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false, comment: "规则名称"),
Code = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false, comment: "规则编码"),
RuleType = table.Column<int>(type: "integer", nullable: false, comment: "规则类型"),
BasePoints = table.Column<int>(type: "integer", nullable: false, comment: "基础积分"),
WeightFactor = table.Column<decimal>(type: "numeric(18,4)", precision: 18, scale: 4, nullable: true, comment: "权重因子"),
ValidityDays = table.Column<int>(type: "integer", nullable: false, comment: "有效期天数"),
Priority = table.Column<int>(type: "integer", nullable: false, comment: "优先级"),
CalculationMode = table.Column<int>(type: "integer", nullable: false, comment: "计算模式"),
IsActive = table.Column<bool>(type: "boolean", nullable: false, comment: "是否启用"),
EffectiveFrom = table.Column<DateTime>(type: "timestamp with time zone", nullable: false, comment: "生效开始时间"),
EffectiveTo = table.Column<DateTime>(type: "timestamp with time zone", nullable: true, comment: "生效结束时间"),
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false, comment: "创建时间"),
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false, comment: "更新时间")
},
constraints: table =>
{
table.PrimaryKey("PK_PointsRules", x => x.Id);
});
migrationBuilder.CreateTable(
name: "fls_member_tag",
columns: table => new
{
id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
member_id = table.Column<Guid>(type: "uuid", nullable: false),
tag_id = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
tag_name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
created_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_fls_member_tag", x => x.id);
table.ForeignKey(
name: "FK_fls_member_tag_fls_member_member_id",
column: x => x.member_id,
principalTable: "fls_member",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "OAuthAuthorization",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
MemberId = table.Column<Guid>(type: "uuid", nullable: false),
Provider = table.Column<int>(type: "integer", nullable: false),
OpenId = table.Column<string>(type: "text", nullable: false),
UnionId = table.Column<string>(type: "text", nullable: true),
AccessToken = table.Column<string>(type: "text", nullable: true),
RefreshToken = table.Column<string>(type: "text", nullable: true),
TokenExpiredAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
AuthorizedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
LastLoginAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_OAuthAuthorization", x => x.Id);
table.ForeignKey(
name: "FK_OAuthAuthorization_fls_member_MemberId",
column: x => x.MemberId,
principalTable: "fls_member",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "PointsTransactions",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
PointsAccountId = table.Column<Guid>(type: "uuid", nullable: false),
MemberId = table.Column<long>(type: "bigint", nullable: false),
TenantId = table.Column<long>(type: "bigint", nullable: false),
Points = table.Column<int>(type: "integer", nullable: false),
TransactionType = table.Column<string>(type: "text", nullable: false),
SourceId = table.Column<string>(type: "text", nullable: false),
TransactionTypeCategory = table.Column<int>(type: "integer", nullable: false),
Remark = table.Column<string>(type: "text", nullable: true),
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
ExpireAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PointsTransactions", x => x.Id);
table.ForeignKey(
name: "FK_PointsTransactions_mka_integraldetails_PointsAccountId",
column: x => x.PointsAccountId,
principalTable: "mka_integraldetails",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "PointsRuleConditions",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false, comment: "条件标识"),
RuleId = table.Column<Guid>(type: "uuid", nullable: false, comment: "关联规则标识"),
DimensionType = table.Column<int>(type: "integer", nullable: false, comment: "维度类型"),
DimensionValue = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false, comment: "维度值"),
Operator = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: true, comment: "操作符"),
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false, comment: "创建时间")
},
constraints: table =>
{
table.PrimaryKey("PK_PointsRuleConditions", x => x.Id);
table.ForeignKey(
name: "FK_PointsRuleConditions_PointsRules_RuleId",
column: x => x.RuleId,
principalTable: "PointsRules",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_ExpiresAt_StatusName",
table: "CAPPublishedMessage",
columns: new[] { "ExpiresAt", "StatusName" });
migrationBuilder.CreateIndex(
name: "IX_Version_ExpiresAt_StatusName",
table: "CAPPublishedMessage",
columns: new[] { "Version", "ExpiresAt", "StatusName" });
migrationBuilder.CreateIndex(
name: "IX_ExpiresAt_StatusName1",
table: "CAPReceivedMessage",
columns: new[] { "ExpiresAt", "StatusName" });
migrationBuilder.CreateIndex(
name: "IX_Version_ExpiresAt_StatusName1",
table: "CAPReceivedMessage",
columns: new[] { "Version", "ExpiresAt", "StatusName" });
migrationBuilder.CreateIndex(
name: "idx_member_openid",
table: "fls_member",
column: "open_id");
migrationBuilder.CreateIndex(
name: "idx_member_tenant_phone",
table: "fls_member",
columns: new[] { "tenant_id", "phone_number" });
migrationBuilder.CreateIndex(
name: "idx_member_tenantid",
table: "fls_member",
column: "tenant_id");
migrationBuilder.CreateIndex(
name: "idx_member_unionid",
table: "fls_member",
column: "union_id");
migrationBuilder.CreateIndex(
name: "idx_membertag_member_tag",
table: "fls_member_tag",
columns: new[] { "member_id", "tag_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "idx_membertag_tagid",
table: "fls_member_tag",
column: "tag_id");
migrationBuilder.CreateIndex(
name: "idx_wechat_auth_memberid",
table: "fls_wechat_authorization",
column: "member_id");
migrationBuilder.CreateIndex(
name: "idx_wechat_auth_openid",
table: "fls_wechat_authorization",
column: "open_id",
unique: true);
migrationBuilder.CreateIndex(
name: "idx_wechat_auth_unionid",
table: "fls_wechat_authorization",
column: "union_id");
migrationBuilder.CreateIndex(
name: "idx_points_account_member_tenant",
table: "mka_integraldetails",
columns: new[] { "user_id", "tenant_id" });
migrationBuilder.CreateIndex(
name: "idx_points_account_memberid",
table: "mka_integraldetails",
column: "user_id",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_OAuthAuthorization_MemberId",
table: "OAuthAuthorization",
column: "MemberId");
migrationBuilder.CreateIndex(
name: "IX_PointsRuleConditions_RuleId_DimensionType",
table: "PointsRuleConditions",
columns: new[] { "RuleId", "DimensionType" });
migrationBuilder.CreateIndex(
name: "IX_PointsRules_Code",
table: "PointsRules",
column: "Code",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_PointsRules_IsActive",
table: "PointsRules",
column: "IsActive");
migrationBuilder.CreateIndex(
name: "IX_PointsTransactions_PointsAccountId",
table: "PointsTransactions",
column: "PointsAccountId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "CAPLock");
migrationBuilder.DropTable(
name: "CAPPublishedMessage");
migrationBuilder.DropTable(
name: "CAPReceivedMessage");
migrationBuilder.DropTable(
name: "fls_member_tag");
migrationBuilder.DropTable(
name: "fls_wechat_authorization");
migrationBuilder.DropTable(
name: "OAuthAuthorization");
migrationBuilder.DropTable(
name: "PointsRuleConditions");
migrationBuilder.DropTable(
name: "PointsTransactions");
migrationBuilder.DropTable(
name: "fls_member");
migrationBuilder.DropTable(
name: "PointsRules");
migrationBuilder.DropTable(
name: "mka_integraldetails");
}
}
}

View File

@ -24,17 +24,18 @@ namespace Fengling.Member.Infrastructure.Migrations
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsModel.PointsAccount", b => modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsModel.PointsAccount", b =>
{ {
b.Property<long>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd() .HasColumnType("uuid")
.HasColumnType("bigint") .HasColumnName("id")
.HasColumnName("id"); .HasComment("积分账户标识");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedAt") b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone") .HasColumnType("timestamp with time zone")
.HasColumnName("created_at"); .HasColumnName("created_at");
b.Property<bool>("Deleted")
.HasColumnType("boolean");
b.Property<int>("FrozenPoints") b.Property<int>("FrozenPoints")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("integer") .HasColumnType("integer")
@ -45,6 +46,10 @@ namespace Fengling.Member.Infrastructure.Migrations
.HasColumnType("bigint") .HasColumnType("bigint")
.HasColumnName("user_id"); .HasColumnName("user_id");
b.Property<int>("RowVersion")
.IsConcurrencyToken()
.HasColumnType("integer");
b.Property<long>("TenantId") b.Property<long>("TenantId")
.HasColumnType("bigint") .HasColumnType("bigint")
.HasColumnName("tenant_id"); .HasColumnName("tenant_id");
@ -98,8 +103,8 @@ namespace Fengling.Member.Infrastructure.Migrations
b.Property<int>("Points") b.Property<int>("Points")
.HasColumnType("integer"); .HasColumnType("integer");
b.Property<long>("PointsAccountId") b.Property<Guid>("PointsAccountId")
.HasColumnType("bigint"); .HasColumnType("uuid");
b.Property<string>("Remark") b.Property<string>("Remark")
.HasColumnType("text"); .HasColumnType("text");
@ -108,6 +113,9 @@ namespace Fengling.Member.Infrastructure.Migrations
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasColumnType("text");
b.Property<long>("TenantId")
.HasColumnType("bigint");
b.Property<string>("TransactionType") b.Property<string>("TransactionType")
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasColumnType("text");
@ -242,6 +250,9 @@ namespace Fengling.Member.Infrastructure.Migrations
.HasColumnType("timestamp with time zone") .HasColumnType("timestamp with time zone")
.HasColumnName("created_at"); .HasColumnName("created_at");
b.Property<bool>("Deleted")
.HasColumnType("boolean");
b.Property<string>("OpenId") b.Property<string>("OpenId")
.HasMaxLength(64) .HasMaxLength(64)
.HasColumnType("character varying(64)") .HasColumnType("character varying(64)")
@ -252,6 +263,10 @@ namespace Fengling.Member.Infrastructure.Migrations
.HasColumnType("character varying(20)") .HasColumnType("character varying(20)")
.HasColumnName("phone_number"); .HasColumnName("phone_number");
b.Property<int>("RowVersion")
.IsConcurrencyToken()
.HasColumnType("integer");
b.Property<string>("Status") b.Property<string>("Status")
.IsRequired() .IsRequired()
.HasMaxLength(20) .HasMaxLength(20)

View File

@ -1,38 +0,0 @@
using Fengling.Member.Domain.Aggregates.PointsModel;
namespace Fengling.Member.Infrastructure.Repositories;
/// <summary>
/// 积分历史仓储接口
/// </summary>
public interface IPointsHistoryRepository
{
/// <summary>
/// 检查 SourceId 是否已存在(幂等性检查)
/// </summary>
Task<bool> ExistsBySourceIdAsync(string sourceId, CancellationToken cancellationToken = default);
/// <summary>
/// 获取会员的积分明细(分页)
/// </summary>
Task<IEnumerable<PointsTransaction>> GetByMemberIdAsync(
long memberId,
int page = 1,
int pageSize = 20,
CancellationToken cancellationToken = default);
/// <summary>
/// 统计会员的积分明细数量
/// </summary>
Task<int> CountByMemberIdAsync(long memberId, CancellationToken cancellationToken = default);
/// <summary>
/// 获取积分明细
/// </summary>
Task<PointsTransaction?> GetByIdAsync(long id, CancellationToken cancellationToken = default);
/// <summary>
/// 添加积分明细
/// </summary>
Task AddAsync(PointsTransaction transaction, CancellationToken cancellationToken = default);
}

View File

@ -1,32 +0,0 @@
using Fengling.Member.Domain.Aggregates.PointsRuleModel;
using Fengling.Member.Domain.Repositories;
namespace Fengling.Member.Infrastructure.Repositories;
public class PointsRuleConditionRepository : IPointsRuleConditionRepository
{
private readonly ApplicationDbContext _context;
public PointsRuleConditionRepository(ApplicationDbContext context)
{
_context = context;
}
public async Task<List<PointsRuleCondition>> GetByRuleIdAsync(PointsRuleId ruleId)
{
return await _context.PointsRuleConditions
.Where(x => x.RuleId == ruleId)
.ToListAsync();
}
public async Task AddAsync(PointsRuleCondition condition)
{
await _context.PointsRuleConditions.AddAsync(condition);
}
public async Task DeleteByRuleIdAsync(PointsRuleId ruleId)
{
var conditions = await GetByRuleIdAsync(ruleId);
_context.PointsRuleConditions.RemoveRange(conditions);
}
}

View File

@ -5,25 +5,27 @@ using Microsoft.EntityFrameworkCore;
namespace Fengling.Member.Infrastructure.Repositories; namespace Fengling.Member.Infrastructure.Repositories;
public class PointsRuleRepository : IPointsRuleRepository public interface IPointsRuleRepository : IRepository<PointsRule, PointsRuleId>
{ {
private readonly ApplicationDbContext _context; Task<PointsRule?> GetByIdAsync(PointsRuleId id);
Task<PointsRule?> GetByCodeAsync(string code);
public PointsRuleRepository(ApplicationDbContext context) Task<List<PointsRule>> GetActiveRulesAsync();
{ Task<List<PointsRule>> GetActiveRulesByDimensionAsync(DimensionType dimensionType, string dimensionValue);
_context = context; }
}
public class PointsRuleRepository(ApplicationDbContext context)
: RepositoryBase<PointsRule, PointsRuleId, ApplicationDbContext>(context), IPointsRuleRepository
{
public async Task<PointsRule?> GetByIdAsync(PointsRuleId id) public async Task<PointsRule?> GetByIdAsync(PointsRuleId id)
{ {
return await _context.PointsRules return await DbContext.PointsRules
.Include(x => x.Conditions) .Include(x => x.Conditions)
.FirstOrDefaultAsync(x => x.Id == id); .FirstOrDefaultAsync(x => x.Id == id);
} }
public async Task<PointsRule?> GetByCodeAsync(string code) public async Task<PointsRule?> GetByCodeAsync(string code)
{ {
return await _context.PointsRules return await DbContext.PointsRules
.Include(x => x.Conditions) .Include(x => x.Conditions)
.FirstOrDefaultAsync(x => x.Code == code); .FirstOrDefaultAsync(x => x.Code == code);
} }
@ -31,7 +33,7 @@ public class PointsRuleRepository : IPointsRuleRepository
public async Task<List<PointsRule>> GetActiveRulesAsync() public async Task<List<PointsRule>> GetActiveRulesAsync()
{ {
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
return await _context.PointsRules return await DbContext.PointsRules
.Include(x => x.Conditions) .Include(x => x.Conditions)
.Where(x => x.IsActive .Where(x => x.IsActive
&& x.EffectiveFrom <= now && x.EffectiveFrom <= now
@ -45,7 +47,7 @@ public class PointsRuleRepository : IPointsRuleRepository
string dimensionValue) string dimensionValue)
{ {
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
return await _context.PointsRules return await DbContext.PointsRules
.Include(x => x.Conditions) .Include(x => x.Conditions)
.Where(x => x.IsActive .Where(x => x.IsActive
&& x.EffectiveFrom <= now && x.EffectiveFrom <= now
@ -54,24 +56,4 @@ public class PointsRuleRepository : IPointsRuleRepository
.OrderByDescending(x => x.Priority) .OrderByDescending(x => x.Priority)
.ToListAsync(); .ToListAsync();
} }
public async Task AddAsync(PointsRule rule)
{
await _context.PointsRules.AddAsync(rule);
}
public async Task UpdateAsync(PointsRule rule)
{
_context.PointsRules.Update(rule);
await Task.CompletedTask;
}
public async Task DeleteAsync(PointsRuleId id)
{
var rule = await GetByIdAsync(id);
if (rule != null)
{
_context.PointsRules.Remove(rule);
}
}
} }

View File

@ -0,0 +1 @@
Unable to retrieve project metadata. Ensure it's an SDK-style project.

View File

@ -128,15 +128,9 @@ public class GetPointsBalanceResponse
public int AvailablePoints { get; set; } public int AvailablePoints { get; set; }
} }
public class GetPointsHistoryEndpoint : Endpoint<GetPointsHistoryRequest, GetPointsHistoryResponse> public class GetPointsHistoryEndpoint(IPointsHistoryRepository historyRepository)
: Endpoint<GetPointsHistoryRequest, GetPointsHistoryResponse>
{ {
private readonly IPointsHistoryRepository _historyRepository;
public GetPointsHistoryEndpoint(IPointsHistoryRepository historyRepository)
{
_historyRepository = historyRepository;
}
public override void Configure() public override void Configure()
{ {
Get("/api/v1/members/{MemberId}/points/history"); Get("/api/v1/members/{MemberId}/points/history");
@ -149,13 +143,13 @@ public class GetPointsHistoryEndpoint : Endpoint<GetPointsHistoryRequest, GetPoi
public override async Task HandleAsync(GetPointsHistoryRequest req, CancellationToken ct) public override async Task HandleAsync(GetPointsHistoryRequest req, CancellationToken ct)
{ {
var transactions = await _historyRepository.GetByMemberIdAsync( var transactions = await historyRepository.GetByMemberIdAsync(
req.MemberId, req.MemberId,
req.Page > 0 ? req.Page : 1, req.Page > 0 ? req.Page : 1,
req.PageSize > 0 && req.PageSize <= 100 ? req.PageSize : 20, req.PageSize > 0 && req.PageSize <= 100 ? req.PageSize : 20,
ct); ct);
var totalCount = await _historyRepository.CountByMemberIdAsync(req.MemberId, ct); var totalCount = await historyRepository.CountByMemberIdAsync(req.MemberId, ct);
Response = new GetPointsHistoryResponse Response = new GetPointsHistoryResponse
{ {

View File

@ -3,7 +3,7 @@ using Fengling.Member.Application.Commands.Points;
using Fengling.Member.Application.Dtos; using Fengling.Member.Application.Dtos;
using Fengling.Member.Application.Dtos.PointsRule; using Fengling.Member.Application.Dtos.PointsRule;
using Fengling.Member.Domain.Aggregates.PointsRuleModel; using Fengling.Member.Domain.Aggregates.PointsRuleModel;
using Fengling.Member.Domain.Repositories; using Fengling.Member.Infrastructure.Repositories;
using MediatR; using MediatR;
namespace Fengling.Member.Web.Endpoints.v1; namespace Fengling.Member.Web.Endpoints.v1;
@ -60,14 +60,10 @@ public class QueryPointsRulesEndpoint : Endpoint<EmptyRequest, List<PointsRuleDt
public class CreatePointsRuleEndpoint : Endpoint<CreatePointsRuleRequest, CreatePointsRuleResponse> public class CreatePointsRuleEndpoint : Endpoint<CreatePointsRuleRequest, CreatePointsRuleResponse>
{ {
private readonly IPointsRuleRepository _ruleRepository; private readonly IPointsRuleRepository _ruleRepository;
private readonly IPointsRuleConditionRepository _conditionRepository;
public CreatePointsRuleEndpoint( public CreatePointsRuleEndpoint(IPointsRuleRepository ruleRepository)
IPointsRuleRepository ruleRepository,
IPointsRuleConditionRepository conditionRepository)
{ {
_ruleRepository = ruleRepository; _ruleRepository = ruleRepository;
_conditionRepository = conditionRepository;
} }
public override void Configure() public override void Configure()
@ -92,8 +88,6 @@ public class CreatePointsRuleEndpoint : Endpoint<CreatePointsRuleRequest, Create
req.CalculationMode, req.CalculationMode,
req.WeightFactor); req.WeightFactor);
await _ruleRepository.AddAsync(rule);
foreach (var condition in req.Conditions) foreach (var condition in req.Conditions)
{ {
var ruleCondition = PointsRuleCondition.Create( var ruleCondition = PointsRuleCondition.Create(
@ -101,10 +95,11 @@ public class CreatePointsRuleEndpoint : Endpoint<CreatePointsRuleRequest, Create
condition.DimensionType, condition.DimensionType,
condition.DimensionValue, condition.DimensionValue,
condition.Operator); condition.Operator);
rule.AddCondition(ruleCondition);
await _conditionRepository.AddAsync(ruleCondition);
} }
await _ruleRepository.AddAsync(rule, ct);
var response = new CreatePointsRuleResponse( var response = new CreatePointsRuleResponse(
Id: rule.Id, Id: rule.Id,
Name: rule.Name, Name: rule.Name,

View File

@ -22,13 +22,14 @@ using NetCorePal.Extensions.CodeAnalysis;
using Fengling.Member.Infrastructure.Repositories; using Fengling.Member.Infrastructure.Repositories;
using Fengling.Member.Application.Services; using Fengling.Member.Application.Services;
using Fengling.Member.Application.Commands.Points; using Fengling.Member.Application.Commands.Points;
using Fengling.Member.Domain.Aggregates.PointsModel;
using Fengling.Member.Web.Endpoints.v1; using Fengling.Member.Web.Endpoints.v1;
using Fengling.Member.Web; using Fengling.Member.Web;
using Fengling.Member.Domain.Repositories; using Fengling.Member.Domain.Repositories;
Log.Logger = new LoggerConfiguration() Log.Logger = new LoggerConfiguration()
.Enrich.WithClientIp() .Enrich.WithClientIp()
.WriteTo.Console(new JsonFormatter()) .WriteTo.Console()
.CreateLogger(); .CreateLogger();
try try
{ {
@ -53,7 +54,12 @@ try
#endregion #endregion
// Add services to the container. // Add services to the container.
builder.Services.AddScoped<IPointsProcessingService, PointsProcessingService>();
builder.Services.AddScoped<IPointsAccountCache, PointsAccountCache>();
builder.Services.AddScoped<ICodeDistributedLock, CodeDistributedLock>();
#region #region
var redis = await ConnectionMultiplexer.ConnectAsync(builder.Configuration.GetConnectionString("Redis")!); var redis = await ConnectionMultiplexer.ConnectAsync(builder.Configuration.GetConnectionString("Redis")!);
@ -144,7 +150,12 @@ try
#endregion #endregion
builder.Services.AddMediatR(cfg => builder.Services.AddMediatR(cfg =>
cfg.RegisterServicesFromAssemblies(Assembly.GetExecutingAssembly()) cfg.RegisterServicesFromAssemblies(
Assembly.GetExecutingAssembly(),
typeof(Fengling.Member.Application.Commands.Member.RegisterMemberCommand).Assembly,
typeof(Fengling.Member.Domain.Aggregates.Users.MemberEntity).Assembly,
typeof(Fengling.Member.Infrastructure.Repositories.MemberRepository).Assembly
)
.AddCommandLockBehavior() .AddCommandLockBehavior()
.AddKnownExceptionValidationBehavior() .AddKnownExceptionValidationBehavior()
.AddUnitOfWorkBehaviors()); .AddUnitOfWorkBehaviors());
@ -185,9 +196,10 @@ try
#region Points Rule Services #region Points Rule Services
builder.Services.AddScoped<IPointsRuleRepository, PointsRuleRepository>(); builder.Services.AddScoped<IPointsProcessingService, PointsProcessingService>();
builder.Services.AddScoped<IPointsRuleConditionRepository, PointsRuleConditionRepository>(); builder.Services.AddScoped<IPointsHistoryRepository, PointsHistoryRepository>();
builder.Services.AddScoped<PointsRuleMatcher>(); builder.Services.AddScoped<IPointsAccountCache, PointsAccountCache>();
builder.Services.AddScoped<PointsRuleMatcher>();
#endregion #endregion

View File

@ -6,7 +6,7 @@
} }
}, },
"ConnectionStrings": { "ConnectionStrings": {
"PostgreSQL": "Host=localhost;Database=dev;Username=postgres;Password=123456", "PostgreSQL": "Host=192.168.100.10;Database=fengling_member;Username=movingsam;Password=sl52788542",
"Redis": "81.68.223.70:16379,password=sl52788542" "Redis": "81.68.223.70:16379,password=sl52788542"
}, },
"RabbitMQ": { "RabbitMQ": {