This commit is contained in:
23
BimAI.Infrastructure/BimAI.Infrastructure.csproj
Normal file
23
BimAI.Infrastructure/BimAI.Infrastructure.csproj
Normal file
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BimAI.Domain\BimAI.Domain.csproj" />
|
||||
<ProjectReference Include="..\BimAI.Application\BimAI.Application.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.17" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.17" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.17">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
90
BimAI.Infrastructure/Data/BimixDbContext.cs
Normal file
90
BimAI.Infrastructure/Data/BimixDbContext.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using BimAI.Domain.Entities;
|
||||
|
||||
namespace BimAI.Infrastructure.Data;
|
||||
|
||||
public class BimAIDbContext(DbContextOptions<BimAIDbContext> options) : DbContext(options)
|
||||
{
|
||||
public DbSet<Product> Products { get; set; }
|
||||
public DbSet<SyncState> SyncStates { get; set; }
|
||||
public DbSet<User> Users { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
// Product properties
|
||||
modelBuilder.Entity<Product>().HasKey(x => x.Id);
|
||||
modelBuilder.Entity<Product>().Property(x => x.Name).IsRequired().HasMaxLength(512);
|
||||
modelBuilder.Entity<Product>().Property(x => x.Code).IsRequired().HasMaxLength(40);
|
||||
modelBuilder.Entity<Product>().Property(x => x.Ean).IsRequired().HasMaxLength(50);
|
||||
modelBuilder.Entity<Product>().Property(x => x.StockAddresses).IsRequired().HasMaxLength(512);
|
||||
|
||||
// SyncState properties
|
||||
modelBuilder.Entity<SyncState>().HasKey((x => x.Entity));
|
||||
|
||||
// User properties
|
||||
modelBuilder.Entity<User>().HasKey(x => x.Id);
|
||||
modelBuilder.Entity<User>().Property(x => x.GoogleId).IsRequired().HasMaxLength(100);
|
||||
modelBuilder.Entity<User>().Property(x => x.Email).IsRequired().HasMaxLength(255);
|
||||
modelBuilder.Entity<User>().Property(x => x.FullName).IsRequired().HasMaxLength(255);
|
||||
modelBuilder.Entity<User>().Property(x => x.IsActive).IsRequired().HasDefaultValue(false);
|
||||
modelBuilder.Entity<User>().Property(x => x.LastLoginAt).IsRequired(false);
|
||||
|
||||
// User indexes
|
||||
modelBuilder.Entity<User>().HasIndex(x => x.GoogleId).IsUnique().HasDatabaseName("IX_Users_GoogleId");
|
||||
modelBuilder.Entity<User>().HasIndex(x => x.Email).IsUnique().HasDatabaseName("IX_Users_Email");
|
||||
|
||||
// Configure defaults for all CreatedAt and UpdatedAt in entities
|
||||
ConfigureBaseEntity(modelBuilder);
|
||||
}
|
||||
|
||||
private void ConfigureBaseEntity(ModelBuilder modelBuilder)
|
||||
{
|
||||
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
|
||||
{
|
||||
if (typeof(BaseEntity).IsAssignableFrom(entityType.ClrType))
|
||||
{
|
||||
modelBuilder.Entity(entityType.ClrType)
|
||||
.Property(nameof(BaseEntity.CreatedAt))
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
modelBuilder.Entity(entityType.ClrType)
|
||||
.Property(nameof(BaseEntity.UpdatedAt))
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override int SaveChanges()
|
||||
{
|
||||
UpdateTimestamps();
|
||||
return base.SaveChanges();
|
||||
}
|
||||
|
||||
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
UpdateTimestamps();
|
||||
return base.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private void UpdateTimestamps()
|
||||
{
|
||||
var entities = ChangeTracker.Entries<BaseEntity>();
|
||||
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
if (entity.State == EntityState.Added)
|
||||
{
|
||||
entity.Entity.CreatedAt = DateTime.UtcNow;
|
||||
entity.Entity.UpdatedAt = DateTime.UtcNow;
|
||||
break;
|
||||
}
|
||||
else if (entity.State == EntityState.Modified)
|
||||
{
|
||||
entity.Entity.UpdatedAt = DateTime.UtcNow;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
BimAI.Infrastructure/Migrations/20250619185202_InitDatabase.Designer.cs
generated
Normal file
52
BimAI.Infrastructure/Migrations/20250619185202_InitDatabase.Designer.cs
generated
Normal file
@@ -0,0 +1,52 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using BimAI.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BimAI.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(BimAIDbContext))]
|
||||
[Migration("20250619185202_InitDatabase")]
|
||||
partial class InitDatabase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.17")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("BimAI.Domain.Entities.Product", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Products");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BimAI.Infrastructure.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitDatabase : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Products",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Products", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Products");
|
||||
}
|
||||
}
|
||||
}
|
||||
65
BimAI.Infrastructure/Migrations/20250623184943_AddSyncState.Designer.cs
generated
Normal file
65
BimAI.Infrastructure/Migrations/20250623184943_AddSyncState.Designer.cs
generated
Normal file
@@ -0,0 +1,65 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using BimAI.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BimAI.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(BimAIDbContext))]
|
||||
[Migration("20250623184943_AddSyncState")]
|
||||
partial class AddSyncState
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.17")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("BimAI.Domain.Entities.Product", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Products");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BimAI.Domain.Entities.SyncState", b =>
|
||||
{
|
||||
b.Property<string>("Entity")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<long>("LastSynced")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Entity");
|
||||
|
||||
b.ToTable("SyncStates");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BimAI.Infrastructure.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddSyncState : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "SyncStates",
|
||||
columns: table => new
|
||||
{
|
||||
Entity = table.Column<string>(type: "nvarchar(450)", nullable: false),
|
||||
LastSynced = table.Column<long>(type: "bigint", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_SyncStates", x => x.Entity);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "SyncStates");
|
||||
}
|
||||
}
|
||||
}
|
||||
65
BimAI.Infrastructure/Migrations/20250623194653_ResizeProductName.Designer.cs
generated
Normal file
65
BimAI.Infrastructure/Migrations/20250623194653_ResizeProductName.Designer.cs
generated
Normal file
@@ -0,0 +1,65 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using BimAI.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BimAI.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(BimAIDbContext))]
|
||||
[Migration("20250623194653_ResizeProductName")]
|
||||
partial class ResizeProductName
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.17")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("BimAI.Domain.Entities.Product", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Products");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BimAI.Domain.Entities.SyncState", b =>
|
||||
{
|
||||
b.Property<string>("Entity")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<long>("LastSynced")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Entity");
|
||||
|
||||
b.ToTable("SyncStates");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BimAI.Infrastructure.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ResizeProductName : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Name",
|
||||
table: "Products",
|
||||
type: "nvarchar(512)",
|
||||
maxLength: 512,
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(200)",
|
||||
oldMaxLength: 200);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Name",
|
||||
table: "Products",
|
||||
type: "nvarchar(200)",
|
||||
maxLength: 200,
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(512)",
|
||||
oldMaxLength: 512);
|
||||
}
|
||||
}
|
||||
}
|
||||
80
BimAI.Infrastructure/Migrations/20250624193445_Products-NewFields.Designer.cs
generated
Normal file
80
BimAI.Infrastructure/Migrations/20250624193445_Products-NewFields.Designer.cs
generated
Normal file
@@ -0,0 +1,80 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using BimAI.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BimAI.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(BimAIDbContext))]
|
||||
[Migration("20250624193445_Products-NewFields")]
|
||||
partial class ProductsNewFields
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.17")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("BimAI.Domain.Entities.Product", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("nvarchar(40)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Ean")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<string>("StockAddresses")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Products");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BimAI.Domain.Entities.SyncState", b =>
|
||||
{
|
||||
b.Property<string>("Entity")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<long>("LastSynced")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Entity");
|
||||
|
||||
b.ToTable("SyncStates");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BimAI.Infrastructure.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ProductsNewFields : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Code",
|
||||
table: "Products",
|
||||
type: "nvarchar(40)",
|
||||
maxLength: 40,
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Ean",
|
||||
table: "Products",
|
||||
type: "nvarchar(50)",
|
||||
maxLength: 50,
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "StockAddresses",
|
||||
table: "Products",
|
||||
type: "nvarchar(512)",
|
||||
maxLength: 512,
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Code",
|
||||
table: "Products");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Ean",
|
||||
table: "Products");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "StockAddresses",
|
||||
table: "Products");
|
||||
}
|
||||
}
|
||||
}
|
||||
136
BimAI.Infrastructure/Migrations/20250718162313_AddUsersTable.Designer.cs
generated
Normal file
136
BimAI.Infrastructure/Migrations/20250718162313_AddUsersTable.Designer.cs
generated
Normal file
@@ -0,0 +1,136 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using BimAI.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BimAI.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(BimAIDbContext))]
|
||||
[Migration("20250718162313_AddUsersTable")]
|
||||
partial class AddUsersTable
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.17")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("BimAI.Domain.Entities.Product", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("nvarchar(40)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Ean")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<string>("StockAddresses")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Products");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BimAI.Domain.Entities.SyncState", b =>
|
||||
{
|
||||
b.Property<string>("Entity")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<long>("LastSynced")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Entity");
|
||||
|
||||
b.ToTable("SyncStates");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BimAI.Domain.Entities.User", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("FullName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("GoogleId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<DateTime?>("LastLoginAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Email")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("IX_Users_Email");
|
||||
|
||||
b.HasIndex("GoogleId")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("IX_Users_GoogleId");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BimAI.Infrastructure.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddUsersTable : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<DateTime>(
|
||||
name: "UpdatedAt",
|
||||
table: "Products",
|
||||
type: "datetime2",
|
||||
nullable: false,
|
||||
defaultValueSql: "GETUTCDATE()",
|
||||
oldClrType: typeof(DateTime),
|
||||
oldType: "datetime2");
|
||||
|
||||
migrationBuilder.AlterColumn<DateTime>(
|
||||
name: "CreatedAt",
|
||||
table: "Products",
|
||||
type: "datetime2",
|
||||
nullable: false,
|
||||
defaultValueSql: "GETUTCDATE()",
|
||||
oldClrType: typeof(DateTime),
|
||||
oldType: "datetime2");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Users",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
GoogleId = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
||||
Email = table.Column<string>(type: "nvarchar(255)", maxLength: 255, nullable: false),
|
||||
FullName = table.Column<string>(type: "nvarchar(255)", maxLength: 255, nullable: false),
|
||||
IsActive = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||
LastLoginAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()"),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Users", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Users_Email",
|
||||
table: "Users",
|
||||
column: "Email",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Users_GoogleId",
|
||||
table: "Users",
|
||||
column: "GoogleId",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Users");
|
||||
|
||||
migrationBuilder.AlterColumn<DateTime>(
|
||||
name: "UpdatedAt",
|
||||
table: "Products",
|
||||
type: "datetime2",
|
||||
nullable: false,
|
||||
oldClrType: typeof(DateTime),
|
||||
oldType: "datetime2",
|
||||
oldDefaultValueSql: "GETUTCDATE()");
|
||||
|
||||
migrationBuilder.AlterColumn<DateTime>(
|
||||
name: "CreatedAt",
|
||||
table: "Products",
|
||||
type: "datetime2",
|
||||
nullable: false,
|
||||
oldClrType: typeof(DateTime),
|
||||
oldType: "datetime2",
|
||||
oldDefaultValueSql: "GETUTCDATE()");
|
||||
}
|
||||
}
|
||||
}
|
||||
133
BimAI.Infrastructure/Migrations/BimixDbContextModelSnapshot.cs
Normal file
133
BimAI.Infrastructure/Migrations/BimixDbContextModelSnapshot.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using BimAI.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BimAI.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(BimAIDbContext))]
|
||||
partial class BimAIDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.17")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("BimAI.Domain.Entities.Product", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("nvarchar(40)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Ean")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<string>("StockAddresses")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Products");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BimAI.Domain.Entities.SyncState", b =>
|
||||
{
|
||||
b.Property<string>("Entity")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<long>("LastSynced")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Entity");
|
||||
|
||||
b.ToTable("SyncStates");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BimAI.Domain.Entities.User", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("FullName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("GoogleId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<DateTime?>("LastLoginAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Email")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("IX_Users_Email");
|
||||
|
||||
b.HasIndex("GoogleId")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("IX_Users_GoogleId");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
87
BimAI.Infrastructure/Sync/ProductSyncService.cs
Normal file
87
BimAI.Infrastructure/Sync/ProductSyncService.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using System.Text.Json;
|
||||
using System.Web;
|
||||
using BimAI.Domain.Entities;
|
||||
using BimAI.Infrastructure.Data;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace BimAI.Infrastructure.Sync;
|
||||
|
||||
public class ProductSyncService(HttpClient httpClient, BimAIDbContext db, IConfiguration configuration)
|
||||
{
|
||||
/// <summary>
|
||||
/// Dekoduje encje HTML w ciągu znaków (np. " na ")
|
||||
/// </summary>
|
||||
private string DecodeHtmlEntities(string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return text;
|
||||
|
||||
return HttpUtility.HtmlDecode(text);
|
||||
}
|
||||
public async Task RunAsync()
|
||||
{
|
||||
var apiKey = configuration["E5_CRM:ApiKey"];
|
||||
var syncState = db.SyncStates.FirstOrDefault(x => x.Entity == "Product") ?? new SyncState { Entity = "Product", LastSynced = 0};
|
||||
|
||||
var url = $"https://crm.e5.pl/REST/index.php?key={apiKey}&action=export.products.list&since={syncState.LastSynced}";
|
||||
var response = await httpClient.GetStringAsync(url);
|
||||
|
||||
var products = JsonSerializer.Deserialize<List<JsonElement>>(response);
|
||||
if (products == null) return;
|
||||
|
||||
var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
|
||||
foreach (var p in products)
|
||||
{
|
||||
var idStr = p.GetProperty("id").GetString() ?? "";
|
||||
var name = DecodeHtmlEntities(p.GetProperty("name").GetString() ?? "");
|
||||
var code = DecodeHtmlEntities(p.GetProperty("code").GetString() ?? "");
|
||||
var stockAddresses = DecodeHtmlEntities(p.GetProperty("stock_addresses").GetString() ?? "");
|
||||
var ean = DecodeHtmlEntities(p.GetProperty("ean").GetString() ?? "");
|
||||
|
||||
if (!Guid.TryParse(idStr, out Guid id))
|
||||
{
|
||||
Console.WriteLine($"[SYNC] Skipping product with wrong ID: '{idStr}', Name: '{name}'");
|
||||
continue;
|
||||
}
|
||||
|
||||
var existing = db.Products.FirstOrDefault(x => x.Id == id);
|
||||
|
||||
if (existing == null)
|
||||
{
|
||||
var product = new Product
|
||||
{
|
||||
Id = id,
|
||||
Name = name,
|
||||
Ean = ean,
|
||||
Code = code,
|
||||
StockAddresses = stockAddresses,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
};
|
||||
db.Products.Add(product);
|
||||
}
|
||||
else
|
||||
{
|
||||
existing.Name = name;
|
||||
existing.UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
Console.WriteLine($"[SYNC] Updated product ID: '{idStr}', Name: '{name}'");
|
||||
|
||||
var exportedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
var updateUrl = $"https://crm.e5.pl/REST/index.php?key={apiKey}&action=export.products.setExportedAt&id={id}&exportedAt={exportedAt}";
|
||||
await httpClient.GetAsync(updateUrl);
|
||||
}
|
||||
syncState.LastSynced = now;
|
||||
if (db.SyncStates.FirstOrDefault(x => x.Entity == "Product") == null)
|
||||
{
|
||||
db.SyncStates.Add(syncState);
|
||||
}
|
||||
else
|
||||
{
|
||||
db.SyncStates.Update(syncState);
|
||||
}
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user