Commit cd6f6254 authored by gdlcf88's avatar gdlcf88

Close #20: Record entity data when a product changed

parent 378d145a
......@@ -3,6 +3,7 @@ using Volo.Abp.Application.Dtos;
namespace EasyAbp.EShop.Products.ProductDetails.Dtos
{
[Serializable]
public class ProductDetailDto : EntityDto<Guid>
{
public string Description { get; set; }
......
......@@ -4,6 +4,7 @@ using Volo.Abp.Application.Dtos;
namespace EasyAbp.EShop.Products.Products.Dtos
{
[Serializable]
public class ProductDto : FullAuditedEntityDto<Guid>
{
public Guid ProductTypeId { get; set; }
......
using System.Threading.Tasks;
using EasyAbp.EShop.Products.ProductDetails;
using EasyAbp.EShop.Products.ProductDetails.Dtos;
using EasyAbp.EShop.Products.ProductHistories;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus;
using Volo.Abp.Guids;
using Volo.Abp.Json;
using Volo.Abp.ObjectMapping;
namespace EasyAbp.EShop.Products.ProductDetailHistories
{
public class ProductDetailHistoryRecorder : ILocalEventHandler<EntityChangedEventData<ProductDetail>>, ITransientDependency
{
private readonly IGuidGenerator _guidGenerator;
private readonly IObjectMapper _objectMapper;
private readonly IJsonSerializer _jsonSerializer;
private readonly IProductDetailHistoryRepository _productDetailHistoryRepository;
public ProductDetailHistoryRecorder(
IGuidGenerator guidGenerator,
IObjectMapper objectMapper,
IJsonSerializer jsonSerializer,
IProductDetailHistoryRepository productDetailHistoryRepository)
{
_guidGenerator = guidGenerator;
_objectMapper = objectMapper;
_jsonSerializer = jsonSerializer;
_productDetailHistoryRepository = productDetailHistoryRepository;
}
public async Task HandleEventAsync(EntityChangedEventData<ProductDetail> eventData)
{
var modificationTime = eventData.Entity.LastModificationTime ?? eventData.Entity.CreationTime;
var serializedDto =
_jsonSerializer.Serialize(_objectMapper.Map<ProductDetail, ProductDetailDto>(eventData.Entity));
await _productDetailHistoryRepository.InsertAsync(new ProductDetailHistory(_guidGenerator.Create(),
eventData.Entity.Id, modificationTime, serializedDto));
}
}
}
\ No newline at end of file
using System.Threading.Tasks;
using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Products.Products.Dtos;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus;
using Volo.Abp.Guids;
using Volo.Abp.Json;
using Volo.Abp.ObjectMapping;
namespace EasyAbp.EShop.Products.ProductHistories
{
public class ProductHistoryRecorder : ILocalEventHandler<EntityChangedEventData<Product>>, ITransientDependency
{
private readonly IGuidGenerator _guidGenerator;
private readonly IObjectMapper _objectMapper;
private readonly IJsonSerializer _jsonSerializer;
private readonly IProductHistoryRepository _productHistoryRepository;
public ProductHistoryRecorder(
IGuidGenerator guidGenerator,
IObjectMapper objectMapper,
IJsonSerializer jsonSerializer,
IProductHistoryRepository productHistoryRepository)
{
_guidGenerator = guidGenerator;
_objectMapper = objectMapper;
_jsonSerializer = jsonSerializer;
_productHistoryRepository = productHistoryRepository;
}
public async Task HandleEventAsync(EntityChangedEventData<Product> eventData)
{
var modificationTime = eventData.Entity.LastModificationTime ?? eventData.Entity.CreationTime;
var serializedDto = _jsonSerializer.Serialize(_objectMapper.Map<Product, ProductDto>(eventData.Entity));
await _productHistoryRepository.InsertAsync(new ProductHistory(_guidGenerator.Create(), eventData.Entity.Id,
modificationTime, serializedDto));
}
}
}
\ No newline at end of file
......@@ -65,7 +65,7 @@ namespace EasyAbp.EShop.Products.Products
await Repository.InsertAsync(product, autoSave: true);
await CheckProductDetailIdAsync(product.Id, input.ProductDetailId);
await CheckProductDetailAvailableAsync(product.Id, input.ProductDetailId);
await AddProductToStoreAsync(product.Id, input.StoreId);
......@@ -74,7 +74,7 @@ namespace EasyAbp.EShop.Products.Products
return MapToGetOutputDto(product);
}
private async Task CheckProductDetailIdAsync(Guid currentProductId, Guid desiredProductDetailId)
protected virtual async Task CheckProductDetailAvailableAsync(Guid currentProductId, Guid desiredProductDetailId)
{
var otherOwner = await _repository.FindAsync(x =>
x.ProductDetailId == desiredProductDetailId && x.Id != currentProductId);
......@@ -109,7 +109,7 @@ namespace EasyAbp.EShop.Products.Products
await Repository.UpdateAsync(product, autoSave: true);
await CheckProductDetailIdAsync(product.Id, input.ProductDetailId);
await CheckProductDetailAvailableAsync(product.Id, input.ProductDetailId);
await UpdateProductCategoriesAsync(product.Id, input.CategoryIds);
......
......@@ -9,6 +9,7 @@ using EasyAbp.EShop.Products.ProductCategories.Dtos;
using AutoMapper;
using EasyAbp.EShop.Products.ProductDetails;
using EasyAbp.EShop.Products.ProductDetails.Dtos;
using EasyAbp.EShop.Products.ProductDetailHistories;
using Volo.Abp.AutoMapper;
namespace EasyAbp.EShop.Products
......
using System;
using Volo.Abp.Domain.Repositories;
namespace EasyAbp.EShop.Products.ProductDetailHistories
{
public interface IProductDetailHistoryRepository : IRepository<ProductDetailHistory, Guid>
{
}
}
\ No newline at end of file
using System;
using JetBrains.Annotations;
using Volo.Abp.Domain.Entities;
namespace EasyAbp.EShop.Products.ProductDetailHistories
{
public class ProductDetailHistory : AggregateRoot<Guid>
{
public virtual Guid ProductDetailId { get; protected set; }
public virtual DateTime ModificationTime { get; protected set; }
[NotNull]
public virtual string SerializedDto { get; protected set; }
protected ProductDetailHistory() {}
public ProductDetailHistory(
Guid id,
Guid productDetailId,
DateTime modificationTime,
[NotNull] string serializedDto) : base(id)
{
ProductDetailId = productDetailId;
ModificationTime = modificationTime;
SerializedDto = serializedDto;
}
}
}
\ No newline at end of file
using System;
using Volo.Abp.Domain.Repositories;
namespace EasyAbp.EShop.Products.ProductHistories
{
public interface IProductHistoryRepository : IRepository<ProductHistory, Guid>
{
}
}
\ No newline at end of file
using System;
using JetBrains.Annotations;
using Volo.Abp.Domain.Entities;
namespace EasyAbp.EShop.Products.ProductHistories
{
public class ProductHistory : AggregateRoot<Guid>
{
public virtual Guid ProductId { get; protected set; }
public virtual DateTime ModificationTime { get; protected set; }
[NotNull]
public virtual string SerializedDto { get; protected set; }
protected ProductHistory() {}
public ProductHistory(
Guid id,
Guid productId,
DateTime modificationTime,
[NotNull] string serializedDto) : base(id)
{
ProductId = productId;
ModificationTime = modificationTime;
SerializedDto = serializedDto;
}
}
}
\ No newline at end of file
......@@ -2,13 +2,20 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
namespace EasyAbp.EShop.Products.Products
{
public class AttributeOptionIdsSerializer : IAttributeOptionIdsSerializer, ITransientDependency
{
private readonly IJsonSerializer _jsonSerializer;
public AttributeOptionIdsSerializer(IJsonSerializer jsonSerializer)
{
_jsonSerializer = jsonSerializer;
}
public async Task<string> FormatAsync(string serializedAttributeOptionIds)
{
return await SerializeAsync(await DeserializeAsync(serializedAttributeOptionIds));
......@@ -16,12 +23,12 @@ namespace EasyAbp.EShop.Products.Products
public Task<string> SerializeAsync(IEnumerable<Guid> attributeOptionIds)
{
return Task.FromResult(JsonConvert.SerializeObject(attributeOptionIds.OrderBy(x => x)));
return Task.FromResult(_jsonSerializer.Serialize(attributeOptionIds.OrderBy(x => x)));
}
public Task<IEnumerable<Guid>> DeserializeAsync(string serializedAttributeOptionIds)
{
return Task.FromResult(JsonConvert.DeserializeObject<IEnumerable<Guid>>(serializedAttributeOptionIds));
return Task.FromResult(_jsonSerializer.Deserialize<IEnumerable<Guid>>(serializedAttributeOptionIds));
}
}
}
\ No newline at end of file
using EasyAbp.EShop.Products.ProductDetailHistories;
using EasyAbp.EShop.Products.ProductHistories;
using EasyAbp.EShop.Products.ProductStores;
using EasyAbp.EShop.Products.ProductCategories;
using EasyAbp.EShop.Products.ProductTypes;
......@@ -29,6 +31,8 @@ namespace EasyAbp.EShop.Products.EntityFrameworkCore
options.AddRepository<ProductType, ProductTypeRepository>();
options.AddRepository<ProductCategory, ProductCategoryRepository>();
options.AddRepository<ProductStore, ProductStoreRepository>();
options.AddRepository<ProductHistory, ProductHistoryRepository>();
options.AddRepository<ProductDetailHistory, ProductDetailHistoryRepository>();
});
}
}
......
......@@ -7,6 +7,8 @@ using EasyAbp.EShop.Products.ProductTypes;
using EasyAbp.EShop.Products.ProductCategories;
using EasyAbp.EShop.Products.ProductDetails;
using EasyAbp.EShop.Products.ProductStores;
using EasyAbp.EShop.Products.ProductHistories;
using EasyAbp.EShop.Products.ProductDetailHistories;
namespace EasyAbp.EShop.Products.EntityFrameworkCore
{
......@@ -25,5 +27,7 @@ namespace EasyAbp.EShop.Products.EntityFrameworkCore
DbSet<ProductType> ProductTypes { get; set; }
DbSet<ProductCategory> ProductCategories { get; set; }
DbSet<ProductStore> ProductStores { get; set; }
DbSet<ProductHistory> ProductHistories { get; set; }
DbSet<ProductDetailHistory> ProductDetailHistories { get; set; }
}
}
......@@ -7,6 +7,8 @@ using EasyAbp.EShop.Products.ProductTypes;
using EasyAbp.EShop.Products.ProductCategories;
using EasyAbp.EShop.Products.ProductDetails;
using EasyAbp.EShop.Products.ProductStores;
using EasyAbp.EShop.Products.ProductHistories;
using EasyAbp.EShop.Products.ProductDetailHistories;
namespace EasyAbp.EShop.Products.EntityFrameworkCore
{
......@@ -25,6 +27,8 @@ namespace EasyAbp.EShop.Products.EntityFrameworkCore
public DbSet<ProductType> ProductTypes { get; set; }
public DbSet<ProductCategory> ProductCategories { get; set; }
public DbSet<ProductStore> ProductStores { get; set; }
public DbSet<ProductHistory> ProductHistories { get; set; }
public DbSet<ProductDetailHistory> ProductDetailHistories { get; set; }
public ProductsDbContext(DbContextOptions<ProductsDbContext> options)
: base(options)
......
using EasyAbp.EShop.Products.ProductDetailHistories;
using EasyAbp.EShop.Products.ProductHistories;
using EasyAbp.EShop.Products.ProductStores;
using EasyAbp.EShop.Products.ProductCategories;
using EasyAbp.EShop.Products.ProductTypes;
......@@ -110,6 +112,20 @@ namespace EasyAbp.EShop.Products.EntityFrameworkCore
b.ConfigureByConvention();
/* Configure more properties here */
});
builder.Entity<ProductHistory>(b =>
{
b.ToTable(options.TablePrefix + "ProductHistories", options.Schema);
b.ConfigureByConvention();
/* Configure more properties here */
});
builder.Entity<ProductDetailHistory>(b =>
{
b.ToTable(options.TablePrefix + "ProductDetailHistories", options.Schema);
b.ConfigureByConvention();
/* Configure more properties here */
});
}
}
}
using System;
using EasyAbp.EShop.Products.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace EasyAbp.EShop.Products.ProductDetailHistories
{
public class ProductDetailHistoryRepository : EfCoreRepository<ProductsDbContext, ProductDetailHistory, Guid>, IProductDetailHistoryRepository
{
public ProductDetailHistoryRepository(IDbContextProvider<ProductsDbContext> dbContextProvider) : base(dbContextProvider)
{
}
}
}
\ No newline at end of file
using System;
using EasyAbp.EShop.Products.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace EasyAbp.EShop.Products.ProductHistories
{
public class ProductHistoryRepository : EfCoreRepository<ProductsDbContext, ProductHistory, Guid>, IProductHistoryRepository
{
public ProductHistoryRepository(IDbContextProvider<ProductsDbContext> dbContextProvider) : base(dbContextProvider)
{
}
}
}
\ No newline at end of file
......@@ -2,18 +2,12 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Products.Categories;
using EasyAbp.EShop.Products.ProductDetails;
using EasyAbp.EShop.Products.ProductDetails.Dtos;
using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Products.Products.Dtos;
using EasyAbp.EShop.Products.ProductTypes;
using EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product.ViewModels;
using EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku.ViewModels;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Newtonsoft.Json;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Json;
namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku
{
......@@ -34,11 +28,15 @@ namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku
public Dictionary<string, Guid> SelectedAttributeOptionIdDict { get; set; }
public Dictionary<string, ICollection<SelectListItem>> Attributes { get; set; }
private readonly IJsonSerializer _jsonSerializer;
private readonly IProductAppService _productAppService;
public CreateModalModel(IProductAppService productAppService)
public CreateModalModel(
IJsonSerializer jsonSerializer,
IProductAppService productAppService)
{
_jsonSerializer = jsonSerializer;
_productAppService = productAppService;
}
......@@ -60,7 +58,7 @@ namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku
{
var createDto = ObjectMapper.Map<CreateEditProductSkuViewModel, CreateProductSkuDto>(ProductSku);
createDto.SerializedAttributeOptionIds = JsonConvert.SerializeObject(SelectedAttributeOptionIdDict.Values);
createDto.SerializedAttributeOptionIds = _jsonSerializer.Serialize(SelectedAttributeOptionIdDict.Values);
await _productAppService.CreateSkuAsync(ProductId, StoreId, createDto);
......
using System.Threading.Tasks;
using Shouldly;
using Xunit;
namespace EasyAbp.EShop.Products.ProductDetailHistories
{
public class ProductDetailHistoryDomainTests : ProductsDomainTestBase
{
public ProductDetailHistoryDomainTests()
{
}
[Fact]
public async Task Test1()
{
// Arrange
// Assert
// Assert
}
}
}
using System.Threading.Tasks;
using Shouldly;
using Xunit;
namespace EasyAbp.EShop.Products.ProductHistories
{
public class ProductHistoryDomainTests : ProductsDomainTestBase
{
public ProductHistoryDomainTests()
{
}
[Fact]
public async Task Test1()
{
// Arrange
// Assert
// Assert
}
}
}
using System;
using System.Threading.Tasks;
using EasyAbp.EShop.Products.ProductDetailHistories;
using Volo.Abp.Domain.Repositories;
using Xunit;
namespace EasyAbp.EShop.Products.EntityFrameworkCore.ProductDetailHistories
{
public class ProductDetailHistoryRepositoryTests : ProductsEntityFrameworkCoreTestBase
{
private readonly IRepository<ProductDetailHistory, Guid> _productDetailHistoryRepository;
public ProductDetailHistoryRepositoryTests()
{
_productDetailHistoryRepository = GetRequiredService<IRepository<ProductDetailHistory, Guid>>();
}
[Fact]
public async Task Test1()
{
await WithUnitOfWorkAsync(async () =>
{
// Arrange
// Act
//Assert
});
}
}
}
using System;
using System.Threading.Tasks;
using EasyAbp.EShop.Products.ProductHistories;
using Volo.Abp.Domain.Repositories;
using Xunit;
namespace EasyAbp.EShop.Products.EntityFrameworkCore.ProductHistories
{
public class ProductHistoryRepositoryTests : ProductsEntityFrameworkCoreTestBase
{
private readonly IRepository<ProductHistory, Guid> _productHistoryRepository;
public ProductHistoryRepositoryTests()
{
_productHistoryRepository = GetRequiredService<IRepository<ProductHistory, Guid>>();
}
[Fact]
public async Task Test1()
{
await WithUnitOfWorkAsync(async () =>
{
// Arrange
// Act
//Assert
});
}
}
}
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace EasyMall.Migrations
{
public partial class AddedHistoryEntities : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ProductsProductDetailHistories",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
ExtraProperties = table.Column<string>(nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true),
ProductDetailId = table.Column<Guid>(nullable: false),
ModificationTime = table.Column<DateTime>(nullable: false),
SerializedDto = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ProductsProductDetailHistories", x => x.Id);
});
migrationBuilder.CreateTable(
name: "ProductsProductHistories",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
ExtraProperties = table.Column<string>(nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true),
ProductId = table.Column<Guid>(nullable: false),
ModificationTime = table.Column<DateTime>(nullable: false),
SerializedDto = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ProductsProductHistories", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ProductsProductDetailHistories");
migrationBuilder.DropTable(
name: "ProductsProductHistories");
}
}
}
......@@ -137,6 +137,35 @@ namespace EasyMall.Migrations
b.ToTable("ProductsProductCategories");
});
modelBuilder.Entity("EasyAbp.EShop.Products.ProductDetailHistories.ProductDetailHistory", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnName("ConcurrencyStamp")
.HasColumnType("nvarchar(max)");
b.Property<string>("ExtraProperties")
.HasColumnName("ExtraProperties")
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("ModificationTime")
.HasColumnType("datetime2");
b.Property<Guid>("ProductDetailId")
.HasColumnType("uniqueidentifier");
b.Property<string>("SerializedDto")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("ProductsProductDetailHistories");
});
modelBuilder.Entity("EasyAbp.EShop.Products.ProductDetails.ProductDetail", b =>
{
b.Property<Guid>("Id")
......@@ -190,6 +219,35 @@ namespace EasyMall.Migrations
b.ToTable("ProductsProductDetails");
});
modelBuilder.Entity("EasyAbp.EShop.Products.ProductHistories.ProductHistory", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnName("ConcurrencyStamp")
.HasColumnType("nvarchar(max)");
b.Property<string>("ExtraProperties")
.HasColumnName("ExtraProperties")
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("ModificationTime")
.HasColumnType("datetime2");
b.Property<Guid>("ProductId")
.HasColumnType("uniqueidentifier");
b.Property<string>("SerializedDto")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("ProductsProductHistories");
});
modelBuilder.Entity("EasyAbp.EShop.Products.ProductStores.ProductStore", b =>
{
b.Property<Guid>("Id")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment