diff --git a/EShop.sln b/EShop.sln index 66339cb1..cb7e623f 100644 --- a/EShop.sln +++ b/EShop.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29020.237 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32526.322 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EShopSample.Domain", "samples\EShopSample\aspnet-core\src\EShopSample.Domain\EShopSample.Domain.csproj", "{554AD327-6DBA-4F8F-96F8-81CE7A0C863F}" EndProject @@ -303,77 +303,77 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{D261 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EShopSample", "EShopSample", "{FE0A5FD6-AEE7-4738-9A6D-261BF086704D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Stores.Application.Shared", "modules\EasyAbp.EShop.Stores\src\EasyAbp.EShop.Stores.Application.Shared\EasyAbp.EShop.Stores.Application.Shared.csproj", "{06700175-4E04-4993-A1FB-24E7A8835E19}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Stores.Application.Shared", "modules\EasyAbp.EShop.Stores\src\EasyAbp.EShop.Stores.Application.Shared\EasyAbp.EShop.Stores.Application.Shared.csproj", "{06700175-4E04-4993-A1FB-24E7A8835E19}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{72F34527-9295-4F29-923E-4B075A4F31A2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Coupons.Application", "plugins\Coupons\src\EasyAbp.EShop.Plugins.Coupons.Application\EasyAbp.EShop.Plugins.Coupons.Application.csproj", "{1EB6504F-7FF0-4628-9D7C-81BDE33D9490}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Coupons.Application", "plugins\Coupons\src\EasyAbp.EShop.Plugins.Coupons.Application\EasyAbp.EShop.Plugins.Coupons.Application.csproj", "{1EB6504F-7FF0-4628-9D7C-81BDE33D9490}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Coupons.Application.Contracts", "plugins\Coupons\src\EasyAbp.EShop.Plugins.Coupons.Application.Contracts\EasyAbp.EShop.Plugins.Coupons.Application.Contracts.csproj", "{C7D05698-E509-4F5C-A792-0036190AE6FF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Coupons.Application.Contracts", "plugins\Coupons\src\EasyAbp.EShop.Plugins.Coupons.Application.Contracts\EasyAbp.EShop.Plugins.Coupons.Application.Contracts.csproj", "{C7D05698-E509-4F5C-A792-0036190AE6FF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Coupons.Domain", "plugins\Coupons\src\EasyAbp.EShop.Plugins.Coupons.Domain\EasyAbp.EShop.Plugins.Coupons.Domain.csproj", "{4DBC9216-80FD-4E71-BCB3-0F1ADA63AEC2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Coupons.Domain", "plugins\Coupons\src\EasyAbp.EShop.Plugins.Coupons.Domain\EasyAbp.EShop.Plugins.Coupons.Domain.csproj", "{4DBC9216-80FD-4E71-BCB3-0F1ADA63AEC2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Coupons.Domain.Shared", "plugins\Coupons\src\EasyAbp.EShop.Plugins.Coupons.Domain.Shared\EasyAbp.EShop.Plugins.Coupons.Domain.Shared.csproj", "{31F1DC02-5790-4FAD-A8F2-0D6F9FE21F00}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Coupons.Domain.Shared", "plugins\Coupons\src\EasyAbp.EShop.Plugins.Coupons.Domain.Shared\EasyAbp.EShop.Plugins.Coupons.Domain.Shared.csproj", "{31F1DC02-5790-4FAD-A8F2-0D6F9FE21F00}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Coupons.EntityFrameworkCore", "plugins\Coupons\src\EasyAbp.EShop.Plugins.Coupons.EntityFrameworkCore\EasyAbp.EShop.Plugins.Coupons.EntityFrameworkCore.csproj", "{EE6B9D10-7038-4CDE-A6BF-3941AFD84D7A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Coupons.EntityFrameworkCore", "plugins\Coupons\src\EasyAbp.EShop.Plugins.Coupons.EntityFrameworkCore\EasyAbp.EShop.Plugins.Coupons.EntityFrameworkCore.csproj", "{EE6B9D10-7038-4CDE-A6BF-3941AFD84D7A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Coupons.HttpApi", "plugins\Coupons\src\EasyAbp.EShop.Plugins.Coupons.HttpApi\EasyAbp.EShop.Plugins.Coupons.HttpApi.csproj", "{0E22DE0A-032B-442E-AEB2-E299B333BADC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Coupons.HttpApi", "plugins\Coupons\src\EasyAbp.EShop.Plugins.Coupons.HttpApi\EasyAbp.EShop.Plugins.Coupons.HttpApi.csproj", "{0E22DE0A-032B-442E-AEB2-E299B333BADC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Coupons.HttpApi.Client", "plugins\Coupons\src\EasyAbp.EShop.Plugins.Coupons.HttpApi.Client\EasyAbp.EShop.Plugins.Coupons.HttpApi.Client.csproj", "{85D39FAB-A9A8-4942-BF94-A6061FCFBF7A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Coupons.HttpApi.Client", "plugins\Coupons\src\EasyAbp.EShop.Plugins.Coupons.HttpApi.Client\EasyAbp.EShop.Plugins.Coupons.HttpApi.Client.csproj", "{85D39FAB-A9A8-4942-BF94-A6061FCFBF7A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Coupons.MongoDB", "plugins\Coupons\src\EasyAbp.EShop.Plugins.Coupons.MongoDB\EasyAbp.EShop.Plugins.Coupons.MongoDB.csproj", "{C97330F5-4634-4865-9D7D-E33F87808938}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Coupons.MongoDB", "plugins\Coupons\src\EasyAbp.EShop.Plugins.Coupons.MongoDB\EasyAbp.EShop.Plugins.Coupons.MongoDB.csproj", "{C97330F5-4634-4865-9D7D-E33F87808938}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Coupons.Web", "plugins\Coupons\src\EasyAbp.EShop.Plugins.Coupons.Web\EasyAbp.EShop.Plugins.Coupons.Web.csproj", "{C11BD71D-FA6B-4833-9CAE-82ED9D857A84}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Coupons.Web", "plugins\Coupons\src\EasyAbp.EShop.Plugins.Coupons.Web\EasyAbp.EShop.Plugins.Coupons.Web.csproj", "{C11BD71D-FA6B-4833-9CAE-82ED9D857A84}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{4001814E-A67B-490D-9E13-2FB9A34B0A0B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Coupons.Application.Tests", "plugins\Coupons\test\EasyAbp.EShop.Plugins.Coupons.Application.Tests\EasyAbp.EShop.Plugins.Coupons.Application.Tests.csproj", "{10675CED-5689-4B2F-895A-E13820928A3B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Coupons.Application.Tests", "plugins\Coupons\test\EasyAbp.EShop.Plugins.Coupons.Application.Tests\EasyAbp.EShop.Plugins.Coupons.Application.Tests.csproj", "{10675CED-5689-4B2F-895A-E13820928A3B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Coupons.Domain.Tests", "plugins\Coupons\test\EasyAbp.EShop.Plugins.Coupons.Domain.Tests\EasyAbp.EShop.Plugins.Coupons.Domain.Tests.csproj", "{B29ABA23-37B2-419A-9E3C-F1A8703D584D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Coupons.Domain.Tests", "plugins\Coupons\test\EasyAbp.EShop.Plugins.Coupons.Domain.Tests\EasyAbp.EShop.Plugins.Coupons.Domain.Tests.csproj", "{B29ABA23-37B2-419A-9E3C-F1A8703D584D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Coupons.EntityFrameworkCore.Tests", "plugins\Coupons\test\EasyAbp.EShop.Plugins.Coupons.EntityFrameworkCore.Tests\EasyAbp.EShop.Plugins.Coupons.EntityFrameworkCore.Tests.csproj", "{D81EC4DC-3944-4CFE-875A-5DD07BE487AE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Coupons.EntityFrameworkCore.Tests", "plugins\Coupons\test\EasyAbp.EShop.Plugins.Coupons.EntityFrameworkCore.Tests\EasyAbp.EShop.Plugins.Coupons.EntityFrameworkCore.Tests.csproj", "{D81EC4DC-3944-4CFE-875A-5DD07BE487AE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Coupons.HttpApi.Client.ConsoleTestApp", "plugins\Coupons\test\EasyAbp.EShop.Plugins.Coupons.HttpApi.Client.ConsoleTestApp\EasyAbp.EShop.Plugins.Coupons.HttpApi.Client.ConsoleTestApp.csproj", "{8B22E009-5ABA-4878-83E5-38154F2B8E2F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Coupons.HttpApi.Client.ConsoleTestApp", "plugins\Coupons\test\EasyAbp.EShop.Plugins.Coupons.HttpApi.Client.ConsoleTestApp\EasyAbp.EShop.Plugins.Coupons.HttpApi.Client.ConsoleTestApp.csproj", "{8B22E009-5ABA-4878-83E5-38154F2B8E2F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Coupons.MongoDB.Tests", "plugins\Coupons\test\EasyAbp.EShop.Plugins.Coupons.MongoDB.Tests\EasyAbp.EShop.Plugins.Coupons.MongoDB.Tests.csproj", "{86CAD303-A0E5-42C9-89A5-61D9AAA4AD8F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Coupons.MongoDB.Tests", "plugins\Coupons\test\EasyAbp.EShop.Plugins.Coupons.MongoDB.Tests\EasyAbp.EShop.Plugins.Coupons.MongoDB.Tests.csproj", "{86CAD303-A0E5-42C9-89A5-61D9AAA4AD8F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Coupons.TestBase", "plugins\Coupons\test\EasyAbp.EShop.Plugins.Coupons.TestBase\EasyAbp.EShop.Plugins.Coupons.TestBase.csproj", "{B076C103-DF0B-464B-A9CB-4BE5CAFEE067}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Coupons.TestBase", "plugins\Coupons\test\EasyAbp.EShop.Plugins.Coupons.TestBase\EasyAbp.EShop.Plugins.Coupons.TestBase.csproj", "{B076C103-DF0B-464B-A9CB-4BE5CAFEE067}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Orders.Plugins.Coupons", "plugins\Coupons\src\EasyAbp.EShop.Orders.Plugins.Coupons\EasyAbp.EShop.Orders.Plugins.Coupons.csproj", "{3C385657-8365-470F-9F4F-30F31F9FCA42}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Orders.Plugins.Coupons", "plugins\Coupons\src\EasyAbp.EShop.Orders.Plugins.Coupons\EasyAbp.EShop.Orders.Plugins.Coupons.csproj", "{3C385657-8365-470F-9F4F-30F31F9FCA42}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Inventories", "Inventories", "{9AC27747-E175-487F-92C9-434DEE543273}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DaprActors", "DaprActors", "{6E6FE4B9-4117-4F57-B219-EE47E4046096}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Products.DaprActorsInventory.Domain", "plugins\Inventories\DaprActors\src\EasyAbp.EShop.Products.DaprActorsInventory.Domain\EasyAbp.EShop.Products.DaprActorsInventory.Domain.csproj", "{6CD1A8B5-8AB7-4A31-8333-024A7FB602D1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Products.DaprActorsInventory.Domain", "plugins\Inventories\DaprActors\src\EasyAbp.EShop.Products.DaprActorsInventory.Domain\EasyAbp.EShop.Products.DaprActorsInventory.Domain.csproj", "{6CD1A8B5-8AB7-4A31-8333-024A7FB602D1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions", "plugins\Inventories\DaprActors\src\EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions\EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions.csproj", "{CF4DE32D-9629-4C48-9BE8-5B83A1C27291}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions", "plugins\Inventories\DaprActors\src\EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions\EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions.csproj", "{CF4DE32D-9629-4C48-9BE8-5B83A1C27291}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Inventories.DaprActors", "plugins\Inventories\DaprActors\src\EasyAbp.EShop.Plugins.Inventories.DaprActors\EasyAbp.EShop.Plugins.Inventories.DaprActors.csproj", "{B6F3ACD5-463E-4455-A094-057A82941A94}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Inventories.DaprActors", "plugins\Inventories\DaprActors\src\EasyAbp.EShop.Plugins.Inventories.DaprActors\EasyAbp.EShop.Plugins.Inventories.DaprActors.csproj", "{B6F3ACD5-463E-4455-A094-057A82941A94}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F468A386-5660-4888-981A-6ECF15182D32}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{485204B1-7603-4EA0-B3A4-73CB89B0D5BC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests", "plugins\Inventories\DaprActors\test\EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests\EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests.csproj", "{733C51A3-19C8-45C4-8B22-3FD40CAF4EFB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests", "plugins\Inventories\DaprActors\test\EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests\EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests.csproj", "{733C51A3-19C8-45C4-8B22-3FD40CAF4EFB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Inventories.DaprActors.AspNetCore", "plugins\Inventories\DaprActors\src\EasyAbp.EShop.Plugins.Inventories.DaprActors.AspNetCore\EasyAbp.EShop.Plugins.Inventories.DaprActors.AspNetCore.csproj", "{3F0EA314-CCF4-4BB2-A8C1-79FAE4442884}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Inventories.DaprActors.AspNetCore", "plugins\Inventories\DaprActors\src\EasyAbp.EShop.Plugins.Inventories.DaprActors.AspNetCore\EasyAbp.EShop.Plugins.Inventories.DaprActors.AspNetCore.csproj", "{3F0EA314-CCF4-4BB2-A8C1-79FAE4442884}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Inventories.OrleansGrains", "plugins\Inventories\OrleansGrains\src\EasyAbp.EShop.Plugins.Inventories.OrleansGrains\EasyAbp.EShop.Plugins.Inventories.OrleansGrains.csproj", "{83F6434F-74DC-4389-870D-46510E28C029}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Inventories.OrleansGrains", "plugins\Inventories\OrleansGrains\src\EasyAbp.EShop.Plugins.Inventories.OrleansGrains\EasyAbp.EShop.Plugins.Inventories.OrleansGrains.csproj", "{83F6434F-74DC-4389-870D-46510E28C029}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OrleansGrains", "OrleansGrains", "{88D17635-75D7-48A1-B622-E6FB3DCACEF8}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8E978749-7972-4703-8A94-6A90080C78DE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Inventories.OrleansGrains.Abstractions", "plugins\Inventories\OrleansGrains\src\EasyAbp.EShop.Plugins.Inventories.OrleansGrains.Abstractions\EasyAbp.EShop.Plugins.Inventories.OrleansGrains.Abstractions.csproj", "{AB3477DB-3457-4167-A086-BAD104D69604}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Inventories.OrleansGrains.Abstractions", "plugins\Inventories\OrleansGrains\src\EasyAbp.EShop.Plugins.Inventories.OrleansGrains.Abstractions\EasyAbp.EShop.Plugins.Inventories.OrleansGrains.Abstractions.csproj", "{AB3477DB-3457-4167-A086-BAD104D69604}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Inventories.OrleansGrains.Silo", "plugins\Inventories\OrleansGrains\src\EasyAbp.EShop.Plugins.Inventories.OrleansGrains.Silo\EasyAbp.EShop.Plugins.Inventories.OrleansGrains.Silo.csproj", "{0D613460-A0AD-4EAF-B719-785FE65E97E8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Inventories.OrleansGrains.Silo", "plugins\Inventories\OrleansGrains\src\EasyAbp.EShop.Plugins.Inventories.OrleansGrains.Silo\EasyAbp.EShop.Plugins.Inventories.OrleansGrains.Silo.csproj", "{0D613460-A0AD-4EAF-B719-785FE65E97E8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Products.OrleansGrainsInventory.Domain", "plugins\Inventories\OrleansGrains\src\EasyAbp.EShop.Products.OrleansGrainsInventory.Domain\EasyAbp.EShop.Products.OrleansGrainsInventory.Domain.csproj", "{DB1C55BF-0C0D-488C-9AFC-992A3DED2EAD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Products.OrleansGrainsInventory.Domain", "plugins\Inventories\OrleansGrains\src\EasyAbp.EShop.Products.OrleansGrainsInventory.Domain\EasyAbp.EShop.Products.OrleansGrainsInventory.Domain.csproj", "{DB1C55BF-0C0D-488C-9AFC-992A3DED2EAD}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{F58B6EEF-5AFF-4B79-BC71-A2D8C71F5E77}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Products.OrleansGrainsInventory.Domain.Tests", "plugins\Inventories\OrleansGrains\test\EasyAbp.EShop.Products.OrleansGrainsInventory.Domain.Tests\EasyAbp.EShop.Products.OrleansGrainsInventory.Domain.Tests.csproj", "{D652EBF0-27CA-44C2-BB78-F446B87377C7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Products.OrleansGrainsInventory.Domain.Tests", "plugins\Inventories\OrleansGrains\test\EasyAbp.EShop.Products.OrleansGrainsInventory.Domain.Tests\EasyAbp.EShop.Products.OrleansGrainsInventory.Domain.Tests.csproj", "{D652EBF0-27CA-44C2-BB78-F446B87377C7}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Booking", "Booking", "{CE945F1D-6636-47D5-A619-C16C4E14CF8C}" EndProject @@ -417,6 +417,56 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Orders.Bookin EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Payments.Booking.Application", "plugins\Booking\src\EasyAbp.EShop.Payments.Booking.Application\EasyAbp.EShop.Payments.Booking.Application.csproj", "{4480BFAF-C981-4242-A509-EDA6F572E45C}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FlashSales", "FlashSales", "{867AD9F8-FD56-469D-A90D-C569EB9C3D2A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.FlashSales.Application", "plugins\FlashSales\src\EasyAbp.EShop.Plugins.FlashSales.Application\EasyAbp.EShop.Plugins.FlashSales.Application.csproj", "{26611C4C-6910-498A-9FBA-BECC09392ADE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.FlashSales.Application.Contracts", "plugins\FlashSales\src\EasyAbp.EShop.Plugins.FlashSales.Application.Contracts\EasyAbp.EShop.Plugins.FlashSales.Application.Contracts.csproj", "{FCD72398-F832-4914-8ACF-EA4C1DD24BBF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.FlashSales.Domain.Shared", "plugins\FlashSales\src\EasyAbp.EShop.Plugins.FlashSales.Domain.Shared\EasyAbp.EShop.Plugins.FlashSales.Domain.Shared.csproj", "{55145D4A-5A98-4B53-9293-CCB7236E69F5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.FlashSales.Domain", "plugins\FlashSales\src\EasyAbp.EShop.Plugins.FlashSales.Domain\EasyAbp.EShop.Plugins.FlashSales.Domain.csproj", "{7E535D1A-3259-4098-8E47-A81B3D8B3920}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore", "plugins\FlashSales\src\EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore\EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.csproj", "{2D1C0E98-D86A-4C60-98CE-86C4E5571B91}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.FlashSales.MongoDB", "plugins\FlashSales\src\EasyAbp.EShop.Plugins.FlashSales.MongoDB\EasyAbp.EShop.Plugins.FlashSales.MongoDB.csproj", "{B5ED3D8B-E2DC-4B38-A9C7-2A18B47385EE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.FlashSales.HttpApi", "plugins\FlashSales\src\EasyAbp.EShop.Plugins.FlashSales.HttpApi\EasyAbp.EShop.Plugins.FlashSales.HttpApi.csproj", "{0D5558BD-A22F-4565-A92A-BB14D517B1A5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F29C5BCD-E6C0-4556-A631-CACA41B1050B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{9C180C9E-50E9-4624-BE06-5C8C24A028E4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client", "plugins\FlashSales\src\EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client\EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.csproj", "{6658CE7D-CA7D-47A7-8B9E-6D8776CE607C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.FlashSales.Web", "plugins\FlashSales\src\EasyAbp.EShop.Plugins.FlashSales.Web\EasyAbp.EShop.Plugins.FlashSales.Web.csproj", "{6F06F221-1371-433A-8C90-3123B5EF32AD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.FlashSales.Application.Tests", "plugins\FlashSales\test\EasyAbp.EShop.Plugins.FlashSales.Application.Tests\EasyAbp.EShop.Plugins.FlashSales.Application.Tests.csproj", "{6EBE36C7-53DB-4194-BDDB-199F789F44E6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.FlashSales.Domain.Tests", "plugins\FlashSales\test\EasyAbp.EShop.Plugins.FlashSales.Domain.Tests\EasyAbp.EShop.Plugins.FlashSales.Domain.Tests.csproj", "{654C320E-78BD-4074-AC2D-154E95457BBC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests", "plugins\FlashSales\test\EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests\EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests.csproj", "{D2AAFF29-3F7B-4C90-AA99-AC686393B670}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp", "plugins\FlashSales\test\EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp\EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp.csproj", "{2291B087-FEC5-47BD-A7ED-04725D54E6A6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests", "plugins\FlashSales\test\EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests\EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests.csproj", "{68993D65-2C9C-438F-8B94-15E98C469A16}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.FlashSales.TestBase", "plugins\FlashSales\test\EasyAbp.EShop.Plugins.FlashSales.TestBase\EasyAbp.EShop.Plugins.FlashSales.TestBase.csproj", "{417AB8E2-1488-4814-9699-3B189D1ABA67}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Orders.Plugins.FlashSales.Application", "plugins\FlashSales\src\EasyAbp.EShop.Orders.Plugins.FlashSales.Application\EasyAbp.EShop.Orders.Plugins.FlashSales.Application.csproj", "{5732E880-CB72-49A0-AC4F-A0620F4E4D16}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Products.Plugins.FlashSales.Application", "plugins\FlashSales\src\EasyAbp.EShop.Products.Plugins.FlashSales.Application\EasyAbp.EShop.Products.Plugins.FlashSales.Application.csproj", "{6AD2F468-D86C-4F9A-B280-3BCC15661C47}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions", "plugins\FlashSales\src\EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions\EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions.csproj", "{C1EB9DC0-F572-41DF-9716-893B154BBB13}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts", "plugins\FlashSales\src\EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts\EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts.csproj", "{F08D9409-4D01-4639-A7B8-A70B7ED8E0F9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi", "plugins\FlashSales\src\EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi\EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.csproj", "{B137BF4B-8C0A-4CE2-AF22-BD9BD29C86B7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.Client", "plugins\FlashSales\src\EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.Client\EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.Client.csproj", "{274769DC-5DD6-4CFD-8078-5E0E0CE8D6D8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests", "plugins\FlashSales\test\EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests\EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests.csproj", "{17A3486C-1845-4B4E-B1A6-752106F0C309}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1083,6 +1133,94 @@ Global {4480BFAF-C981-4242-A509-EDA6F572E45C}.Debug|Any CPU.Build.0 = Debug|Any CPU {4480BFAF-C981-4242-A509-EDA6F572E45C}.Release|Any CPU.ActiveCfg = Release|Any CPU {4480BFAF-C981-4242-A509-EDA6F572E45C}.Release|Any CPU.Build.0 = Release|Any CPU + {26611C4C-6910-498A-9FBA-BECC09392ADE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {26611C4C-6910-498A-9FBA-BECC09392ADE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {26611C4C-6910-498A-9FBA-BECC09392ADE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {26611C4C-6910-498A-9FBA-BECC09392ADE}.Release|Any CPU.Build.0 = Release|Any CPU + {FCD72398-F832-4914-8ACF-EA4C1DD24BBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FCD72398-F832-4914-8ACF-EA4C1DD24BBF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FCD72398-F832-4914-8ACF-EA4C1DD24BBF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FCD72398-F832-4914-8ACF-EA4C1DD24BBF}.Release|Any CPU.Build.0 = Release|Any CPU + {55145D4A-5A98-4B53-9293-CCB7236E69F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55145D4A-5A98-4B53-9293-CCB7236E69F5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55145D4A-5A98-4B53-9293-CCB7236E69F5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55145D4A-5A98-4B53-9293-CCB7236E69F5}.Release|Any CPU.Build.0 = Release|Any CPU + {7E535D1A-3259-4098-8E47-A81B3D8B3920}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7E535D1A-3259-4098-8E47-A81B3D8B3920}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7E535D1A-3259-4098-8E47-A81B3D8B3920}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7E535D1A-3259-4098-8E47-A81B3D8B3920}.Release|Any CPU.Build.0 = Release|Any CPU + {2D1C0E98-D86A-4C60-98CE-86C4E5571B91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2D1C0E98-D86A-4C60-98CE-86C4E5571B91}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2D1C0E98-D86A-4C60-98CE-86C4E5571B91}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2D1C0E98-D86A-4C60-98CE-86C4E5571B91}.Release|Any CPU.Build.0 = Release|Any CPU + {B5ED3D8B-E2DC-4B38-A9C7-2A18B47385EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B5ED3D8B-E2DC-4B38-A9C7-2A18B47385EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B5ED3D8B-E2DC-4B38-A9C7-2A18B47385EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B5ED3D8B-E2DC-4B38-A9C7-2A18B47385EE}.Release|Any CPU.Build.0 = Release|Any CPU + {0D5558BD-A22F-4565-A92A-BB14D517B1A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0D5558BD-A22F-4565-A92A-BB14D517B1A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0D5558BD-A22F-4565-A92A-BB14D517B1A5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0D5558BD-A22F-4565-A92A-BB14D517B1A5}.Release|Any CPU.Build.0 = Release|Any CPU + {6658CE7D-CA7D-47A7-8B9E-6D8776CE607C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6658CE7D-CA7D-47A7-8B9E-6D8776CE607C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6658CE7D-CA7D-47A7-8B9E-6D8776CE607C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6658CE7D-CA7D-47A7-8B9E-6D8776CE607C}.Release|Any CPU.Build.0 = Release|Any CPU + {6F06F221-1371-433A-8C90-3123B5EF32AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6F06F221-1371-433A-8C90-3123B5EF32AD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6F06F221-1371-433A-8C90-3123B5EF32AD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6F06F221-1371-433A-8C90-3123B5EF32AD}.Release|Any CPU.Build.0 = Release|Any CPU + {6EBE36C7-53DB-4194-BDDB-199F789F44E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6EBE36C7-53DB-4194-BDDB-199F789F44E6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6EBE36C7-53DB-4194-BDDB-199F789F44E6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6EBE36C7-53DB-4194-BDDB-199F789F44E6}.Release|Any CPU.Build.0 = Release|Any CPU + {654C320E-78BD-4074-AC2D-154E95457BBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {654C320E-78BD-4074-AC2D-154E95457BBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {654C320E-78BD-4074-AC2D-154E95457BBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {654C320E-78BD-4074-AC2D-154E95457BBC}.Release|Any CPU.Build.0 = Release|Any CPU + {D2AAFF29-3F7B-4C90-AA99-AC686393B670}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D2AAFF29-3F7B-4C90-AA99-AC686393B670}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D2AAFF29-3F7B-4C90-AA99-AC686393B670}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D2AAFF29-3F7B-4C90-AA99-AC686393B670}.Release|Any CPU.Build.0 = Release|Any CPU + {2291B087-FEC5-47BD-A7ED-04725D54E6A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2291B087-FEC5-47BD-A7ED-04725D54E6A6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2291B087-FEC5-47BD-A7ED-04725D54E6A6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2291B087-FEC5-47BD-A7ED-04725D54E6A6}.Release|Any CPU.Build.0 = Release|Any CPU + {68993D65-2C9C-438F-8B94-15E98C469A16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68993D65-2C9C-438F-8B94-15E98C469A16}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68993D65-2C9C-438F-8B94-15E98C469A16}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68993D65-2C9C-438F-8B94-15E98C469A16}.Release|Any CPU.Build.0 = Release|Any CPU + {417AB8E2-1488-4814-9699-3B189D1ABA67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {417AB8E2-1488-4814-9699-3B189D1ABA67}.Debug|Any CPU.Build.0 = Debug|Any CPU + {417AB8E2-1488-4814-9699-3B189D1ABA67}.Release|Any CPU.ActiveCfg = Release|Any CPU + {417AB8E2-1488-4814-9699-3B189D1ABA67}.Release|Any CPU.Build.0 = Release|Any CPU + {5732E880-CB72-49A0-AC4F-A0620F4E4D16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5732E880-CB72-49A0-AC4F-A0620F4E4D16}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5732E880-CB72-49A0-AC4F-A0620F4E4D16}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5732E880-CB72-49A0-AC4F-A0620F4E4D16}.Release|Any CPU.Build.0 = Release|Any CPU + {6AD2F468-D86C-4F9A-B280-3BCC15661C47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6AD2F468-D86C-4F9A-B280-3BCC15661C47}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6AD2F468-D86C-4F9A-B280-3BCC15661C47}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6AD2F468-D86C-4F9A-B280-3BCC15661C47}.Release|Any CPU.Build.0 = Release|Any CPU + {C1EB9DC0-F572-41DF-9716-893B154BBB13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C1EB9DC0-F572-41DF-9716-893B154BBB13}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C1EB9DC0-F572-41DF-9716-893B154BBB13}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C1EB9DC0-F572-41DF-9716-893B154BBB13}.Release|Any CPU.Build.0 = Release|Any CPU + {F08D9409-4D01-4639-A7B8-A70B7ED8E0F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F08D9409-4D01-4639-A7B8-A70B7ED8E0F9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F08D9409-4D01-4639-A7B8-A70B7ED8E0F9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F08D9409-4D01-4639-A7B8-A70B7ED8E0F9}.Release|Any CPU.Build.0 = Release|Any CPU + {B137BF4B-8C0A-4CE2-AF22-BD9BD29C86B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B137BF4B-8C0A-4CE2-AF22-BD9BD29C86B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B137BF4B-8C0A-4CE2-AF22-BD9BD29C86B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B137BF4B-8C0A-4CE2-AF22-BD9BD29C86B7}.Release|Any CPU.Build.0 = Release|Any CPU + {274769DC-5DD6-4CFD-8078-5E0E0CE8D6D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {274769DC-5DD6-4CFD-8078-5E0E0CE8D6D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {274769DC-5DD6-4CFD-8078-5E0E0CE8D6D8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {274769DC-5DD6-4CFD-8078-5E0E0CE8D6D8}.Release|Any CPU.Build.0 = Release|Any CPU + {17A3486C-1845-4B4E-B1A6-752106F0C309}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {17A3486C-1845-4B4E-B1A6-752106F0C309}.Debug|Any CPU.Build.0 = Debug|Any CPU + {17A3486C-1845-4B4E-B1A6-752106F0C309}.Release|Any CPU.ActiveCfg = Release|Any CPU + {17A3486C-1845-4B4E-B1A6-752106F0C309}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1255,16 +1393,16 @@ Global {3C385657-8365-470F-9F4F-30F31F9FCA42} = {72F34527-9295-4F29-923E-4B075A4F31A2} {9AC27747-E175-487F-92C9-434DEE543273} = {94CC5A11-DA0F-413C-96CA-01DB0FC426E0} {6E6FE4B9-4117-4F57-B219-EE47E4046096} = {9AC27747-E175-487F-92C9-434DEE543273} - {F468A386-5660-4888-981A-6ECF15182D32} = {6E6FE4B9-4117-4F57-B219-EE47E4046096} - {B6F3ACD5-463E-4455-A094-057A82941A94} = {F468A386-5660-4888-981A-6ECF15182D32} - {CF4DE32D-9629-4C48-9BE8-5B83A1C27291} = {F468A386-5660-4888-981A-6ECF15182D32} {6CD1A8B5-8AB7-4A31-8333-024A7FB602D1} = {F468A386-5660-4888-981A-6ECF15182D32} + {CF4DE32D-9629-4C48-9BE8-5B83A1C27291} = {F468A386-5660-4888-981A-6ECF15182D32} + {B6F3ACD5-463E-4455-A094-057A82941A94} = {F468A386-5660-4888-981A-6ECF15182D32} + {F468A386-5660-4888-981A-6ECF15182D32} = {6E6FE4B9-4117-4F57-B219-EE47E4046096} {485204B1-7603-4EA0-B3A4-73CB89B0D5BC} = {6E6FE4B9-4117-4F57-B219-EE47E4046096} {733C51A3-19C8-45C4-8B22-3FD40CAF4EFB} = {485204B1-7603-4EA0-B3A4-73CB89B0D5BC} {3F0EA314-CCF4-4BB2-A8C1-79FAE4442884} = {F468A386-5660-4888-981A-6ECF15182D32} + {83F6434F-74DC-4389-870D-46510E28C029} = {8E978749-7972-4703-8A94-6A90080C78DE} {88D17635-75D7-48A1-B622-E6FB3DCACEF8} = {9AC27747-E175-487F-92C9-434DEE543273} {8E978749-7972-4703-8A94-6A90080C78DE} = {88D17635-75D7-48A1-B622-E6FB3DCACEF8} - {83F6434F-74DC-4389-870D-46510E28C029} = {8E978749-7972-4703-8A94-6A90080C78DE} {AB3477DB-3457-4167-A086-BAD104D69604} = {8E978749-7972-4703-8A94-6A90080C78DE} {0D613460-A0AD-4EAF-B719-785FE65E97E8} = {8E978749-7972-4703-8A94-6A90080C78DE} {DB1C55BF-0C0D-488C-9AFC-992A3DED2EAD} = {8E978749-7972-4703-8A94-6A90080C78DE} @@ -1291,6 +1429,31 @@ Global {739CAE6A-14E6-44FC-8863-DA905CBD289F} = {7087FDFF-196A-4C9F-8C66-EEBC2C49F2F8} {C0399352-1278-4D91-8D4E-7491FD77C18B} = {A8C4583C-034E-47AF-B7EC-1A34EE288E2F} {4480BFAF-C981-4242-A509-EDA6F572E45C} = {A8C4583C-034E-47AF-B7EC-1A34EE288E2F} + {867AD9F8-FD56-469D-A90D-C569EB9C3D2A} = {94CC5A11-DA0F-413C-96CA-01DB0FC426E0} + {26611C4C-6910-498A-9FBA-BECC09392ADE} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B} + {FCD72398-F832-4914-8ACF-EA4C1DD24BBF} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B} + {55145D4A-5A98-4B53-9293-CCB7236E69F5} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B} + {7E535D1A-3259-4098-8E47-A81B3D8B3920} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B} + {2D1C0E98-D86A-4C60-98CE-86C4E5571B91} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B} + {B5ED3D8B-E2DC-4B38-A9C7-2A18B47385EE} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B} + {0D5558BD-A22F-4565-A92A-BB14D517B1A5} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B} + {F29C5BCD-E6C0-4556-A631-CACA41B1050B} = {867AD9F8-FD56-469D-A90D-C569EB9C3D2A} + {9C180C9E-50E9-4624-BE06-5C8C24A028E4} = {867AD9F8-FD56-469D-A90D-C569EB9C3D2A} + {6658CE7D-CA7D-47A7-8B9E-6D8776CE607C} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B} + {6F06F221-1371-433A-8C90-3123B5EF32AD} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B} + {6EBE36C7-53DB-4194-BDDB-199F789F44E6} = {9C180C9E-50E9-4624-BE06-5C8C24A028E4} + {654C320E-78BD-4074-AC2D-154E95457BBC} = {9C180C9E-50E9-4624-BE06-5C8C24A028E4} + {D2AAFF29-3F7B-4C90-AA99-AC686393B670} = {9C180C9E-50E9-4624-BE06-5C8C24A028E4} + {2291B087-FEC5-47BD-A7ED-04725D54E6A6} = {9C180C9E-50E9-4624-BE06-5C8C24A028E4} + {68993D65-2C9C-438F-8B94-15E98C469A16} = {9C180C9E-50E9-4624-BE06-5C8C24A028E4} + {417AB8E2-1488-4814-9699-3B189D1ABA67} = {9C180C9E-50E9-4624-BE06-5C8C24A028E4} + {5732E880-CB72-49A0-AC4F-A0620F4E4D16} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B} + {6AD2F468-D86C-4F9A-B280-3BCC15661C47} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B} + {C1EB9DC0-F572-41DF-9716-893B154BBB13} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B} + {F08D9409-4D01-4639-A7B8-A70B7ED8E0F9} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B} + {B137BF4B-8C0A-4CE2-AF22-BD9BD29C86B7} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B} + {274769DC-5DD6-4CFD-8078-5E0E0CE8D6D8} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B} + {17A3486C-1845-4B4E-B1A6-752106F0C309} = {9C180C9E-50E9-4624-BE06-5C8C24A028E4} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {28315BFD-90E7-4E14-A2EA-F3D23AF4126F} diff --git a/docs/plugins/flash-sales/README.md b/docs/plugins/flash-sales/README.md new file mode 100644 index 00000000..f29c46e7 --- /dev/null +++ b/docs/plugins/flash-sales/README.md @@ -0,0 +1,46 @@ +# EShop.Plugins.FlashSales + +[![ABP version](https://img.shields.io/badge/dynamic/xml?style=flat-square&color=yellow&label=abp&query=%2F%2FProject%2FPropertyGroup%2FAbpVersion&url=https%3A%2F%2Fraw.githubusercontent.com%2FEasyAbp%2FEShop%2Fmaster%2FDirectory.Build.props)](https://abp.io) +[![NuGet](https://img.shields.io/nuget/v/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared.svg?style=flat-square)](https://www.nuget.org/packages/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared) +[![NuGet Download](https://img.shields.io/nuget/dt/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared.svg?style=flat-square)](https://www.nuget.org/packages/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared) +[![Discord online](https://badgen.net/discord/online-members/S6QaezrCRq?label=Discord)](https://discord.gg/S6QaezrCRq) +[![GitHub stars](https://img.shields.io/github/stars/EasyAbp/EShop?style=social)](https://www.github.com/EasyAbp/EShop) + +A flash-sales plugin for EShop. + +## Installation + +1. Install the following NuGet packages. ([see how](https://github.com/EasyAbp/EasyAbpGuide/blob/master/docs/How-To.md#add-nuget-packages)) + + - EasyAbp.EShop.Orders.Plugins.FlashSales.Application (install at EasyAbp.EShop.Orders.Application location) + - EasyAbp.EShop.Products.Plugins.FlashSales.Application (install at EasyAbp.EShop.Products.Application location) + - EasyAbp.EShop.Plugins.FlashSales.Application + - EasyAbp.EShop.Plugins.FlashSales.Application.Contracts + - EasyAbp.EShop.Plugins.FlashSales.Domain + - EasyAbp.EShop.Plugins.FlashSales.Domain.Shared + - EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore + - EasyAbp.EShop.Plugins.FlashSales.HttpApi + - EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client + - (Optional) EasyAbp.EShop.Plugins.FlashSales.MongoDB + - (Optional) EasyAbp.EShop.Plugins.FlashSales.Web + - (Optional) EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts (install at EasyAbp.EShop.Products.Application.Contracts location) + - (Optional) EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi (install at EasyAbp.EShop.Products.HttpApi location) + - (Optional) EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.Client (install at EasyAbp.EShop.Products.HttpApi.Client location) + +2. Add `DependsOn(typeof(EShopXxxModule))` attribute to configure the module dependencies. ([see how](https://github.com/EasyAbp/EasyAbpGuide/blob/master/docs/How-To.md#add-module-dependencies)) + +3. Add `builder.ConfigureEShopPluginsFlashSales();` to the `OnModelCreating()` method in **MyProjectDbContext.cs**. + +4. Add EF Core migrations and update your database. See: [ABP document](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=MVC&DB=EF#add-database-migration). + +## Usage + +### Admins + +1. Create a published flash-sale plan. + +### Customers + +1. Use `/api/e-shop/plugins/flash-sales/flash-sale-plan/{planId}/pre-order` (POST) to pre-order. It will return an expiration time if your pre-order request succeeds. You should re-invoke this API to refresh your request before the expiration time. +2. When the flash sale starts, use `/api/e-shop/plugins/flash-sales/flash-sale-plan/{planId}/order` (POST) to create your order. If you are fast enough, it will occupy the inventory and create an order for you in the background. +3. If you are told that you have succeeded, continuous use `/api/e-shop/plugins/flash-sales/flash-sale-result/{resultId}` (GET) to query the order creation result until it succeeds or fails. diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp.EShop.Products.Domain.csproj b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp.EShop.Products.Domain.csproj index 2dab26aa..663bb9c9 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp.EShop.Products.Domain.csproj +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp.EShop.Products.Domain.csproj @@ -8,6 +8,7 @@ + diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/DefaultProductInventoryProvider.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/DefaultProductInventoryProvider.cs index 613a434c..9f420ea2 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/DefaultProductInventoryProvider.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/DefaultProductInventoryProvider.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using EasyAbp.EShop.Products.ProductInventories; +using Microsoft.Extensions.Logging; using Volo.Abp.DependencyInjection; +using Volo.Abp.DistributedLocking; using Volo.Abp.EventBus.Distributed; using Volo.Abp.Guids; using Volo.Abp.MultiTenancy; @@ -13,6 +15,14 @@ namespace EasyAbp.EShop.Products.Products { public class DefaultProductInventoryProvider : IProductInventoryProvider, ITransientDependency { + /// + /// The lock key format. + /// {0}: Tenant ID + /// {1}: Product ID + /// {2}: ProductSku ID + /// + public const string DefaultProductInventoryLockKeyFormat = "eshop-product-inventory-{0}-{1}-{2}"; + public static string DefaultProductInventoryProviderName { get; set; } = "Default"; public static string DefaultProductInventoryProviderDisplayName { get; set; } = "Default"; public static string DefaultProductInventoryProviderDescription { get; set; } = "Default"; @@ -24,17 +34,23 @@ namespace EasyAbp.EShop.Products.Products private readonly ICurrentTenant _currentTenant; private readonly IDistributedEventBus _distributedEventBus; private readonly IProductInventoryRepository _productInventoryRepository; + private readonly IAbpDistributedLock _distributedLock; + private readonly ILogger _logger; public DefaultProductInventoryProvider( IGuidGenerator guidGenerator, ICurrentTenant currentTenant, IDistributedEventBus distributedEventBus, - IProductInventoryRepository productInventoryRepository) + IProductInventoryRepository productInventoryRepository, + IAbpDistributedLock distributedLock, + ILogger logger) { _guidGenerator = guidGenerator; _currentTenant = currentTenant; _distributedEventBus = distributedEventBus; _productInventoryRepository = productInventoryRepository; + _distributedLock = distributedLock; + _logger = logger; } [UnitOfWork] @@ -62,6 +78,15 @@ namespace EasyAbp.EShop.Products.Products public virtual async Task TryIncreaseInventoryAsync(InventoryQueryModel model, int quantity, bool decreaseSold) { + await using var handle = await _distributedLock.TryAcquireAsync(await GetLockKeyAsync(model), TimeSpan.FromSeconds(30)); + + if (handle == null) + { + _logger.LogWarning("TryIncreaseInventory failed to acquire lock for product inventory: {TenantId},{ProductId},{ProductSkuId}", + model.TenantId, model.ProductId, model.ProductSkuId); + return false; + } + var productInventory = await GetOrCreateProductInventoryAsync(model.ProductId, model.ProductSkuId); return await TryIncreaseInventoryAsync(model, productInventory, quantity, decreaseSold); @@ -71,6 +96,15 @@ namespace EasyAbp.EShop.Products.Products public virtual async Task TryReduceInventoryAsync(InventoryQueryModel model, int quantity, bool increaseSold) { + await using var handle = await _distributedLock.TryAcquireAsync(await GetLockKeyAsync(model), TimeSpan.FromSeconds(30)); + + if (handle == null) + { + _logger.LogWarning("TryReduceInventory failed to acquire lock for product inventory: {TenantId},{ProductId},{ProductSkuId}", + model.TenantId, model.ProductId, model.ProductSkuId); + return false; + } + var productInventory = await GetOrCreateProductInventoryAsync(model.ProductId, model.ProductSkuId); return await TryReduceInventoryAsync(model, productInventory, quantity, increaseSold); @@ -158,5 +192,10 @@ namespace EasyAbp.EShop.Products.Products newInventory, sold)); } + + protected virtual Task GetLockKeyAsync(InventoryQueryModel model) + { + return Task.FromResult(string.Format(DefaultProductInventoryLockKeyFormat, model.TenantId, model.ProductId, model.ProductSkuId)); + } } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductInventoryProviderResolver.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductInventoryProviderResolver.cs index 1fd1daca..09ea7ab9 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductInventoryProviderResolver.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductInventoryProviderResolver.cs @@ -9,4 +9,6 @@ public interface IProductInventoryProviderResolver Task ExistProviderAsync([NotNull] string providerName); Task GetAsync(Product product); + + Task GetAsync([NotNull] string providerName); } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductInventoryProviderResolver.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductInventoryProviderResolver.cs index bd1e4472..77199c85 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductInventoryProviderResolver.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductInventoryProviderResolver.cs @@ -7,6 +7,7 @@ using EasyAbp.EShop.Products.ProductInventories; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Volo.Abp; using Volo.Abp.DependencyInjection; namespace EasyAbp.EShop.Products.Products; @@ -47,6 +48,11 @@ public class ProductInventoryProviderResolver : IProductInventoryProviderResolve return Task.FromResult(GetProviderByName(options.Value.DefaultInventoryProviderName)); } + public virtual Task GetAsync([CanBeNull] string providerName) + { + return Task.FromResult(GetProviderByName(providerName)); + } + protected virtual IProductInventoryProvider GetProviderByName([CanBeNull] string providerName) { if (providerName.IsNullOrEmpty()) @@ -67,7 +73,7 @@ public class ProductInventoryProviderResolver : IProductInventoryProviderResolve { return; } - + var options = ServiceProvider.GetRequiredService>().Value; foreach (var pair in options.InventoryProviders.GetConfigurationsDictionary()) diff --git a/plugins/FlashSales/.editorconfig b/plugins/FlashSales/.editorconfig new file mode 100644 index 00000000..6f8b7846 --- /dev/null +++ b/plugins/FlashSales/.editorconfig @@ -0,0 +1,223 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = true +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = omit_if_default + +# Expression-level preferences +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Field preferences +dotnet_style_readonly_field = true + +# Parameter preferences +dotnet_code_quality_unused_parameters = all + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = 0 + +# New line preferences +dotnet_style_allow_multiple_blank_lines_experimental = false +dotnet_style_allow_statement_immediately_after_block_experimental = false + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = true +csharp_style_var_for_built_in_types = true +csharp_style_var_when_type_is_apparent = true + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = true + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Null-checking preferences +csharp_style_conditional_delegate_call = true +csharp_style_prefer_parameter_null_checking = true + +# Modifier preferences +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async + +# Code-block preferences +csharp_prefer_braces = true +csharp_prefer_simple_using_statement = true +csharp_style_namespace_declarations = file_scoped +csharp_style_prefer_method_group_conversion = true + +# Expression-level preferences +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true +csharp_style_prefer_tuple_swap = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:suggestion + +# New line preferences +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case diff --git a/plugins/FlashSales/host/EasyAbp.EShop.Plugins.FlashSales.Host.Shared/EasyAbp.EShop.Plugins.FlashSales.Host.Shared.abppkg.json b/plugins/FlashSales/host/EasyAbp.EShop.Plugins.FlashSales.Host.Shared/EasyAbp.EShop.Plugins.FlashSales.Host.Shared.abppkg.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/plugins/FlashSales/host/EasyAbp.EShop.Plugins.FlashSales.Host.Shared/EasyAbp.EShop.Plugins.FlashSales.Host.Shared.abppkg.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/plugins/FlashSales/host/EasyAbp.EShop.Plugins.FlashSales.Host.Shared/EasyAbp.EShop.Plugins.FlashSales.Host.Shared.csproj b/plugins/FlashSales/host/EasyAbp.EShop.Plugins.FlashSales.Host.Shared/EasyAbp.EShop.Plugins.FlashSales.Host.Shared.csproj new file mode 100644 index 00000000..1669f598 --- /dev/null +++ b/plugins/FlashSales/host/EasyAbp.EShop.Plugins.FlashSales.Host.Shared/EasyAbp.EShop.Plugins.FlashSales.Host.Shared.csproj @@ -0,0 +1,10 @@ + + + + + + netstandard2.0 + + + + diff --git a/plugins/FlashSales/host/EasyAbp.EShop.Plugins.FlashSales.Host.Shared/EasyAbp/EShop/Plugins/FlashSales/MultiTenancy/MultiTenancyConsts.cs b/plugins/FlashSales/host/EasyAbp.EShop.Plugins.FlashSales.Host.Shared/EasyAbp/EShop/Plugins/FlashSales/MultiTenancy/MultiTenancyConsts.cs new file mode 100644 index 00000000..6d205dd3 --- /dev/null +++ b/plugins/FlashSales/host/EasyAbp.EShop.Plugins.FlashSales.Host.Shared/EasyAbp/EShop/Plugins/FlashSales/MultiTenancy/MultiTenancyConsts.cs @@ -0,0 +1,9 @@ +namespace EasyAbp.EShop.Plugins.FlashSales.MultiTenancy; + +public static class MultiTenancyConsts +{ + /* Enable/disable multi-tenancy in a single point + * to test your module with multi-tenancy. + */ + public const bool IsEnabled = false; +} diff --git a/plugins/FlashSales/host/EasyAbp.EShop.Plugins.FlashSales.Host.Shared/FodyWeavers.xml b/plugins/FlashSales/host/EasyAbp.EShop.Plugins.FlashSales.Host.Shared/FodyWeavers.xml new file mode 100644 index 00000000..1715698c --- /dev/null +++ b/plugins/FlashSales/host/EasyAbp.EShop.Plugins.FlashSales.Host.Shared/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/host/EasyAbp.EShop.Plugins.FlashSales.Host.Shared/FodyWeavers.xsd b/plugins/FlashSales/host/EasyAbp.EShop.Plugins.FlashSales.Host.Shared/FodyWeavers.xsd new file mode 100644 index 00000000..ffa6fc4b --- /dev/null +++ b/plugins/FlashSales/host/EasyAbp.EShop.Plugins.FlashSales.Host.Shared/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.csproj b/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.csproj new file mode 100644 index 00000000..5455d3fd --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.csproj @@ -0,0 +1,15 @@ + + + + + + net6.0 + + + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Orders/CreateFlashSaleOrderEventHandler.cs b/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Orders/CreateFlashSaleOrderEventHandler.cs new file mode 100644 index 00000000..57d85ec1 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Orders/CreateFlashSaleOrderEventHandler.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using EasyAbp.EShop.Orders.Orders.Dtos; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; +using EasyAbp.EShop.Products.ProductDetails; +using EasyAbp.EShop.Products.ProductDetails.Dtos; +using EasyAbp.EShop.Products.Products; +using EasyAbp.EShop.Products.Products.Dtos; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.ObjectMapping; +using Volo.Abp.Uow; + +namespace EasyAbp.EShop.Orders.Orders; + +public class CreateFlashSaleOrderEventHandler : IDistributedEventHandler, ITransientDependency +{ + protected INewOrderGenerator NewOrderGenerator { get; } + + protected IObjectMapper ObjectMapper { get; } + + protected IEnumerable OrderDiscountProviders { get; } + + protected IOrderRepository OrderRepository { get; } + + protected IDistributedEventBus DistributedEventBus { get; } + + protected IProductAppService ProductAppService { get; } + + protected IProductDetailAppService ProductDetailAppService { get; } + + protected IFlashSalePlanHasher FlashSalePlanHasher { get; } + + public CreateFlashSaleOrderEventHandler( + INewOrderGenerator newOrderGenerator, + IObjectMapper objectMapper, + IEnumerable orderDiscountProviders, + IOrderRepository orderRepository, + IDistributedEventBus distributedEventBus, + IProductAppService productAppService, + IProductDetailAppService productDetailAppService, + IFlashSalePlanHasher flashSalePlanHasher) + { + NewOrderGenerator = newOrderGenerator; + ObjectMapper = objectMapper; + OrderDiscountProviders = orderDiscountProviders; + OrderRepository = orderRepository; + DistributedEventBus = distributedEventBus; + ProductAppService = productAppService; + ProductDetailAppService = productDetailAppService; + FlashSalePlanHasher = flashSalePlanHasher; + } + + [UnitOfWork(true)] + public virtual async Task HandleEventAsync(CreateFlashSaleOrderEto eventData) + { + var product = await ProductAppService.GetAsync(eventData.Plan.ProductId); + var productSku = product.GetSkuById(eventData.Plan.ProductSkuId); + + if (!await ValidateHashTokenAsync(eventData.Plan, product, productSku, eventData.HashToken)) + { + await DistributedEventBus.PublishAsync(new CreateFlashSaleOrderCompleteEto() + { + TenantId = eventData.TenantId, + PlanId = eventData.PlanId, + OrderId = null, + UserId = eventData.UserId, + StoreId = eventData.StoreId, + PendingResultId = eventData.PendingResultId, + Success = false, + Reason = FlashSaleResultFailedReason.InvalidHashToken + }); + return; + } + + var input = await ConvertToCreateOrderDtoAsync(eventData); + + var productDict = await GetProductDictionaryAsync(product); + + var productDetailDict = await GetProductDetailDictionaryAsync(product, productSku); + + var order = await NewOrderGenerator.GenerateAsync(eventData.UserId, input, productDict, productDetailDict); + + await OrderRepository.InsertAsync(order, autoSave: true); + + await DistributedEventBus.PublishAsync(new CreateFlashSaleOrderCompleteEto() + { + TenantId = eventData.TenantId, + PlanId = eventData.PlanId, + OrderId = order.Id, + UserId = eventData.UserId, + StoreId = eventData.StoreId, + PendingResultId = eventData.PendingResultId, + Success = true, + Reason = null + }); + } + + protected virtual Task> GetProductDictionaryAsync(ProductDto product) + { + var productDict = new Dictionary() + { + {product.Id, product} + }; + + return Task.FromResult(productDict); + } + + protected virtual async Task> GetProductDetailDictionaryAsync(ProductDto product, ProductSkuDto productSku) + { + var dict = new Dictionary(); + + var productDetailId = productSku.ProductDetailId ?? product.ProductDetailId; + + if (productDetailId.HasValue) + { + dict.Add(productDetailId.Value, await ProductDetailAppService.GetAsync(productDetailId.Value)); + } + + return dict; + } + + protected virtual Task ConvertToCreateOrderDtoAsync(CreateFlashSaleOrderEto eventData) + { + var input = new CreateOrderDto() + { + StoreId = eventData.StoreId, + CustomerRemark = eventData.CustomerRemark, + OrderLines = new List() + { + new CreateOrderLineDto() + { + ProductId = eventData.Plan.ProductId, + ProductSkuId = eventData.Plan.ProductSkuId, + Quantity = 1 + } + } + }; + + return Task.FromResult(input); + } + + protected virtual async Task ValidateHashTokenAsync(FlashSalePlanEto plan, ProductDto product, ProductSkuDto productSku, string originHashToken) + { + var hashToken = await FlashSalePlanHasher.HashAsync(plan.LastModificationTime, product.LastModificationTime, productSku.LastModificationTime); + + return string.Equals(hashToken, originHashToken, StringComparison.InvariantCulture); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationModule.cs new file mode 100644 index 00000000..6086e4d7 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationModule.cs @@ -0,0 +1,24 @@ +using EasyAbp.EShop.Plugins.FlashSales; +using EasyAbp.EShop.Products; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AutoMapper; +using Volo.Abp.Modularity; + +namespace EasyAbp.EShop.Orders.Plugins.FlashSales; + +[DependsOn( + typeof(EShopOrdersApplicationModule), + typeof(EShopProductsApplicationContractsModule), + typeof(EShopPluginsFlashSalesApplicationContractsModule) +)] +public class EShopOrdersPluginsFlashSalesApplicationModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAutoMapperObjectMapper(); + Configure(options => + { + options.AddMaps(validate: true); + }); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/FodyWeavers.xml b/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/FodyWeavers.xml new file mode 100644 index 00000000..be0de3a9 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/FodyWeavers.xsd b/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/FodyWeavers.xsd new file mode 100644 index 00000000..3f3946e2 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts.abppkg.json b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts.abppkg.json new file mode 100644 index 00000000..49032794 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts.abppkg.json @@ -0,0 +1,3 @@ +{ + "role": "lib.application-contracts" +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts.csproj b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts.csproj new file mode 100644 index 00000000..b29cdfb9 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts.csproj @@ -0,0 +1,16 @@ + + + + + + netstandard2.0 + + + + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesApplicationContractsModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesApplicationContractsModule.cs new file mode 100644 index 00000000..bfdd3261 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesApplicationContractsModule.cs @@ -0,0 +1,15 @@ +using Volo.Abp.Application; +using Volo.Abp.Modularity; +using Volo.Abp.Authorization; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +[DependsOn( + typeof(EShopPluginsFlashSalesDomainSharedModule), + typeof(AbpDddApplicationContractsModule), + typeof(AbpAuthorizationModule) + )] +public class EShopPluginsFlashSalesApplicationContractsModule : AbpModule +{ + +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesRemoteServiceConsts.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesRemoteServiceConsts.cs new file mode 100644 index 00000000..0250ea15 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesRemoteServiceConsts.cs @@ -0,0 +1,8 @@ +namespace EasyAbp.EShop.Plugins.FlashSales; + +public class EShopPluginsFlashSalesRemoteServiceConsts +{ + public const string RemoteServiceName = "EasyAbpEShopPluginsFlashSales"; + + public const string ModuleName = "easyAbpEShopPluginsFlashSales"; +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/CreateOrderInput.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/CreateOrderInput.cs new file mode 100644 index 00000000..a9661459 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/CreateOrderInput.cs @@ -0,0 +1,8 @@ +using Volo.Abp.Application.Dtos; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.Dtos; + +public class CreateOrderInput : ExtensibleEntityDto +{ + public string CustomerRemark { get; set; } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/FlashSaleOrderResultDto.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/FlashSaleOrderResultDto.cs new file mode 100644 index 00000000..adeec222 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/FlashSaleOrderResultDto.cs @@ -0,0 +1,11 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.Dtos; + +public class FlashSaleOrderResultDto : ExtensibleEntityDto +{ + public bool IsSuccess { get; set; } + + public Guid? FlashSaleResultId { get; set; } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/FlashSalePlanCreateDto.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/FlashSalePlanCreateDto.cs new file mode 100644 index 00000000..40b5d80c --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/FlashSalePlanCreateDto.cs @@ -0,0 +1,21 @@ +using System; +using EasyAbp.EShop.Stores.Stores; +using Volo.Abp.Application.Dtos; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.Dtos; + +[Serializable] +public class FlashSalePlanCreateDto : ExtensibleEntityDto, IMultiStore +{ + public Guid StoreId { get; set; } + + public DateTime BeginTime { get; set; } + + public DateTime EndTime { get; set; } + + public Guid ProductId { get; set; } + + public Guid ProductSkuId { get; set; } + + public bool IsPublished { get; set; } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/FlashSalePlanDto.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/FlashSalePlanDto.cs new file mode 100644 index 00000000..5e16106e --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/FlashSalePlanDto.cs @@ -0,0 +1,23 @@ +using System; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Domain.Entities; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.Dtos; + +[Serializable] +public class FlashSalePlanDto : ExtensibleFullAuditedEntityDto, IHasConcurrencyStamp +{ + public Guid StoreId { get; set; } + + public DateTime BeginTime { get; set; } + + public DateTime EndTime { get; set; } + + public Guid ProductId { get; set; } + + public Guid ProductSkuId { get; set; } + + public bool IsPublished { get; set; } + + public string ConcurrencyStamp { get; set; } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/FlashSalePlanGetListInput.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/FlashSalePlanGetListInput.cs new file mode 100644 index 00000000..9192589a --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/FlashSalePlanGetListInput.cs @@ -0,0 +1,20 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.Dtos; + +[Serializable] +public class FlashSalePlanGetListInput : ExtensiblePagedAndSortedResultRequestDto +{ + public Guid? StoreId { get; set; } + + public Guid? ProductId { get; set; } + + public Guid? ProductSkuId { get; set; } + + public bool IncludeUnpublished { get; set; } + + public DateTime? Start { get; set; } + + public DateTime? End { get; set; } +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/FlashSalePlanPreOrderDto.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/FlashSalePlanPreOrderDto.cs new file mode 100644 index 00000000..ccfdea14 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/FlashSalePlanPreOrderDto.cs @@ -0,0 +1,11 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.Dtos; + +public class FlashSalePlanPreOrderDto : ExtensibleEntityDto +{ + public DateTime ExpiresTime { get; set; } + + public double ExpiresInSeconds { get; set; } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/FlashSalePlanUpdateDto.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/FlashSalePlanUpdateDto.cs new file mode 100644 index 00000000..0a4075f3 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/Dtos/FlashSalePlanUpdateDto.cs @@ -0,0 +1,21 @@ +using System; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Domain.Entities; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.Dtos; + +[Serializable] +public class FlashSalePlanUpdateDto : ExtensibleEntityDto, IHasConcurrencyStamp +{ + public DateTime BeginTime { get; set; } + + public DateTime EndTime { get; set; } + + public Guid ProductId { get; set; } + + public Guid ProductSkuId { get; set; } + + public bool IsPublished { get; set; } + + public string ConcurrencyStamp { get; set; } +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/IFlashSalePlanAppService.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/IFlashSalePlanAppService.cs new file mode 100644 index 00000000..24cdd447 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/IFlashSalePlanAppService.cs @@ -0,0 +1,19 @@ +using System; +using System.Threading.Tasks; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.Dtos; +using Volo.Abp.Application.Services; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +public interface IFlashSalePlanAppService : + ICrudAppService< + FlashSalePlanDto, + Guid, + FlashSalePlanGetListInput, + FlashSalePlanCreateDto, + FlashSalePlanUpdateDto> +{ + Task PreOrderAsync(Guid id); + + Task OrderAsync(Guid id, CreateOrderInput input); +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/Dtos/FlashSaleResultDto.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/Dtos/FlashSaleResultDto.cs new file mode 100644 index 00000000..1df7365f --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/Dtos/FlashSaleResultDto.cs @@ -0,0 +1,20 @@ +using System; +using EasyAbp.EShop.Stores.Stores; +using Volo.Abp.Application.Dtos; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults.Dtos; + +public class FlashSaleResultDto : ExtensibleFullAuditedEntityDto, IMultiStore +{ + public virtual Guid StoreId { get; set; } + + public virtual Guid PlanId { get; set; } + + public virtual FlashSaleResultStatus Status { get; set; } + + public virtual string Reason { get; set; } + + public virtual Guid UserId { get; set; } + + public virtual Guid? OrderId { get; set; } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/Dtos/FlashSaleResultGetListInput.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/Dtos/FlashSaleResultGetListInput.cs new file mode 100644 index 00000000..b5e0b823 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/Dtos/FlashSaleResultGetListInput.cs @@ -0,0 +1,17 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults.Dtos; + +public class FlashSaleResultGetListInput : ExtensiblePagedAndSortedResultRequestDto +{ + public virtual Guid? StoreId { get; set; } + + public virtual Guid? PlanId { get; set; } + + public virtual FlashSaleResultStatus? Status { get; set; } + + public virtual Guid? UserId { get; set; } + + public virtual Guid? OrderId { get; set; } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/IFlashSaleResultAppService.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/IFlashSaleResultAppService.cs new file mode 100644 index 00000000..3c1bce94 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/IFlashSaleResultAppService.cs @@ -0,0 +1,14 @@ +using System; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults.Dtos; +using Volo.Abp.Application.Services; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; + +public interface IFlashSaleResultAppService : + IReadOnlyAppService< + FlashSaleResultDto, + Guid, + FlashSaleResultGetListInput> +{ + +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/Permissions/FlashSalesPermissionDefinitionProvider.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/Permissions/FlashSalesPermissionDefinitionProvider.cs new file mode 100644 index 00000000..4a2a7d28 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/Permissions/FlashSalesPermissionDefinitionProvider.cs @@ -0,0 +1,29 @@ +using EasyAbp.EShop.Plugins.FlashSales.Localization; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Localization; + +namespace EasyAbp.EShop.Plugins.FlashSales.Permissions; + +public class FlashSalesPermissionDefinitionProvider : PermissionDefinitionProvider +{ + public override void Define(IPermissionDefinitionContext context) + { + var myGroup = context.AddGroup(FlashSalesPermissions.GroupName, L("Permission:FlashSales")); + + var flashSalePlanPermission = myGroup.AddPermission(FlashSalesPermissions.FlashSalePlan.Default, L("Permission:FlashSalePlan")); + flashSalePlanPermission.AddChild(FlashSalesPermissions.FlashSalePlan.Manage, L("Permission:Manage")); + flashSalePlanPermission.AddChild(FlashSalesPermissions.FlashSalePlan.CrossStore, L("Permission:CrossStore")); + flashSalePlanPermission.AddChild(FlashSalesPermissions.FlashSalePlan.Create, L("Permission:Create")); + flashSalePlanPermission.AddChild(FlashSalesPermissions.FlashSalePlan.Update, L("Permission:Update")); + flashSalePlanPermission.AddChild(FlashSalesPermissions.FlashSalePlan.Delete, L("Permission:Delete")); + + var flashSaleResultPermission = myGroup.AddPermission(FlashSalesPermissions.FlashSaleResult.Default, L("Permission:FlashSaleResult")); + flashSaleResultPermission.AddChild(FlashSalesPermissions.FlashSaleResult.Manage, L("Permission:Manage")); + flashSaleResultPermission.AddChild(FlashSalesPermissions.FlashSaleResult.CrossStore, L("Permission:CrossStore")); + } + + private static LocalizableString L(string name) + { + return LocalizableString.Create(name); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/Permissions/FlashSalesPermissions.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/Permissions/FlashSalesPermissions.cs new file mode 100644 index 00000000..b03f5b90 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/Permissions/FlashSalesPermissions.cs @@ -0,0 +1,30 @@ +using Volo.Abp.Reflection; + +namespace EasyAbp.EShop.Plugins.FlashSales.Permissions; + +public class FlashSalesPermissions +{ + public const string GroupName = "EasyAbp.EShop.Plugins.FlashSales"; + + public static string[] GetAll() + { + return ReflectionHelper.GetPublicConstantsRecursively(typeof(FlashSalesPermissions)); + } + + public class FlashSalePlan + { + public const string Default = GroupName + ".FlashSalePlan"; + public const string Manage = Default + ".Manage"; + public const string CrossStore = Default + ".CrossStore"; + public const string Update = Default + ".Update"; + public const string Create = Default + ".Create"; + public const string Delete = Default + ".Delete"; + } + + public class FlashSaleResult + { + public const string Default = GroupName + ".FlashSaleResult"; + public const string Manage = Default + ".Manage"; + public const string CrossStore = Default + ".CrossStore"; + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/FodyWeavers.xml b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/FodyWeavers.xml new file mode 100644 index 00000000..1715698c --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/FodyWeavers.xsd b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/FodyWeavers.xsd new file mode 100644 index 00000000..ffa6fc4b --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp.EShop.Plugins.FlashSales.Application.abppkg.json b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp.EShop.Plugins.FlashSales.Application.abppkg.json new file mode 100644 index 00000000..412567ac --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp.EShop.Plugins.FlashSales.Application.abppkg.json @@ -0,0 +1,3 @@ +{ + "role": "lib.application" +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp.EShop.Plugins.FlashSales.Application.csproj b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp.EShop.Plugins.FlashSales.Application.csproj new file mode 100644 index 00000000..12758834 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp.EShop.Plugins.FlashSales.Application.csproj @@ -0,0 +1,23 @@ + + + + + + net6.0 + + + + + + + + + + + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesApplicationModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesApplicationModule.cs new file mode 100644 index 00000000..48e94a13 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesApplicationModule.cs @@ -0,0 +1,33 @@ +using EasyAbp.EShop.Products; +using EasyAbp.EShop.Products.Plugins.FlashSales; +using EasyAbp.EShop.Stores; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Application; +using Volo.Abp.AutoMapper; +using Volo.Abp.Caching; +using Volo.Abp.Modularity; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +[DependsOn( + typeof(EShopProductsApplicationContractsModule), + typeof(EShopPluginsFlashSalesDomainModule), + typeof(EShopPluginsFlashSalesApplicationContractsModule), + typeof(EShopStoresApplicationSharedModule), + typeof(EShopProductsPluginsFlashSalesAbstractionsModule), + typeof(EShopProductsPluginsFlashSalesApplicationContractsModule), + typeof(AbpDddApplicationModule), + typeof(AbpAutoMapperModule), + typeof(AbpCachingModule) + )] +public class EShopPluginsFlashSalesApplicationModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAutoMapperObjectMapper(); + Configure(options => + { + options.AddMaps(validate: true); + }); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/CreateFlashSaleOrderFailedEventHandler.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/CreateFlashSaleOrderFailedEventHandler.cs new file mode 100644 index 00000000..7e5f7d89 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/CreateFlashSaleOrderFailedEventHandler.cs @@ -0,0 +1,71 @@ +using System; +using System.Threading.Tasks; +using EasyAbp.Eshop.Products.Products; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; +using EasyAbp.EShop.Products.Products; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Logging; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Distributed; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +public class CreateFlashSaleOrderFailedEventHandler : IDistributedEventHandler, ITransientDependency +{ + protected IFlashSaleInventoryManager FlashSaleInventoryManager { get; } + protected IDistributedCache DistributedCache { get; } + protected IFlashSalePlanRepository FlashSalePlanRepository { get; } + protected IProductAppService ProductAppService { get; } + protected ILogger Logger { get; } + + public CreateFlashSaleOrderFailedEventHandler ( + IFlashSaleInventoryManager flashSaleInventoryManager, + IDistributedCache distributedCache, + IFlashSalePlanRepository flashSalePlanRepository, + IProductAppService productAppService, + ILogger logger) + { + FlashSaleInventoryManager = flashSaleInventoryManager; + DistributedCache = distributedCache; + FlashSalePlanRepository = flashSalePlanRepository; + ProductAppService = productAppService; + Logger = logger; + } + + public virtual async Task HandleEventAsync(CreateFlashSaleOrderCompleteEto eventData) + { + if (eventData.Success) + { + return; + } + + if (eventData.Reason != FlashSaleResultFailedReason.InvalidHashToken) + { + return; + } + + var plan = await FlashSalePlanRepository.GetAsync(eventData.PlanId); + var product = await ProductAppService.GetAsync(plan.ProductId); + + if (!await FlashSaleInventoryManager.TryRollBackInventoryAsync( + plan.TenantId, product.InventoryProviderName, + plan.StoreId, plan.ProductId, plan.ProductSkuId, 1, true + )) + { + Logger.LogWarning("Try roll back inventory failed."); + return; + } + + await RemoveUserFlashSaleResultCacheAsync(plan, eventData.UserId); + } + + protected virtual Task GetUserFlashSaleResultCacheKeyAsync(FlashSalePlan plan, Guid userId) + { + return Task.FromResult(string.Format(FlashSalePlanAppService.UserFlashSaleResultCacheKeyFormat, plan.TenantId, plan.Id, userId)); + } + + protected virtual async Task RemoveUserFlashSaleResultCacheAsync(FlashSalePlan plan, Guid userId) + { + await DistributedCache.RemoveAsync(await GetUserFlashSaleResultCacheKeyAsync(plan, userId)); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanAppService.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanAppService.cs new file mode 100644 index 00000000..4bc20ffb --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanAppService.cs @@ -0,0 +1,450 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using EasyAbp.Eshop.Products.Products; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.Dtos; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; +using EasyAbp.EShop.Plugins.FlashSales.Permissions; +using EasyAbp.EShop.Products.Products; +using EasyAbp.EShop.Products.Products.Dtos; +using EasyAbp.EShop.Stores.Stores; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Options; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Caching; +using Volo.Abp.Data; +using Volo.Abp.DistributedLocking; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.Timing; +using Volo.Abp.Users; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +[Authorize] +public class FlashSalePlanAppService : + MultiStoreCrudAppService, + IFlashSalePlanAppService +{ + /// + /// The cache key format. + /// {0}: FlashSalePlan ID + /// {1}: User ID + /// + public const string PreOrderCacheKeyFormat = "eshopflashsales_{0}_{1}"; + + /// + /// The cache key format. + /// {0}: Tenant ID + /// {1}: FlashSalePlan ID + /// {2}: User ID + /// + public const string UserFlashSaleResultCacheKeyFormat = "eshopflashsales-result_{0}_{1}_{2}"; + + protected override string CrossStorePolicyName { get; set; } = FlashSalesPermissions.FlashSalePlan.CrossStore; + protected override string GetPolicyName { get; set; } + protected override string GetListPolicyName { get; set; } + protected override string CreatePolicyName { get; set; } = FlashSalesPermissions.FlashSalePlan.Create; + protected override string UpdatePolicyName { get; set; } = FlashSalesPermissions.FlashSalePlan.Update; + protected override string DeletePolicyName { get; set; } = FlashSalesPermissions.FlashSalePlan.Delete; + + protected IFlashSalePlanRepository FlashSalePlanRepository { get; } + + protected IProductAppService ProductAppService { get; } + + protected IDistributedCache PreOrderDistributedCache { get; } + + protected IDistributedCache PlanDistributedCache { get; } + + protected IDistributedEventBus DistributedEventBus { get; } + + protected IFlashSaleResultRepository FlashSaleResultRepository { get; } + + protected IAbpDistributedLock DistributedLock { get; } + + protected IFlashSalePlanHasher FlashSalePlanHasher { get; } + + protected IFlashSaleInventoryManager FlashSaleInventoryManager { get; } + + protected IDistributedCache DistributedCache { get; } + + protected FlashSalesOptions Options { get; } + + public FlashSalePlanAppService( + IFlashSalePlanRepository flashSalePlanRepository, + IProductAppService productAppService, + IDistributedCache tokenDistributedCache, + IDistributedCache planDistributedCache, + IDistributedEventBus distributedEventBus, + IFlashSaleResultRepository flashSaleResultRepository, + IAbpDistributedLock distributedLock, + IFlashSalePlanHasher flashSalePlanHasher, + IFlashSaleInventoryManager flashSaleInventoryManager, + IDistributedCache distributedCache, + IOptionsMonitor optionsMonitor) + : base(flashSalePlanRepository) + { + FlashSalePlanRepository = flashSalePlanRepository; + ProductAppService = productAppService; + PreOrderDistributedCache = tokenDistributedCache; + PlanDistributedCache = planDistributedCache; + DistributedEventBus = distributedEventBus; + FlashSaleResultRepository = flashSaleResultRepository; + DistributedLock = distributedLock; + FlashSalePlanHasher = flashSalePlanHasher; + FlashSaleInventoryManager = flashSaleInventoryManager; + DistributedCache = distributedCache; + Options = optionsMonitor.CurrentValue; + } + + public override async Task GetAsync(Guid id) + { + var flashSalePlan = await GetEntityByIdAsync(id); + + await CheckMultiStorePolicyAsync(flashSalePlan.StoreId, GetPolicyName); + + if (!flashSalePlan.IsPublished) + { + await CheckMultiStorePolicyAsync(flashSalePlan.StoreId, FlashSalesPermissions.FlashSalePlan.Manage); + } + + return await MapToGetOutputDtoAsync(flashSalePlan); + } + + public override async Task> GetListAsync(FlashSalePlanGetListInput input) + { + await CheckMultiStorePolicyAsync(input.StoreId, GetListPolicyName); + + return await base.GetListAsync(input); + } + + public override async Task CreateAsync(FlashSalePlanCreateDto input) + { + await CheckMultiStorePolicyAsync(input.StoreId, CreatePolicyName); + + var product = await ProductAppService.GetAsync(input.ProductId); + var productSku = product.GetSkuById(input.ProductSkuId); + + await ValidateProductAsync(input.ProductId, product, input.StoreId); + + var flashSalePlan = new FlashSalePlan( + GuidGenerator.Create(), + CurrentTenant.Id, + input.StoreId, + input.BeginTime, + input.EndTime, + product.Id, + productSku.Id, + input.IsPublished + ); + + await FlashSalePlanRepository.InsertAsync(flashSalePlan, autoSave: true); + + return await MapToGetOutputDtoAsync(flashSalePlan); + } + + public override async Task UpdateAsync(Guid id, FlashSalePlanUpdateDto input) + { + var flashSalePlan = await GetEntityByIdAsync(id); + var product = await ProductAppService.GetAsync(input.ProductId); + var productSku = product.GetSkuById(input.ProductSkuId); + + await CheckMultiStorePolicyAsync(product.StoreId, UpdatePolicyName); + + await ValidateProductAsync(input.ProductId, product, flashSalePlan.StoreId); + + if (await ExistRelatedFlashSaleResultsAsync(id) && (input.ProductId != flashSalePlan.ProductId || input.ProductSkuId != flashSalePlan.ProductSkuId)) + { + throw new RelatedFlashSaleResultsExistException(id); + } + + flashSalePlan.SetTimeRange(input.BeginTime, input.EndTime); + flashSalePlan.SetProductSku(flashSalePlan.StoreId, product.Id, productSku.Id); + flashSalePlan.SetPublished(input.IsPublished); + + flashSalePlan.SetConcurrencyStampIfNotNull(input.ConcurrencyStamp); + + await FlashSalePlanRepository.UpdateAsync(flashSalePlan, autoSave: true); + + return await MapToGetOutputDtoAsync(flashSalePlan); + } + + public override async Task DeleteAsync(Guid id) + { + var flashSalePlan = await GetEntityByIdAsync(id); + + await CheckMultiStorePolicyAsync(flashSalePlan.StoreId, DeletePolicyName); + + if (await ExistRelatedFlashSaleResultsAsync(id)) + { + throw new RelatedFlashSaleResultsExistException(id); + } + + await FlashSalePlanRepository.DeleteAsync(flashSalePlan); + } + + protected override async Task> CreateFilteredQueryAsync(FlashSalePlanGetListInput input) + { + if (input.IncludeUnpublished) + { + await CheckMultiStorePolicyAsync(input.StoreId, FlashSalesPermissions.FlashSalePlan.Manage); + } + + return (await base.CreateFilteredQueryAsync(input)) + .WhereIf(input.StoreId.HasValue, x => x.StoreId == input.StoreId.Value) + .WhereIf(input.ProductId.HasValue, x => x.ProductId == input.ProductId.Value) + .WhereIf(input.ProductSkuId.HasValue, x => x.ProductSkuId == input.ProductSkuId.Value) + .WhereIf(!input.IncludeUnpublished, x => x.IsPublished) + .WhereIf(input.Start.HasValue, x => x.BeginTime >= input.Start.Value) + .WhereIf(input.End.HasValue, x => x.BeginTime <= input.End.Value); + } + + public virtual async Task PreOrderAsync(Guid id) + { + var plan = await GetFlashSalePlanCacheAsync(id); + var product = await ProductAppService.GetAsync(plan.ProductId); + var productSku = product.GetSkuById(plan.ProductSkuId); + var expiresTime = DateTimeOffset.Now.Add(Options.PreOrderExpires); + + await ValidatePreOrderAsync(plan, product, productSku); + + await SetPreOrderCacheAsync(plan, product, productSku, expiresTime); + + return new FlashSalePlanPreOrderDto { ExpiresTime = Clock.Normalize(expiresTime.LocalDateTime), ExpiresInSeconds = Options.PreOrderExpires.TotalSeconds }; + } + + public virtual async Task OrderAsync(Guid id, CreateOrderInput input) + { + var preOrderCache = await GetPreOrderCacheAsync(id); + if (preOrderCache == null) + { + throw new BusinessException(FlashSalesErrorCodes.PreOrderExpired); + } + + var plan = await GetFlashSalePlanCacheAsync(id); + var now = Clock.Now; + if (plan.BeginTime > now) + { + throw new BusinessException(FlashSalesErrorCodes.FlashSaleNotStarted); + } + + if (now >= plan.EndTime) + { + throw new BusinessException(FlashSalesErrorCodes.FlashSaleIsOver); + } + + await RemovePreOrderCacheAsync(id); + + var userId = CurrentUser.GetId(); + + var lockKey = $"create-flash-sale-order-{plan.Id}-{userId}"; + + await using var handle = await DistributedLock.TryAcquireAsync(lockKey); + + if (handle == null) + { + throw new BusinessException(FlashSalesErrorCodes.BusyToCreateFlashSaleOrder); + } + + var userFlashSaleResultCache = await GetUserFlashSaleResultCacheAsync(plan.Id); + if (!userFlashSaleResultCache.IsNullOrWhiteSpace()) + { + throw new BusinessException(FlashSalesErrorCodes.DuplicateFlashSalesOrder); + } + + if (!await FlashSaleInventoryManager.TryReduceInventoryAsync( + plan.TenantId, preOrderCache.InventoryProviderName, + plan.StoreId, plan.ProductId, plan.ProductSkuId, 1, true)) + { + return new FlashSaleOrderResultDto() { IsSuccess = false }; + } + + var result = await CreatePendingFlashSaleResultAsync(plan, userId, async (existsResultId) => + { + await SetUserFlashSaleResultCacheAsync(plan.Id, existsResultId); + + await FlashSaleInventoryManager.TryRollBackInventoryAsync( + plan.TenantId, preOrderCache.InventoryProviderName, + plan.StoreId, plan.ProductId, plan.ProductSkuId, 1, true + ); + }); + + await SetUserFlashSaleResultCacheAsync(plan.Id, result.Id); + + var createFlashSaleOrderEto = await PrepareCreateFlashSaleOrderEtoAsync(plan, result.Id, input, userId, now, preOrderCache.HashToken); + + await DistributedEventBus.PublishAsync(createFlashSaleOrderEto); + + return new FlashSaleOrderResultDto() { IsSuccess = true, FlashSaleResultId = result.Id }; + } + + #region PreOrderCache + + protected virtual async Task GetFlashSalePlanCacheAsync(Guid id) + { + return await PlanDistributedCache.GetOrAddAsync(id, async () => + { + var flashSalePlan = await FlashSalePlanRepository.GetAsync(id); + return ObjectMapper.Map(flashSalePlan); + }); + } + + protected virtual Task GetPreOrderCacheKeyAsync(Guid planId) + { + return Task.FromResult(string.Format(PreOrderCacheKeyFormat, planId, CurrentUser.Id)); + } + + protected virtual async Task GetPreOrderCacheAsync(Guid planId) + { + return await PreOrderDistributedCache.GetAsync(await GetPreOrderCacheKeyAsync(planId)); + } + + protected virtual async Task RemovePreOrderCacheAsync(Guid planId) + { + await PreOrderDistributedCache.RemoveAsync(await GetPreOrderCacheKeyAsync(planId)); + } + + protected virtual async Task SetPreOrderCacheAsync(FlashSalePlanCacheItem plan, ProductDto product, ProductSkuDto productSku, DateTimeOffset expirationTime) + { + var hashToken = await FlashSalePlanHasher.HashAsync(plan.LastModificationTime, product.LastModificationTime, productSku.LastModificationTime); + + await PreOrderDistributedCache.SetAsync(await GetPreOrderCacheKeyAsync(plan.Id), new FlashSalePlanPreOrderCacheItem() + { + HashToken = hashToken, + PlanId = plan.Id, + ProductId = product.Id, + ProductSkuId = productSku.Id, + InventoryProviderName = product.InventoryProviderName, + }, new DistributedCacheEntryOptions() + { + AbsoluteExpiration = expirationTime + }); + } + + #endregion + + #region UserFlashSaleResultCache + + protected virtual Task GetUserFlashSaleResultCacheKeyAsync(Guid planId) + { + return Task.FromResult(string.Format(UserFlashSaleResultCacheKeyFormat, CurrentTenant.Id, planId, CurrentUser.GetId())); + } + + protected virtual async Task GetUserFlashSaleResultCacheAsync(Guid planId) + { + var userFlashSaleResultCacheKey = await GetUserFlashSaleResultCacheKeyAsync(planId); + return await DistributedCache.GetStringAsync(userFlashSaleResultCacheKey); + } + + protected virtual async Task SetUserFlashSaleResultCacheAsync(Guid planId, Guid resultId) + { + var userFlashSaleResultCacheKey = await GetUserFlashSaleResultCacheKeyAsync(planId); + await DistributedCache.SetStringAsync(userFlashSaleResultCacheKey, resultId.ToString()); + } + + #endregion + + protected virtual Task ValidatePreOrderAsync(FlashSalePlanCacheItem plan, ProductDto product, ProductSkuDto productSku) + { + if (!product.IsPublished) + { + throw new BusinessException(FlashSalesErrorCodes.ProductIsNotPublished); + } + + if (product.InventoryStrategy != InventoryStrategy.FlashSales) + { + throw new UnexpectedInventoryStrategyException(InventoryStrategy.FlashSales); + } + + if (!plan.IsPublished) + { + throw new EntityNotFoundException(typeof(FlashSalePlan), plan.Id); + } + + if (Clock.Now >= plan.EndTime) + { + throw new BusinessException(FlashSalesErrorCodes.FlashSaleIsOver); + } + + if (productSku.Inventory < 1) + { + throw new BusinessException(FlashSalesErrorCodes.ProductSkuInventoryExceeded); + } + + return Task.CompletedTask; + } + + protected virtual async Task ExistRelatedFlashSaleResultsAsync(Guid planId) + { + return await FlashSaleResultRepository.AnyAsync(x => x.PlanId == planId); + } + + protected virtual Task ValidateProductAsync(Guid productId, ProductDto product, Guid storeId) + { + if (product.StoreId != storeId) + { + throw new ProductIsNotInThisStoreException(productId, storeId); + } + + if (product.InventoryStrategy != InventoryStrategy.FlashSales) + { + throw new UnexpectedInventoryStrategyException(InventoryStrategy.FlashSales); + } + + return Task.CompletedTask; + } + + protected virtual Task PrepareCreateFlashSaleOrderEtoAsync( + FlashSalePlanCacheItem plan, Guid resultId, CreateOrderInput input, + Guid userId, DateTime now, string hashToken) + { + var planEto = ObjectMapper.Map(plan); + planEto.TenantId = CurrentTenant.Id; + + var eto = new CreateFlashSaleOrderEto() + { + TenantId = CurrentTenant.Id, + PlanId = plan.Id, + UserId = userId, + PendingResultId = resultId, + StoreId = plan.StoreId, + CreateTime = now, + CustomerRemark = input.CustomerRemark, + Plan = planEto, + HashToken = hashToken + }; + + foreach (var item in input.ExtraProperties) + { + eto.ExtraProperties.Add(item.Key, item.Value); + } + + return Task.FromResult(eto); + } + + protected virtual async Task CreatePendingFlashSaleResultAsync(FlashSalePlanCacheItem plan, Guid userId, Func existResultPreProcess) + { + // Prevent repeat submit + var existsResult = await FlashSaleResultRepository.FirstOrDefaultAsync(x => + x.PlanId == plan.Id && x.UserId == userId && x.Status != FlashSaleResultStatus.Failed && x.Reason != FlashSaleResultFailedReason.InvalidHashToken); + + if (existsResult != null) + { + await existResultPreProcess(existsResult.Id); + throw new BusinessException(FlashSalesErrorCodes.DuplicateFlashSalesOrder); + } + + var result = new FlashSaleResult( + id: GuidGenerator.Create(), + tenantId: CurrentTenant.Id, + storeId: plan.StoreId, + planId: plan.Id, + userId: userId + ); + + return await FlashSaleResultRepository.InsertAsync(result, autoSave: true); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanCacheInvalidator.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanCacheInvalidator.cs new file mode 100644 index 00000000..7c3c568a --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanCacheInvalidator.cs @@ -0,0 +1,33 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Entities.Events; +using Volo.Abp.EventBus; +using Volo.Abp.Uow; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +public class FlashSalePlanCacheInvalidator : ILocalEventHandler>, ITransientDependency +{ + protected IDistributedCache DistributedCache { get; } + protected IUnitOfWorkManager UnitOfWorkManager { get; } + + public FlashSalePlanCacheInvalidator( + IDistributedCache distributedCache, + IUnitOfWorkManager unitOfWorkManager) + { + DistributedCache = distributedCache; + UnitOfWorkManager = unitOfWorkManager; + } + + public virtual async Task HandleEventAsync(EntityChangedEventData eventData) + { + await DistributedCache.RemoveAsync(eventData.Entity.Id); + + UnitOfWorkManager.Current.OnCompleted(async () => + { + await DistributedCache.RemoveAsync(eventData.Entity.Id); + }); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanCacheItem.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanCacheItem.cs new file mode 100644 index 00000000..4aefcbad --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanCacheItem.cs @@ -0,0 +1,11 @@ +using System; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.Dtos; +using Volo.Abp.MultiTenancy; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +[Serializable] +public class FlashSalePlanCacheItem : FlashSalePlanDto, IMultiTenant +{ + public Guid? TenantId { get; set; } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanTokenCacheItem.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanTokenCacheItem.cs new file mode 100644 index 00000000..efaff0e9 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanTokenCacheItem.cs @@ -0,0 +1,20 @@ +using System; +using Volo.Abp.MultiTenancy; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +[Serializable] +public class FlashSalePlanPreOrderCacheItem : IMultiTenant +{ + public Guid? TenantId { get; set; } + + public string HashToken { get; set; } + + public Guid PlanId { get; set; } + + public Guid ProductId { get; set; } + + public Guid ProductSkuId { get; set; } + + public string InventoryProviderName { get; set; } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalesOptions.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalesOptions.cs new file mode 100644 index 00000000..45d94a88 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalesOptions.cs @@ -0,0 +1,11 @@ +using System; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +public class FlashSalesOptions +{ + /// + /// Default: 3 minutes + /// + public TimeSpan PreOrderExpires { get; set; } = TimeSpan.FromMinutes(3); +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/ProductIsNotInThisStoreException.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/ProductIsNotInThisStoreException.cs new file mode 100644 index 00000000..3733fb84 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/ProductIsNotInThisStoreException.cs @@ -0,0 +1,13 @@ +using System; +using Volo.Abp; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +public class ProductIsNotInThisStoreException : BusinessException +{ + public ProductIsNotInThisStoreException(Guid productId, Guid storeId) : base(FlashSalesErrorCodes.ProductIsNotInThisStore) + { + WithData(nameof(productId), productId); + WithData(nameof(storeId), storeId); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/ProductSkuIsNotFoundException.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/ProductSkuIsNotFoundException.cs new file mode 100644 index 00000000..f1ab3263 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/ProductSkuIsNotFoundException.cs @@ -0,0 +1,12 @@ +using System; +using Volo.Abp; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +public class ProductSkuIsNotFoundException : BusinessException +{ + public ProductSkuIsNotFoundException(Guid productSkuId) : base(FlashSalesErrorCodes.ProductSkuIsNotFound) + { + WithData(nameof(productSkuId), productSkuId); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/RelatedFlashSaleResultsExistException.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/RelatedFlashSaleResultsExistException.cs new file mode 100644 index 00000000..b50dfeb6 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/RelatedFlashSaleResultsExistException.cs @@ -0,0 +1,13 @@ +using System; +using Volo.Abp; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +[Serializable] +public class RelatedFlashSaleResultsExistException : BusinessException +{ + public RelatedFlashSaleResultsExistException(Guid planId) : base(FlashSalesErrorCodes.RelatedFlashSaleResultsExist) + { + WithData(nameof(planId), planId); + } +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/UnexpectedInventoryStrategyException.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/UnexpectedInventoryStrategyException.cs new file mode 100644 index 00000000..cc9deae9 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/UnexpectedInventoryStrategyException.cs @@ -0,0 +1,12 @@ +using EasyAbp.EShop.Products.Products; +using Volo.Abp; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +public class UnexpectedInventoryStrategyException : BusinessException +{ + public UnexpectedInventoryStrategyException(InventoryStrategy expectedInventoryStrategy) : base(FlashSalesErrorCodes.UnexpectedInventoryStrategy) + { + WithData(nameof(expectedInventoryStrategy), expectedInventoryStrategy); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultAppService.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultAppService.cs new file mode 100644 index 00000000..20e1a1bc --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultAppService.cs @@ -0,0 +1,58 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults.Dtos; +using EasyAbp.EShop.Plugins.FlashSales.Permissions; +using EasyAbp.EShop.Stores.Stores; +using Volo.Abp.Application.Dtos; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; + +public class FlashSaleResultAppService : + MultiStoreCrudAppService, + IFlashSaleResultAppService +{ + protected override string CrossStorePolicyName { get; set; } = FlashSalesPermissions.FlashSaleResult.CrossStore; + protected override string GetPolicyName { get; set; } + protected override string GetListPolicyName { get; set; } + + public FlashSaleResultAppService(IFlashSaleResultRepository flashSaleResultRepository) : base(flashSaleResultRepository) + { + } + + public override async Task GetAsync(Guid id) + { + var flashSaleResult = await GetEntityByIdAsync(id); + + await CheckMultiStorePolicyAsync(flashSaleResult.StoreId, GetPolicyName); + + if (flashSaleResult.UserId != CurrentUser.Id) + { + await CheckMultiStorePolicyAsync(flashSaleResult.StoreId, FlashSalesPermissions.FlashSaleResult.Manage); + } + + return await MapToGetOutputDtoAsync(flashSaleResult); + } + + public override async Task> GetListAsync(FlashSaleResultGetListInput input) + { + await CheckMultiStorePolicyAsync(input.StoreId, GetListPolicyName); + + return await base.GetListAsync(input); + } + + protected override async Task> CreateFilteredQueryAsync(FlashSaleResultGetListInput input) + { + if (input.UserId != CurrentUser.Id) + { + await CheckMultiStorePolicyAsync(input.StoreId, FlashSalesPermissions.FlashSaleResult.Manage); + } + + return (await base.CreateFilteredQueryAsync(input)) + .WhereIf(input.StoreId.HasValue, x => x.StoreId == input.StoreId.Value) + .WhereIf(input.PlanId.HasValue, x => x.PlanId == input.PlanId.Value) + .WhereIf(input.Status.HasValue, x => x.Status == input.Status.Value) + .WhereIf(input.UserId.HasValue, x => x.UserId == input.UserId.Value) + .WhereIf(input.OrderId.HasValue, x => x.OrderId == input.OrderId.Value); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesAppService.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesAppService.cs new file mode 100644 index 00000000..4d6c6e6a --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesAppService.cs @@ -0,0 +1,13 @@ +using EasyAbp.EShop.Plugins.FlashSales.Localization; +using Volo.Abp.Application.Services; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +public abstract class FlashSalesAppService : ApplicationService +{ + protected FlashSalesAppService() + { + LocalizationResource = typeof(FlashSalesResource); + ObjectMapperContext = typeof(EShopPluginsFlashSalesApplicationModule); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesApplicationAutoMapperProfile.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesApplicationAutoMapperProfile.cs new file mode 100644 index 00000000..3f2a4324 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesApplicationAutoMapperProfile.cs @@ -0,0 +1,28 @@ +using AutoMapper; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.Dtos; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults.Dtos; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +public class FlashSalesApplicationAutoMapperProfile : Profile +{ + public FlashSalesApplicationAutoMapperProfile() + { + /* You can configure your AutoMapper mapping configuration here. + * Alternatively, you can split your mapping configurations + * into multiple profile classes for a better organization. */ + CreateMap() + .MapExtraProperties(); + CreateMap(MemberList.Source); + CreateMap(MemberList.Source); + CreateMap() + .MapExtraProperties(); + CreateMap() + .MapExtraProperties(); + + CreateMap() + .MapExtraProperties(); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/FlashSaleInventoryManager.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/FlashSaleInventoryManager.cs new file mode 100644 index 00000000..6e6bd678 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/FlashSaleInventoryManager.cs @@ -0,0 +1,33 @@ +using System; +using System.Threading.Tasks; +using EasyAbp.Eshop.Products.Products; +using EasyAbp.EShop.Products.Products.Dtos; +using Volo.Abp.DependencyInjection; + +namespace EasyAbp.EShop.Products.Products; + +[ExposeServices(typeof(IFlashSaleInventoryManager), IncludeSelf = true)] +[Dependency(TryRegister = true)] +public class FlashSaleInventoryManager : IFlashSaleInventoryManager, ITransientDependency +{ + protected IFlashSaleInventoryAppService FlashSaleInventoryReducerAppService { get; } + + public FlashSaleInventoryManager(IFlashSaleInventoryAppService flashSaleInventoryReducerAppService) + { + FlashSaleInventoryReducerAppService = flashSaleInventoryReducerAppService; + } + + public virtual async Task TryReduceInventoryAsync(Guid? tenantId, string providerName, Guid storeId, Guid productId, + Guid productSkuId, int quantity, bool increaseSold) + { + return await FlashSaleInventoryReducerAppService.TryReduceAsync( + new ReduceInventoryInput(tenantId, providerName, storeId, productId, productSkuId, quantity, increaseSold)); + } + + public virtual async Task TryRollBackInventoryAsync(Guid? tenantId, string providerName, Guid storeId, Guid productId, + Guid productSkuId, int quantity, bool decreaseSold) + { + return await FlashSaleInventoryReducerAppService.TryIncreaseAsync + (new IncreaseInventoryInput(tenantId, providerName, storeId, productId, productSkuId, quantity, decreaseSold)); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/FodyWeavers.xml b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/FodyWeavers.xml new file mode 100644 index 00000000..1715698c --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/FodyWeavers.xsd b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/FodyWeavers.xsd new file mode 100644 index 00000000..ffa6fc4b --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared.abppkg.json b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared.abppkg.json new file mode 100644 index 00000000..8b3de05f --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared.abppkg.json @@ -0,0 +1,3 @@ +{ + "role": "lib.domain-shared" +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared.csproj b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared.csproj new file mode 100644 index 00000000..95717151 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared.csproj @@ -0,0 +1,28 @@ + + + + + + netstandard2.0 + + true + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesDomainSharedModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesDomainSharedModule.cs new file mode 100644 index 00000000..2063355f --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesDomainSharedModule.cs @@ -0,0 +1,36 @@ +using Volo.Abp.Modularity; +using Volo.Abp.Localization; +using EasyAbp.EShop.Plugins.FlashSales.Localization; +using Volo.Abp.Localization.ExceptionHandling; +using Volo.Abp.Validation; +using Volo.Abp.Validation.Localization; +using Volo.Abp.VirtualFileSystem; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +[DependsOn( + typeof(AbpValidationModule) +)] +public class EShopPluginsFlashSalesDomainSharedModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + + Configure(options => + { + options.Resources + .Add("en") + .AddBaseTypes(typeof(AbpValidationResource)) + .AddVirtualJson("EasyAbp/EShop/Plugins/FlashSales/Localization"); + }); + + Configure(options => + { + options.MapCodeNamespace(FlashSalesErrorCodes.Namespace, typeof(FlashSalesResource)); + }); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/CreateFlashSaleOrderCompleteEto.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/CreateFlashSaleOrderCompleteEto.cs new file mode 100644 index 00000000..b5a76b63 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/CreateFlashSaleOrderCompleteEto.cs @@ -0,0 +1,24 @@ +using System; +using Volo.Abp.MultiTenancy; +using Volo.Abp.ObjectExtending; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +public class CreateFlashSaleOrderCompleteEto : ExtensibleObject, IMultiTenant +{ + public Guid? TenantId { get; set; } + + public Guid PendingResultId { get; set; } + + public bool Success { get; set; } + + public Guid StoreId { get; set; } + + public Guid PlanId { get; set; } + + public string Reason { get; set; } + + public Guid UserId { get; set; } + + public Guid? OrderId { get; set; } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/CreateFlashSaleOrderEto.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/CreateFlashSaleOrderEto.cs new file mode 100644 index 00000000..c17a0f39 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/CreateFlashSaleOrderEto.cs @@ -0,0 +1,27 @@ +using System; +using Volo.Abp.MultiTenancy; +using Volo.Abp.ObjectExtending; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +[Serializable] +public class CreateFlashSaleOrderEto : ExtensibleObject, IMultiTenant +{ + public Guid? TenantId { get; set; } + + public Guid StoreId { get; set; } + + public Guid PlanId { get; set; } + + public Guid UserId { get; set; } + + public Guid PendingResultId { get; set; } + + public DateTime CreateTime { get; set; } + + public string CustomerRemark { get; set; } + + public FlashSalePlanEto Plan { get; set; } + + public string HashToken { get; set; } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanEto.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanEto.cs new file mode 100644 index 00000000..275e352f --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanEto.cs @@ -0,0 +1,23 @@ +using System; +using Volo.Abp.MultiTenancy; +using Volo.Abp.ObjectExtending; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +[Serializable] +public class FlashSalePlanEto : FullAuditedEntityEto, IMultiTenant +{ + public Guid? TenantId { get; set; } + + public Guid StoreId { get; set; } + + public DateTime BeginTime { get; set; } + + public DateTime EndTime { get; set; } + + public Guid ProductId { get; set; } + + public Guid ProductSkuId { get; set; } + + public bool IsPublished { get; set; } +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanHasher.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanHasher.cs new file mode 100644 index 00000000..783c44b3 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanHasher.cs @@ -0,0 +1,31 @@ +using System; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +public class FlashSalePlanHasher : IFlashSalePlanHasher, ITransientDependency +{ + public virtual Task HashAsync(DateTime? planLastModificationTime, DateTime? productLastModificationTime, DateTime? productSkuLastModificationTime) + { + var input = $"{planLastModificationTime?.Ticks}|{productLastModificationTime?.Ticks}|{productSkuLastModificationTime?.Ticks}"; + + return Task.FromResult(CreateMd5(input)); + } + + private static string CreateMd5(string input) + { + using var md5 = MD5.Create(); + + var inputBytes = Encoding.UTF8.GetBytes(input); + + var sb = new StringBuilder(); + foreach (var t in md5.ComputeHash(inputBytes)) + { + sb.Append(t.ToString("X2")); + } + return sb.ToString(); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/IFlashSalePlanHasher.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/IFlashSalePlanHasher.cs new file mode 100644 index 00000000..6da39e91 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/IFlashSalePlanHasher.cs @@ -0,0 +1,9 @@ +using System; +using System.Threading.Tasks; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +public interface IFlashSalePlanHasher +{ + Task HashAsync(DateTime? planLastModificationTime, DateTime? productLastModificationTime, DateTime? productSkuLastModificationTime); +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultFailedReason.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultFailedReason.cs new file mode 100644 index 00000000..f4ef7845 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultFailedReason.cs @@ -0,0 +1,8 @@ +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; + +public static class FlashSaleResultFailedReason +{ + public const string InsufficientInventory = "InsufficientInventory"; + + public const string InvalidHashToken = "InvalidHashToken"; +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultStatus.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultStatus.cs new file mode 100644 index 00000000..4b2c972b --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultStatus.cs @@ -0,0 +1,8 @@ +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; + +public enum FlashSaleResultStatus +{ + Pending = 0, + Successful = 1, + Failed = 2 +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesErrorCodes.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesErrorCodes.cs new file mode 100644 index 00000000..a38555b3 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesErrorCodes.cs @@ -0,0 +1,32 @@ +namespace EasyAbp.EShop.Plugins.FlashSales; + +public static class FlashSalesErrorCodes +{ + public const string Namespace = "EasyAbp.EShop.Plugins.FlashSales"; + + public const string InvalidEndTime = $"{Namespace}:{nameof(InvalidEndTime)}"; + + public const string ProductIsNotInThisStore = $"{Namespace}:{nameof(ProductIsNotInThisStore)}"; + + public const string ProductSkuIsNotFound = $"{Namespace}:{nameof(ProductSkuIsNotFound)}"; + + public const string ProductIsNotPublished = $"{Namespace}:{nameof(ProductIsNotPublished)}"; + + public const string ProductSkuInventoryExceeded = $"{Namespace}:{nameof(ProductSkuInventoryExceeded)}"; + + public const string UnexpectedInventoryStrategy = $"{Namespace}:{nameof(UnexpectedInventoryStrategy)}"; + + public const string PreOrderExpired = $"{Namespace}:{nameof(PreOrderExpired)}"; + + public const string FlashSaleNotStarted = $"{Namespace}:{nameof(FlashSaleNotStarted)}"; + + public const string FlashSaleIsOver = $"{Namespace}:{nameof(FlashSaleIsOver)}"; + + public const string BusyToCreateFlashSaleOrder = $"{Namespace}:{nameof(BusyToCreateFlashSaleOrder)}"; + + public const string DuplicateFlashSalesOrder = $"{Namespace}:{nameof(DuplicateFlashSalesOrder)}"; + + public const string RelatedFlashSaleResultsExist = $"{Namespace}:{nameof(RelatedFlashSaleResultsExist)}"; + + public const string FlashSaleResultStatusNotPending = $"{Namespace}:{nameof(FlashSaleResultStatusNotPending)}"; +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FullAuditedEntityEto.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FullAuditedEntityEto.cs new file mode 100644 index 00000000..7961d1f6 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FullAuditedEntityEto.cs @@ -0,0 +1,23 @@ +using System; +using Volo.Abp.ObjectExtending; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +public class FullAuditedEntityEto : ExtensibleObject +{ + public TPrimaryKey Id { get; set; } + + public DateTime CreationTime { get; set; } + + public Guid? CreatorId { get; set; } + + public DateTime? LastModificationTime { get; set; } + + public Guid? LastModifierId { get; set; } + + public bool IsDeleted { get; set; } + + public Guid? DeleterId { get; set; } + + public DateTime? DeletionTime { get; set; } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/FlashSalesResource.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/FlashSalesResource.cs new file mode 100644 index 00000000..b2519b45 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/FlashSalesResource.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Localization; + +namespace EasyAbp.EShop.Plugins.FlashSales.Localization; + +[LocalizationResourceName("EasyAbpEShopPluginsFlashSales")] +public class FlashSalesResource +{ + +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/en.json b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/en.json new file mode 100644 index 00000000..0b5b2c37 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/en.json @@ -0,0 +1,53 @@ +{ + "culture": "en", + "texts": { + "EasyAbp.EShop.Plugins.FlashSales:InvalidEndTime": "End time must be later than start time", + "EasyAbp.EShop.Plugins.FlashSales:ProductIsNotInThisStore": "Product '{productId}' is not in this store '{storeId}'", + "EasyAbp.EShop.Plugins.FlashSales:ProductSkuIsNotFound": "ProductSku '{productSkuId}' is not found", + "EasyAbp.EShop.Plugins.FlashSales:ProductIsNotPublished": "Product is not published", + "EasyAbp.EShop.Plugins.FlashSales:ProductSkuInventoryExceeded": "ProductSku inventory exceeded", + "EasyAbp.EShop.Plugins.FlashSales:UnexpectedInventoryStrategy": "Only the specified inventory strategy {expectedInventoryStrategy} product is allowed.", + "EasyAbp.EShop.Plugins.FlashSales:PreOrderExpired": "Pre order expired", + "EasyAbp.EShop.Plugins.FlashSales:FlashSaleNotStarted": "Flash-sale not started", + "EasyAbp.EShop.Plugins.FlashSales:FlashSaleIsOver": "Flash-sale is over", + "EasyAbp.EShop.Plugins.FlashSales:BusyToCreateFlashSaleOrder": "Busy to create flash-sale order", + "EasyAbp.EShop.Plugins.FlashSales:DuplicateFlashSalesOrder": "Duplicate flash-sales order", + "EasyAbp.EShop.Plugins.FlashSales:RelatedFlashSaleResultsExist": "Related flash-sales results exist", + "EasyAbp.EShop.Plugins.FlashSales:FlashSaleResultStatusNotPending": "The status of flash-sale result has been changed", + "Menu:FlashSalesManagement": "Flash-Sales Management", + "Permission:FlashSalePlan": "FlashSale Plan", + "Permission:Manage": "Manage", + "Permission:Create": "Create", + "Permission:Update": "Update", + "Permission:Delete": "Delete", + "Menu:FlashSalePlan": "Flash-Sale Plan", + "FlashSalePlan": "Flash-Sale Plan", + "FlashSalePlanStoreId": "Store ID", + "FlashSalePlanBeginTime": "Begin Time", + "FlashSalePlanEndTime": "End Time", + "FlashSalePlanProductId": "Product ID", + "FlashSalePlanProductSkuId": "Product SKU ID", + "FlashSalePlanIsPublished": "Is Published", + "CreateFlashSalePlan": "Create", + "EditFlashSalePlan": "Edit", + "FlashSalePlanDeletionConfirmationMessage": "Are you sure to delete the flash-sale plan {0}?", + "SuccessfullyDeleted": "Successfully deleted", + "Permission:FlashSaleResult": "Flash-Sale Result", + "Menu:FlashSaleResult": "Flash-Sale Result", + "FlashSaleResult": "Flash-Sale Result", + "FlashSaleResultStoreId": "Store ID", + "FlashSaleResultPlanId": "Plan ID", + "FlashSaleResultStatus": "Status", + "FlashSaleResultReason": "Reason", + "FlashSaleResultUserId": "User ID", + "FlashSaleResultOrderId": "Order ID", + "FlashSaleResultCreationTime": "Creation Time", + "ViewFlashSaleResult": "View", + "Enum:FlashSaleResultStatus.Pending": "Pending", + "Enum:FlashSaleResultStatus.Successful": "Successful", + "Enum:FlashSaleResultStatus.Failed": "Failed", + "Enum:FlashSaleResultStatus.0": "Pending", + "Enum:FlashSaleResultStatus.1": "Successful", + "Enum:FlashSaleResultStatus.2": "Failed" + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/zh-Hans.json b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/zh-Hans.json new file mode 100644 index 00000000..5aa756b8 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/zh-Hans.json @@ -0,0 +1,53 @@ +{ + "culture": "zh-Hans", + "texts": { + "EasyAbp.EShop.Plugins.FlashSales:InvalidEndTime": "结束时间必须晚于开始时间", + "EasyAbp.EShop.Plugins.FlashSales:ProductIsNotInThisStore": "产品'{productId}'不在此店铺'{storeId}'中", + "EasyAbp.EShop.Plugins.FlashSales:ProductSkuIsNotFound": "产品SKU'{productSkuId}'不存在", + "EasyAbp.EShop.Plugins.FlashSales:ProductIsNotPublished": "产品尚未发布", + "EasyAbp.EShop.Plugins.FlashSales:ProductSkuInventoryExceeded": "产品SKU库存不足", + "EasyAbp.EShop.Plugins.FlashSales:UnexpectedInventoryStrategy": "只允许使用指定{expectedInventoryStrategy}库存策略的产品", + "EasyAbp.EShop.Plugins.FlashSales:PreOrderExpired": "预下单已过期", + "EasyAbp.EShop.Plugins.FlashSales:FlashSaleNotStarted": "闪购尚未开始", + "EasyAbp.EShop.Plugins.FlashSales:FlashSaleIsOver": "闪购已经结束", + "EasyAbp.EShop.Plugins.FlashSales:BusyToCreateFlashSaleOrder": "闪购下单繁忙", + "EasyAbp.EShop.Plugins.FlashSales:DuplicateFlashSalesOrder": "重复闪购下单", + "EasyAbp.EShop.Plugins.FlashSales:RelatedFlashSaleResultsExist": "已存在关联的闪购结果", + "EasyAbp.EShop.Plugins.FlashSales:FlashSaleResultStatusNotPending": "闪购结果的状态已经变更", + "Menu:FlashSalesManagement": "闪购", + "Permission:FlashSalePlan": "闪购计划", + "Permission:Manage": "管理", + "Permission:Create": "新建", + "Permission:Update": "更新", + "Permission:Delete": "删除", + "Menu:FlashSalePlan": "闪购计划", + "FlashSalePlan": "闪购计划", + "FlashSalePlanStoreId": "店铺 ID", + "FlashSalePlanBeginTime": "开始时间", + "FlashSalePlanEndTime": "结束时间", + "FlashSalePlanProductId": "产品 ID", + "FlashSalePlanProductSkuId": "产品SKU ID", + "FlashSalePlanIsPublished": "发布", + "CreateFlashSalePlan": "新建", + "EditFlashSalePlan": "編輯", + "FlashSalePlanDeletionConfirmationMessage": "确认删除闪购计划 {0}?", + "SuccessfullyDeleted": "删除成功", + "Permission:FlashSaleResult": "闪购结果", + "Menu:FlashSaleResult": "闪购结果", + "FlashSaleResult": "闪购结果", + "FlashSaleResultStoreId": "店铺 ID", + "FlashSaleResultPlanId": "计划 ID", + "FlashSaleResultStatus": "状态", + "FlashSaleResultReason": "原因", + "FlashSaleResultUserId": "用户 ID", + "FlashSaleResultOrderId": "订单 ID", + "FlashSaleResultCreationTime": "创建时间", + "ViewFlashSaleResult": "查看", + "Enum:FlashSaleResultStatus.Pending": "等待中", + "Enum:FlashSaleResultStatus.Successful": "成功", + "Enum:FlashSaleResultStatus.Failed": "失败", + "Enum:FlashSaleResultStatus.0": "等待中", + "Enum:FlashSaleResultStatus.1": "成功", + "Enum:FlashSaleResultStatus.2": "失败" + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/zh-Hant.json b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/zh-Hant.json new file mode 100644 index 00000000..6f7a3e84 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/zh-Hant.json @@ -0,0 +1,53 @@ +{ + "culture": "zh-Hant", + "texts": { + "EasyAbp.EShop.Plugins.FlashSales:InvalidEndTime": "結束時間必須晚於開始時間", + "EasyAbp.EShop.Plugins.FlashSales:ProductIsNotInThisStore": "產品'{productId}'不在此店鋪'{storeId}'中", + "EasyAbp.EShop.Plugins.FlashSales:ProductSkuIsNotFound": "產品SKU'{productSkuId}'不存在", + "EasyAbp.EShop.Plugins.FlashSales:ProductIsNotPublished": "產品尚未發佈", + "EasyAbp.EShop.Plugins.FlashSales:ProductSkuInventoryExceeded": "產品SKU庫存不足", + "EasyAbp.EShop.Plugins.FlashSales:UnexpectedInventoryStrategy": "只允許使用指定的{expectedInventoryStrategy}庫存策略的產品", + "EasyAbp.EShop.Plugins.FlashSales:PreOrderExpired": "預下單已過期", + "EasyAbp.EShop.Plugins.FlashSales:FlashSaleNotStarted": "閃購尚未開始", + "EasyAbp.EShop.Plugins.FlashSales:FlashSaleIsOver": "閃購已經結束", + "EasyAbp.EShop.Plugins.FlashSales:BusyToCreateFlashSaleOrder": "閃購下單繁忙", + "EasyAbp.EShop.Plugins.FlashSales:DuplicateFlashSalesOrder": "重複閃購下單", + "EasyAbp.EShop.Plugins.FlashSales:RelatedFlashSaleResultsExist": "已存在關聯的閃購結果", + "EasyAbp.EShop.Plugins.FlashSales:FlashSaleResultStatusNotPending": "閃購結果的状态已經變更", + "Menu:FlashSalesManagement": "閃購管理", + "Permission:FlashSalePlan": "閃購計畫", + "Permission:Manage": "管理", + "Permission:Create": "新建", + "Permission:Update": "更新", + "Permission:Delete": "删除", + "Menu:FlashSalePlan": "閃購計畫", + "FlashSalePlan": "閃購計畫", + "FlashSalePlanStoreId": "店鋪ID", + "FlashSalePlanBeginTime": "開始時間", + "FlashSalePlanEndTime": "結束時間", + "FlashSalePlanProductId": "產品ID", + "FlashSalePlanProductSkuId": "產品SKU ID", + "FlashSalePlanIsPublished": "發佈", + "CreateFlashSalePlan": "新建", + "EditFlashSalePlan": "更新", + "FlashSalePlanDeletionConfirmationMessage": "確認删除閃購計畫 {0}?", + "SuccessfullyDeleted": "删除成功", + "Permission:FlashSaleResult": "閃購結果", + "Menu:FlashSaleResult": "閃購結果", + "FlashSaleResult": "閃購結果", + "FlashSaleResultStoreId": "店铺 ID", + "FlashSaleResultPlanId": "計畫 ID", + "FlashSaleResultStatus": "狀態", + "FlashSaleResultReason": "原因", + "FlashSaleResultUserId": "用戶 ID", + "FlashSaleResultOrderId": "訂單 ID", + "FlashSaleResultCreationTime": "創建時間", + "ViewFlashSaleResult": "查看", + "Enum:FlashSaleResultStatus.Pending": "等待中", + "Enum:FlashSaleResultStatus.Successful": "成功", + "Enum:FlashSaleResultStatus.Failed": "失敗", + "Enum:FlashSaleResultStatus.0": "等待中", + "Enum:FlashSaleResultStatus.1": "成功", + "Enum:FlashSaleResultStatus.2": "失敗" + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/FodyWeavers.xml b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/FodyWeavers.xml new file mode 100644 index 00000000..1715698c --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/FodyWeavers.xsd b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/FodyWeavers.xsd new file mode 100644 index 00000000..ffa6fc4b --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp.EShop.Plugins.FlashSales.Domain.abppkg.json b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp.EShop.Plugins.FlashSales.Domain.abppkg.json new file mode 100644 index 00000000..1d574efe --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp.EShop.Plugins.FlashSales.Domain.abppkg.json @@ -0,0 +1,3 @@ +{ + "role": "lib.domain" +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp.EShop.Plugins.FlashSales.Domain.csproj b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp.EShop.Plugins.FlashSales.Domain.csproj new file mode 100644 index 00000000..15aa2bfd --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp.EShop.Plugins.FlashSales.Domain.csproj @@ -0,0 +1,16 @@ + + + + + + net6.0 + + + + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesDomainAutoMapperProfile.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesDomainAutoMapperProfile.cs new file mode 100644 index 00000000..fed8b5cd --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesDomainAutoMapperProfile.cs @@ -0,0 +1,16 @@ +using AutoMapper; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +public class EShopPluginsFlashSalesDomainAutoMapperProfile : Profile +{ + public EShopPluginsFlashSalesDomainAutoMapperProfile() + { + /* You can configure your AutoMapper mapping configuration here. + * Alternatively, you can split your mapping configurations + * into multiple profile classes for a better organization. */ + CreateMap() + .MapExtraProperties(); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesDomainModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesDomainModule.cs new file mode 100644 index 00000000..f05ee3d6 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesDomainModule.cs @@ -0,0 +1,33 @@ +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AutoMapper; +using Volo.Abp.Domain; +using Volo.Abp.Domain.Entities.Events.Distributed; +using Volo.Abp.Modularity; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +[DependsOn( + typeof(AbpDddDomainModule), + typeof(AbpAutoMapperModule), + typeof(EShopPluginsFlashSalesDomainSharedModule) +)] +public class EShopPluginsFlashSalesDomainModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAutoMapperObjectMapper(); + + Configure(options => + { + options.AddMaps(validate: true); + }); + + Configure(options => + { + options.EtoMappings.Add(typeof(EShopPluginsFlashSalesDomainModule)); + + options.AutoEventSelectors.Add(); + }); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan.cs new file mode 100644 index 00000000..45d9f05e --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan.cs @@ -0,0 +1,59 @@ +using System; +using EasyAbp.EShop.Stores.Stores; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.MultiTenancy; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +public class FlashSalePlan : FullAuditedAggregateRoot, IMultiTenant, IMultiStore +{ + public virtual Guid? TenantId { get; protected set; } + + public virtual Guid StoreId { get; protected set; } + + public virtual DateTime BeginTime { get; set; } + + public virtual DateTime EndTime { get; set; } + + public virtual Guid ProductId { get; protected set; } + + public virtual Guid ProductSkuId { get; protected set; } + + public virtual bool IsPublished { get; protected set; } + + protected FlashSalePlan() + { + } + + public FlashSalePlan(Guid id, Guid? tenantId, Guid storeId, DateTime beginTime, DateTime endTime, Guid productId, Guid productSkuId, bool isPublished) + : base(id) + { + TenantId = tenantId; + SetTimeRange(beginTime, endTime); + SetProductSku(storeId, productId, productSkuId); + SetPublished(isPublished); + } + + public void SetTimeRange(DateTime beginTime, DateTime endTime) + { + if (beginTime > endTime) + { + throw new InvalidEndTimeException(); + } + + BeginTime = beginTime; + EndTime = endTime; + } + + public void SetProductSku(Guid storeId, Guid productId, Guid productSkuId) + { + StoreId = storeId; + ProductId = productId; + ProductSkuId = productSkuId; + } + + public void SetPublished(bool isPublished) + { + IsPublished = isPublished; + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/IFlashSalePlanRepository.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/IFlashSalePlanRepository.cs new file mode 100644 index 00000000..6fba405c --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/IFlashSalePlanRepository.cs @@ -0,0 +1,8 @@ +using System; +using Volo.Abp.Domain.Repositories; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +public interface IFlashSalePlanRepository : IRepository +{ +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/InvalidEndTimeException.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/InvalidEndTimeException.cs new file mode 100644 index 00000000..72ff19ef --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/InvalidEndTimeException.cs @@ -0,0 +1,12 @@ +using Volo.Abp; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +public class InvalidEndTimeException : BusinessException +{ + public InvalidEndTimeException() + : base(FlashSalesErrorCodes.InvalidEndTime) + { + + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/CreateFlashSaleOrderCompleteEventHandler.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/CreateFlashSaleOrderCompleteEventHandler.cs new file mode 100644 index 00000000..e4377fcd --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/CreateFlashSaleOrderCompleteEventHandler.cs @@ -0,0 +1,37 @@ +using System.Threading.Tasks; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.Guids; +using Volo.Abp.Uow; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; + +public class CreateFlashSaleOrderCompleteEventHandler : IDistributedEventHandler, ITransientDependency +{ + protected IFlashSaleResultRepository FlashSaleResultRepository { get; } + protected IGuidGenerator GuidGenerator { get; } + + public CreateFlashSaleOrderCompleteEventHandler(IFlashSaleResultRepository flashSaleResultRepository, IGuidGenerator guidGenerator) + { + FlashSaleResultRepository = flashSaleResultRepository; + GuidGenerator = guidGenerator; + } + + [UnitOfWork] + public virtual async Task HandleEventAsync(CreateFlashSaleOrderCompleteEto eventData) + { + var flashSaleResult = await FlashSaleResultRepository.GetAsync(eventData.PendingResultId); + + if (eventData.Success) + { + flashSaleResult.MarkAsSuccessful(eventData.OrderId.Value); + } + else + { + flashSaleResult.MarkAsFailed(eventData.Reason); + } + + await FlashSaleResultRepository.UpdateAsync(flashSaleResult, autoSave: true); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult.cs new file mode 100644 index 00000000..2617a14a --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult.cs @@ -0,0 +1,56 @@ +using System; +using EasyAbp.EShop.Stores.Stores; +using Volo.Abp; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.MultiTenancy; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; + +public class FlashSaleResult : FullAuditedAggregateRoot, IMultiTenant, IMultiStore +{ + public virtual Guid? TenantId { get; protected set; } + + public virtual Guid StoreId { get; protected set; } + + public virtual Guid PlanId { get; protected set; } + + public virtual FlashSaleResultStatus Status { get; protected set; } + + public virtual string Reason { get; protected set; } + + public virtual Guid UserId { get; protected set; } + + public virtual Guid? OrderId { get; protected set; } + + protected FlashSaleResult() { } + + public FlashSaleResult(Guid id, Guid? tenantId, Guid storeId, Guid planId, Guid userId) + : base(id) + { + TenantId = tenantId; + StoreId = storeId; + PlanId = planId; + Status = FlashSaleResultStatus.Pending; + UserId = userId; + } + + public void MarkAsSuccessful(Guid orderId) + { + if (Status != FlashSaleResultStatus.Pending) + { + throw new FlashSaleResultStatusNotPendingException(Id); + } + Status = FlashSaleResultStatus.Successful; + OrderId = orderId; + } + + public void MarkAsFailed(string reason) + { + if (Status != FlashSaleResultStatus.Pending) + { + throw new FlashSaleResultStatusNotPendingException(Id); + } + Status = FlashSaleResultStatus.Failed; + Reason = Check.NotNullOrEmpty(reason, nameof(reason)); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultStatusNotPendingException.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultStatusNotPendingException.cs new file mode 100644 index 00000000..9402b683 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultStatusNotPendingException.cs @@ -0,0 +1,12 @@ +using System; +using Volo.Abp; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; + +public class FlashSaleResultStatusNotPendingException : BusinessException +{ + public FlashSaleResultStatusNotPendingException(Guid resultId) : base(FlashSalesErrorCodes.FlashSaleResultStatusNotPending) + { + WithData(nameof(resultId), resultId); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/IFlashSaleResultRepository.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/IFlashSaleResultRepository.cs new file mode 100644 index 00000000..dc30ef1b --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/IFlashSaleResultRepository.cs @@ -0,0 +1,9 @@ +using System; +using Volo.Abp.Domain.Repositories; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; + +public interface IFlashSaleResultRepository : IRepository +{ + +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSalesDbProperties.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSalesDbProperties.cs new file mode 100644 index 00000000..909df743 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSalesDbProperties.cs @@ -0,0 +1,10 @@ +namespace EasyAbp.EShop.Plugins.FlashSales; + +public static class FlashSalesDbProperties +{ + public static string DbTablePrefix { get; set; } = "EasyAbpEShopPluginsFlashSales"; + + public static string DbSchema { get; set; } = null; + + public const string ConnectionStringName = "EasyAbpEShopPluginsFlashSales"; +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/Settings/FlashSalesSettingDefinitionProvider.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/Settings/FlashSalesSettingDefinitionProvider.cs new file mode 100644 index 00000000..817f807d --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/Settings/FlashSalesSettingDefinitionProvider.cs @@ -0,0 +1,13 @@ +using Volo.Abp.Settings; + +namespace EasyAbp.EShop.Plugins.FlashSales.Settings; + +public class FlashSalesSettingDefinitionProvider : SettingDefinitionProvider +{ + public override void Define(ISettingDefinitionContext context) + { + /* Define module settings here. + * Use names from FlashSalesSettings class. + */ + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/Settings/FlashSalesSettings.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/Settings/FlashSalesSettings.cs new file mode 100644 index 00000000..1cae4330 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/Settings/FlashSalesSettings.cs @@ -0,0 +1,10 @@ +namespace EasyAbp.EShop.Plugins.FlashSales.Settings; + +public static class FlashSalesSettings +{ + public const string GroupName = "FlashSales"; + + /* Add constants for setting names. Example: + * public const string MySettingName = GroupName + ".MySettingName"; + */ +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/FodyWeavers.xml b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/FodyWeavers.xml new file mode 100644 index 00000000..1715698c --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/FodyWeavers.xsd b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/FodyWeavers.xsd new file mode 100644 index 00000000..ffa6fc4b --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.abppkg.json b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.abppkg.json new file mode 100644 index 00000000..e1c64f01 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.abppkg.json @@ -0,0 +1,3 @@ +{ + "role": "lib.ef" +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.csproj b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.csproj new file mode 100644 index 00000000..84e4197c --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.csproj @@ -0,0 +1,15 @@ + + + + + + net6.0 + + + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/EntityFrameworkCore/EShopPluginsFlashSalesEntityFrameworkCoreModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/EntityFrameworkCore/EShopPluginsFlashSalesEntityFrameworkCoreModule.cs new file mode 100644 index 00000000..336d42e3 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/EntityFrameworkCore/EShopPluginsFlashSalesEntityFrameworkCoreModule.cs @@ -0,0 +1,23 @@ +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore; + +[DependsOn( + typeof(EShopPluginsFlashSalesDomainModule), + typeof(AbpEntityFrameworkCoreModule) +)] +public class EShopPluginsFlashSalesEntityFrameworkCoreModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAbpDbContext(options => + { + options.AddRepository(); + options.AddRepository(); + }); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/EntityFrameworkCore/FlashSalesDbContext.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/EntityFrameworkCore/FlashSalesDbContext.cs new file mode 100644 index 00000000..d9002f5c --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/EntityFrameworkCore/FlashSalesDbContext.cs @@ -0,0 +1,28 @@ +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; + +namespace EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore; + +[ConnectionStringName(FlashSalesDbProperties.ConnectionStringName)] +public class FlashSalesDbContext : AbpDbContext, IFlashSalesDbContext +{ + public DbSet FlashSalePlans { get; set; } + + public DbSet FlashSaleResults { get; set; } + + public FlashSalesDbContext(DbContextOptions options) + : base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.ConfigureEShopPluginsFlashSales(); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/EntityFrameworkCore/FlashSalesDbContextModelCreatingExtensions.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/EntityFrameworkCore/FlashSalesDbContextModelCreatingExtensions.cs new file mode 100644 index 00000000..f12853a7 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/EntityFrameworkCore/FlashSalesDbContextModelCreatingExtensions.cs @@ -0,0 +1,32 @@ +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; +using Microsoft.EntityFrameworkCore; +using Volo.Abp; +using Volo.Abp.EntityFrameworkCore.Modeling; + +namespace EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore; + +public static class FlashSalesDbContextModelCreatingExtensions +{ + public static void ConfigureEShopPluginsFlashSales( + this ModelBuilder builder) + { + Check.NotNull(builder, nameof(builder)); + + builder.Entity(b => + { + b.ToTable(FlashSalesDbProperties.DbTablePrefix + "FlashSalePlans", FlashSalesDbProperties.DbSchema); + b.ConfigureByConvention(); + + /* Configure more properties here */ + }); + + builder.Entity(b => + { + b.ToTable(FlashSalesDbProperties.DbTablePrefix + "FlashSaleResults", FlashSalesDbProperties.DbSchema); + b.ConfigureByConvention(); + + /* Configure more properties here */ + }); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/EntityFrameworkCore/IFlashSalesDbContext.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/EntityFrameworkCore/IFlashSalesDbContext.cs new file mode 100644 index 00000000..a0bfd185 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/EntityFrameworkCore/IFlashSalesDbContext.cs @@ -0,0 +1,15 @@ +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; + +namespace EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore; + +[ConnectionStringName(FlashSalesDbProperties.ConnectionStringName)] +public interface IFlashSalesDbContext : IEfCoreDbContext +{ + DbSet FlashSalePlans { get; set; } + + DbSet FlashSaleResults { get; set; } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/EfCoreFlashSalePlanRepository.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/EfCoreFlashSalePlanRepository.cs new file mode 100644 index 00000000..b78bffbd --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/EfCoreFlashSalePlanRepository.cs @@ -0,0 +1,21 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans +{ + public class EfCoreFlashSalePlanRepository : EfCoreRepository, IFlashSalePlanRepository + { + public EfCoreFlashSalePlanRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } + + public override async Task> WithDetailsAsync() + { + return (await GetQueryableAsync()).IncludeDetails(); + } + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanEfCoreQuerableExtensions.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanEfCoreQuerableExtensions.cs new file mode 100644 index 00000000..cbbad1a8 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanEfCoreQuerableExtensions.cs @@ -0,0 +1,18 @@ +using System.Linq; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +public static class FlashSalePlanEfCoreQueryableExtensions +{ + public static IQueryable IncludeDetails(this IQueryable queryable, bool include = true) + { + if (!include) + { + return queryable; + } + + return queryable + // .Include(x => x.xxx) // TODO: AbpHelper generated + ; + } +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/EfCoreFlashSaleResultRepository.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/EfCoreFlashSaleResultRepository.cs new file mode 100644 index 00000000..937ecdf5 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/EfCoreFlashSaleResultRepository.cs @@ -0,0 +1,20 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; + +public class EfCoreFlashSaleResultRepository : EfCoreRepository, IFlashSaleResultRepository +{ + public EfCoreFlashSaleResultRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } + + public override async Task> WithDetailsAsync() + { + return (await GetQueryableAsync()).IncludeDetails(); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultEfCoreQuerableExtensions.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultEfCoreQuerableExtensions.cs new file mode 100644 index 00000000..ef21cde9 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultEfCoreQuerableExtensions.cs @@ -0,0 +1,16 @@ +using System.Linq; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; + +public static class FlashSaleResultEfCoreQuerableExtensions +{ + public static IQueryable IncludeDetails(this IQueryable queryable, bool include = true) + { + if (!include) + { + return queryable; + } + + return queryable; + } +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/FodyWeavers.xml b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/FodyWeavers.xml new file mode 100644 index 00000000..1715698c --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/FodyWeavers.xsd b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/FodyWeavers.xsd new file mode 100644 index 00000000..ffa6fc4b --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.abppkg.json b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.abppkg.json new file mode 100644 index 00000000..7deef5e3 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.abppkg.json @@ -0,0 +1,3 @@ +{ + "role": "lib.http-api-client" +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.csproj b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.csproj new file mode 100644 index 00000000..918f6179 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.csproj @@ -0,0 +1,20 @@ + + + + + + netstandard2.1 + + + + + + + + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesHttpApiClientModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesHttpApiClientModule.cs new file mode 100644 index 00000000..07577fda --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesHttpApiClientModule.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Http.Client; +using Volo.Abp.Modularity; +using Volo.Abp.VirtualFileSystem; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +[DependsOn( + typeof(EShopPluginsFlashSalesApplicationContractsModule), + typeof(AbpHttpClientModule))] +public class EShopPluginsFlashSalesHttpApiClientModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddHttpClientProxies( + typeof(EShopPluginsFlashSalesApplicationContractsModule).Assembly, + EShopPluginsFlashSalesRemoteServiceConsts.RemoteServiceName + ); + + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client/FodyWeavers.xml b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client/FodyWeavers.xml new file mode 100644 index 00000000..1715698c --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client/FodyWeavers.xsd b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client/FodyWeavers.xsd new file mode 100644 index 00000000..ffa6fc4b --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp.EShop.Plugins.FlashSales.HttpApi.abppkg.json b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp.EShop.Plugins.FlashSales.HttpApi.abppkg.json new file mode 100644 index 00000000..515bfe64 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp.EShop.Plugins.FlashSales.HttpApi.abppkg.json @@ -0,0 +1,3 @@ +{ + "role": "lib.http-api" +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp.EShop.Plugins.FlashSales.HttpApi.csproj b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp.EShop.Plugins.FlashSales.HttpApi.csproj new file mode 100644 index 00000000..416585c3 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp.EShop.Plugins.FlashSales.HttpApi.csproj @@ -0,0 +1,15 @@ + + + + + + net6.0 + + + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesHttpApiModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesHttpApiModule.cs new file mode 100644 index 00000000..b3c4a90d --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesHttpApiModule.cs @@ -0,0 +1,32 @@ +using Localization.Resources.AbpUi; +using EasyAbp.EShop.Plugins.FlashSales.Localization; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; +using Microsoft.Extensions.DependencyInjection; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +[DependsOn( + typeof(EShopPluginsFlashSalesApplicationContractsModule), + typeof(AbpAspNetCoreMvcModule))] +public class EShopPluginsFlashSalesHttpApiModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + PreConfigure(mvcBuilder => + { + mvcBuilder.AddApplicationPartIfNotExists(typeof(EShopPluginsFlashSalesHttpApiModule).Assembly); + }); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.Resources + .Get() + .AddBaseTypes(typeof(AbpUiResource)); + }); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanController.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanController.cs new file mode 100644 index 00000000..bd18e94e --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanController.cs @@ -0,0 +1,65 @@ +using System; +using System.Threading.Tasks; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.Dtos; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp; +using Volo.Abp.Application.Dtos; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +[Area(EShopPluginsFlashSalesRemoteServiceConsts.ModuleName)] +[RemoteService(Name = EShopPluginsFlashSalesRemoteServiceConsts.RemoteServiceName)] +[Route("/api/e-shop/plugins/flash-sales/flash-sale-plan")] +public class FlashSalePlanController : + FlashSalesController, + IFlashSalePlanAppService +{ + protected IFlashSalePlanAppService Service { get; } + + public FlashSalePlanController(IFlashSalePlanAppService flashSalePlanAppService) + { + Service = flashSalePlanAppService; + } + + [HttpGet("{id}")] + public virtual Task GetAsync(Guid id) + { + return Service.GetAsync(id); + } + + [HttpGet] + public virtual Task> GetListAsync(FlashSalePlanGetListInput input) + { + return Service.GetListAsync(input); + } + + [HttpPost("{id}")] + public virtual Task CreateAsync(FlashSalePlanCreateDto input) + { + return Service.CreateAsync(input); + } + + [HttpPut("{id}")] + public virtual Task UpdateAsync(Guid id, FlashSalePlanUpdateDto input) + { + return Service.UpdateAsync(id, input); + } + + [HttpDelete("{id}")] + public virtual Task DeleteAsync(Guid id) + { + return Service.DeleteAsync(id); + } + + [HttpPost("{id}/pre-order")] + public virtual Task PreOrderAsync(Guid id) + { + return Service.PreOrderAsync(id); + } + + [HttpPost("{id}/order")] + public virtual Task OrderAsync(Guid id, CreateOrderInput input) + { + return Service.OrderAsync(id, input); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultController.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultController.cs new file mode 100644 index 00000000..b0095dcc --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultController.cs @@ -0,0 +1,33 @@ +using System; +using System.Threading.Tasks; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults.Dtos; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp; +using Volo.Abp.Application.Dtos; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; + +[Area(EShopPluginsFlashSalesRemoteServiceConsts.ModuleName)] +[RemoteService(Name = EShopPluginsFlashSalesRemoteServiceConsts.RemoteServiceName)] +[Route("/api/e-shop/plugins/flash-sales/flash-sale-result")] +public class FlashSaleResultController : FlashSalesController, IFlashSaleResultAppService +{ + protected IFlashSaleResultAppService Service { get; } + + public FlashSaleResultController(IFlashSaleResultAppService service) + { + Service = service; + } + + [HttpGet("{id}")] + public virtual Task GetAsync(Guid id) + { + return Service.GetAsync(id); + } + + [HttpGet] + public virtual Task> GetListAsync(FlashSaleResultGetListInput input) + { + return Service.GetListAsync(input); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Plugins/FlashSales/FlashSalesController.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Plugins/FlashSales/FlashSalesController.cs new file mode 100644 index 00000000..f087a9c7 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Plugins/FlashSales/FlashSalesController.cs @@ -0,0 +1,12 @@ +using EasyAbp.EShop.Plugins.FlashSales.Localization; +using Volo.Abp.AspNetCore.Mvc; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +public abstract class FlashSalesController : AbpControllerBase +{ + protected FlashSalesController() + { + LocalizationResource = typeof(FlashSalesResource); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/FodyWeavers.xml b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/FodyWeavers.xml new file mode 100644 index 00000000..1715698c --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/FodyWeavers.xsd b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/FodyWeavers.xsd new file mode 100644 index 00000000..ffa6fc4b --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Installer/EasyAbp.EShop.Plugins.FlashSales.Installer.csproj b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Installer/EasyAbp.EShop.Plugins.FlashSales.Installer.csproj new file mode 100644 index 00000000..60bb3626 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Installer/EasyAbp.EShop.Plugins.FlashSales.Installer.csproj @@ -0,0 +1,21 @@ + + + + + + net6.0 + true + EasyAbp.EShop.Plugins.FlashSales + + + + + + + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Installer/FlashSalesInstallerModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Installer/FlashSalesInstallerModule.cs new file mode 100644 index 00000000..619b2d4b --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Installer/FlashSalesInstallerModule.cs @@ -0,0 +1,20 @@ +using Volo.Abp.Studio; +using Volo.Abp.Modularity; +using Volo.Abp.VirtualFileSystem; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +[DependsOn( + typeof(AbpStudioModuleInstallerModule), + typeof(AbpVirtualFileSystemModule) + )] +public class FlashSalesInstallerModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Installer/FlashSalesInstallerPipelineBuilder.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Installer/FlashSalesInstallerPipelineBuilder.cs new file mode 100644 index 00000000..56e7fd3f --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Installer/FlashSalesInstallerPipelineBuilder.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Studio.ModuleInstalling; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +[Dependency(ServiceLifetime.Transient, ReplaceServices = true)] +[ExposeServices(typeof(IModuleInstallingPipelineBuilder))] +public class FlashSalesInstallerPipelineBuilder : ModuleInstallingPipelineBuilderBase, IModuleInstallingPipelineBuilder, ITransientDependency +{ + public async Task BuildAsync(ModuleInstallingContext context) + { + return GetBasePipeline(context); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Installer/FodyWeavers.xml b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Installer/FodyWeavers.xml new file mode 100644 index 00000000..be0de3a9 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Installer/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Installer/FodyWeavers.xsd b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Installer/FodyWeavers.xsd new file mode 100644 index 00000000..3f3946e2 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Installer/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp.EShop.Plugins.FlashSales.MongoDB.abppkg.json b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp.EShop.Plugins.FlashSales.MongoDB.abppkg.json new file mode 100644 index 00000000..8b23fd1e --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp.EShop.Plugins.FlashSales.MongoDB.abppkg.json @@ -0,0 +1,3 @@ +{ + "role": "lib.mongodb" +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp.EShop.Plugins.FlashSales.MongoDB.csproj b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp.EShop.Plugins.FlashSales.MongoDB.csproj new file mode 100644 index 00000000..3900b394 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp.EShop.Plugins.FlashSales.MongoDB.csproj @@ -0,0 +1,15 @@ + + + + + + net6.0 + + + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/MongoFlashSalePlanRepository.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/MongoFlashSalePlanRepository.cs new file mode 100644 index 00000000..4e0c476b --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/MongoFlashSalePlanRepository.cs @@ -0,0 +1,13 @@ +using System; +using EasyAbp.EShop.Plugins.FlashSales.MongoDB; +using Volo.Abp.Domain.Repositories.MongoDB; +using Volo.Abp.MongoDB; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +public class MongoFlashSalePlanRepository : MongoDbRepository, IFlashSalePlanRepository +{ + public MongoFlashSalePlanRepository(IMongoDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/MongoFlashSaleResultRepository.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/MongoFlashSaleResultRepository.cs new file mode 100644 index 00000000..fc8a74a2 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/MongoFlashSaleResultRepository.cs @@ -0,0 +1,13 @@ +using System; +using EasyAbp.EShop.Plugins.FlashSales.MongoDB; +using Volo.Abp.Domain.Repositories.MongoDB; +using Volo.Abp.MongoDB; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; + +public class MongoFlashSaleResultRepository : MongoDbRepository, IFlashSaleResultRepository +{ + public MongoFlashSaleResultRepository(IMongoDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp/EShop/Plugins/FlashSales/MongoDB/EShopPluginsFlashSalesMongoDbModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp/EShop/Plugins/FlashSales/MongoDB/EShopPluginsFlashSalesMongoDbModule.cs new file mode 100644 index 00000000..fcfceade --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp/EShop/Plugins/FlashSales/MongoDB/EShopPluginsFlashSalesMongoDbModule.cs @@ -0,0 +1,23 @@ +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; +using Volo.Abp.MongoDB; + +namespace EasyAbp.EShop.Plugins.FlashSales.MongoDB; + +[DependsOn( + typeof(EShopPluginsFlashSalesDomainModule), + typeof(AbpMongoDbModule) + )] +public class EShopPluginsFlashSalesMongoDbModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddMongoDbContext(options => + { + options.AddRepository(); + options.AddRepository(); + }); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp/EShop/Plugins/FlashSales/MongoDB/FlashSalesMongoDbContext.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp/EShop/Plugins/FlashSales/MongoDB/FlashSalesMongoDbContext.cs new file mode 100644 index 00000000..b7dfc7a2 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp/EShop/Plugins/FlashSales/MongoDB/FlashSalesMongoDbContext.cs @@ -0,0 +1,22 @@ +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; +using MongoDB.Driver; +using Volo.Abp.Data; +using Volo.Abp.MongoDB; + +namespace EasyAbp.EShop.Plugins.FlashSales.MongoDB; + +[ConnectionStringName(FlashSalesDbProperties.ConnectionStringName)] +public class FlashSalesMongoDbContext : AbpMongoDbContext, IFlashSalesMongoDbContext +{ + public IMongoCollection FlashSalePlans => Collection(); + + public IMongoCollection FlashSaleResults => Collection(); + + protected override void CreateModel(IMongoModelBuilder modelBuilder) + { + base.CreateModel(modelBuilder); + + modelBuilder.ConfigureFlashSales(); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp/EShop/Plugins/FlashSales/MongoDB/FlashSalesMongoDbContextExtensions.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp/EShop/Plugins/FlashSales/MongoDB/FlashSalesMongoDbContextExtensions.cs new file mode 100644 index 00000000..e3d2201a --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp/EShop/Plugins/FlashSales/MongoDB/FlashSalesMongoDbContextExtensions.cs @@ -0,0 +1,19 @@ +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; +using Volo.Abp; +using Volo.Abp.MongoDB; + +namespace EasyAbp.EShop.Plugins.FlashSales.MongoDB; + +public static class FlashSalesMongoDbContextExtensions +{ + public static void ConfigureFlashSales( + this IMongoModelBuilder builder) + { + Check.NotNull(builder, nameof(builder)); + + builder.Entity(b => b.CollectionName = FlashSalesDbProperties.DbTablePrefix + "FlashSalePlans"); + + builder.Entity(b => b.CollectionName = FlashSalesDbProperties.DbTablePrefix + "FlashSaleResults"); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp/EShop/Plugins/FlashSales/MongoDB/IFlashSalesMongoDbContext.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp/EShop/Plugins/FlashSales/MongoDB/IFlashSalesMongoDbContext.cs new file mode 100644 index 00000000..6a4c7951 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/EasyAbp/EShop/Plugins/FlashSales/MongoDB/IFlashSalesMongoDbContext.cs @@ -0,0 +1,15 @@ +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; +using MongoDB.Driver; +using Volo.Abp.Data; +using Volo.Abp.MongoDB; + +namespace EasyAbp.EShop.Plugins.FlashSales.MongoDB; + +[ConnectionStringName(FlashSalesDbProperties.ConnectionStringName)] +public interface IFlashSalesMongoDbContext : IAbpMongoDbContext +{ + IMongoCollection FlashSalePlans { get; } + + IMongoCollection FlashSaleResults { get; } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/FodyWeavers.xml b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/FodyWeavers.xml new file mode 100644 index 00000000..1715698c --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/FodyWeavers.xsd b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/FodyWeavers.xsd new file mode 100644 index 00000000..ffa6fc4b --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.MongoDB/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/EShopPluginsFlashSalesWebAutoMapperProfile.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/EShopPluginsFlashSalesWebAutoMapperProfile.cs new file mode 100644 index 00000000..5a88342e --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/EShopPluginsFlashSalesWebAutoMapperProfile.cs @@ -0,0 +1,25 @@ +using AutoMapper; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.Dtos; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults.Dtos; +using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalePlans.FlashSalePlan.ViewModels; +using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSaleResults.FlashSaleResult.ViewModels; + +namespace EasyAbp.EShop.Plugins.FlashSales.Web; + +public class EShopPluginsFlashSalesWebAutoMapperProfile : Profile +{ + public EShopPluginsFlashSalesWebAutoMapperProfile() + { + /* You can configure your AutoMapper mapping configuration here. + * Alternatively, you can split your mapping configurations + * into multiple profile classes for a better organization. */ + CreateMap(MemberList.Destination) + .ForMember(dest => dest.ExtraProperties, opt => opt.Ignore()); + CreateMap(MemberList.Destination) + .ForMember(dest => dest.ExtraProperties, opt => opt.Ignore()); + CreateMap(MemberList.Destination) + .ForSourceMember(dest => dest.ExtraProperties, opt => opt.DoNotValidate()); + CreateMap(MemberList.Destination) + .ForSourceMember(dest => dest.ExtraProperties, opt => opt.DoNotValidate()); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/EShopPluginsFlashSalesWebModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/EShopPluginsFlashSalesWebModule.cs new file mode 100644 index 00000000..443dc336 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/EShopPluginsFlashSalesWebModule.cs @@ -0,0 +1,58 @@ +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.DependencyInjection; +using EasyAbp.EShop.Plugins.FlashSales.Localization; +using EasyAbp.EShop.Plugins.FlashSales.Web.Menus; +using Volo.Abp.AspNetCore.Mvc.Localization; +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; +using Volo.Abp.AutoMapper; +using Volo.Abp.Modularity; +using Volo.Abp.UI.Navigation; +using Volo.Abp.VirtualFileSystem; +using EasyAbp.EShop.Plugins.FlashSales.Permissions; + +namespace EasyAbp.EShop.Plugins.FlashSales.Web; + +[DependsOn( + typeof(EShopPluginsFlashSalesApplicationContractsModule), + typeof(AbpAspNetCoreMvcUiThemeSharedModule), + typeof(AbpAutoMapperModule) + )] +public class EShopPluginsFlashSalesWebModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + context.Services.PreConfigure(options => + { + options.AddAssemblyResource(typeof(FlashSalesResource), typeof(EShopPluginsFlashSalesWebModule).Assembly); + }); + + PreConfigure(mvcBuilder => + { + mvcBuilder.AddApplicationPartIfNotExists(typeof(EShopPluginsFlashSalesWebModule).Assembly); + }); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.MenuContributors.Add(new FlashSalesMenuContributor()); + }); + + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + + context.Services.AddAutoMapperObjectMapper(); + Configure(options => + { + options.AddMaps(validate: true); + }); + + Configure(options => + { + //Configure authorization. + }); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/EasyAbp.EShop.Plugins.FlashSales.Web.abppkg.json b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/EasyAbp.EShop.Plugins.FlashSales.Web.abppkg.json new file mode 100644 index 00000000..930c4018 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/EasyAbp.EShop.Plugins.FlashSales.Web.abppkg.json @@ -0,0 +1,3 @@ +{ + "role": "lib.mvc" +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/EasyAbp.EShop.Plugins.FlashSales.Web.csproj b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/EasyAbp.EShop.Plugins.FlashSales.Web.csproj new file mode 100644 index 00000000..cedf9892 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/EasyAbp.EShop.Plugins.FlashSales.Web.csproj @@ -0,0 +1,40 @@ + + + + + + net6.0 + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + true + Library + EasyAbp.EShop.Plugins.FlashSales.Web + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/FodyWeavers.xml b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/FodyWeavers.xml new file mode 100644 index 00000000..1715698c --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/FodyWeavers.xsd b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/FodyWeavers.xsd new file mode 100644 index 00000000..ffa6fc4b --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Menus/FlashSalesMenuContributor.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Menus/FlashSalesMenuContributor.cs new file mode 100644 index 00000000..50d1a707 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Menus/FlashSalesMenuContributor.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using EasyAbp.EShop.Plugins.FlashSales.Localization; +using EasyAbp.EShop.Plugins.FlashSales.Permissions; +using Volo.Abp.UI.Navigation; + +namespace EasyAbp.EShop.Plugins.FlashSales.Web.Menus; + +public class FlashSalesMenuContributor : IMenuContributor +{ + public async Task ConfigureMenuAsync(MenuConfigurationContext context) + { + if (context.Menu.Name == StandardMenus.Main) + { + await ConfigureMainMenuAsync(context); + } + } + + private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) + { + var l = context.GetLocalizer(); + //Add main menu items. + var flashSalesManagementMenuItem = new ApplicationMenuItem(FlashSalesMenus.Prefix, l["Menu:FlashSalesManagement"]); + + if (await context.IsGrantedAsync(FlashSalesPermissions.FlashSalePlan.Default)) + { + flashSalesManagementMenuItem.AddItem( + new ApplicationMenuItem( + FlashSalesMenus.FlashSalePlan, + l["Menu:FlashSalePlan"], + "/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan" + ) + ); + } + + if (await context.IsGrantedAsync(FlashSalesPermissions.FlashSaleResult.Default)) + { + flashSalesManagementMenuItem.AddItem( + new ApplicationMenuItem( + FlashSalesMenus.FlashSaleResult, + l["Menu:FlashSaleResult"], + "/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult" + ) + ); + } + + if (!flashSalesManagementMenuItem.Items.IsNullOrEmpty()) + { + var eShopMenuItem = context.Menu.Items.GetOrAdd(i => i.Name == FlashSalesMenus.ModuleGroupPrefix, + () => new ApplicationMenuItem(FlashSalesMenus.ModuleGroupPrefix, l["Menu:EasyAbpEShop"], icon: "fa fa-shopping-bag")); + + eShopMenuItem.Items.Add(flashSalesManagementMenuItem); + } + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Menus/FlashSalesMenus.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Menus/FlashSalesMenus.cs new file mode 100644 index 00000000..e792e1aa --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Menus/FlashSalesMenus.cs @@ -0,0 +1,12 @@ +namespace EasyAbp.EShop.Plugins.FlashSales.Web.Menus; + +public static class FlashSalesMenus +{ + public const string ModuleGroupPrefix = "EasyAbp.EShop"; + + public const string Prefix = ModuleGroupPrefix + ".Plugins.FlashSales"; + + public const string FlashSalePlan = Prefix + ".FlashSalePlan"; + + public const string FlashSaleResult = Prefix + ".FlashSaleResult"; +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/CreateModal.cshtml b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/CreateModal.cshtml new file mode 100644 index 00000000..8e86f4a7 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/CreateModal.cshtml @@ -0,0 +1,21 @@ +@page +@using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@using EasyAbp.EShop.Plugins.FlashSales.Localization +@using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalePlans.FlashSalePlan + +@inject IHtmlLocalizer L +@model CreateModalModel +@{ + Layout = null; +} + + + + + + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/CreateModal.cshtml.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/CreateModal.cshtml.cs new file mode 100644 index 00000000..08eab7da --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/CreateModal.cshtml.cs @@ -0,0 +1,27 @@ +using System.Threading.Tasks; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.Dtos; +using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalePlans.FlashSalePlan.ViewModels; +using Microsoft.AspNetCore.Mvc; + +namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalePlans.FlashSalePlan; + +public class CreateModalModel : FlashSalesPageModel +{ + [BindProperty] + public CreateFlashSalePlanViewModel ViewModel { get; set; } + + protected IFlashSalePlanAppService Service { get; } + + public CreateModalModel(IFlashSalePlanAppService service) + { + Service = service; + } + + public virtual async Task OnPostAsync() + { + var dto = ObjectMapper.Map(ViewModel); + await Service.CreateAsync(dto); + return NoContent(); + } +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/EditModal.cshtml b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/EditModal.cshtml new file mode 100644 index 00000000..356d19dd --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/EditModal.cshtml @@ -0,0 +1,22 @@ +@page +@using EasyAbp.EShop.Plugins.FlashSales.Localization +@using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalePlans.FlashSalePlan + +@inject IHtmlLocalizer L +@model EditModalModel +@{ + Layout = null; +} + + + + + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/EditModal.cshtml.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/EditModal.cshtml.cs new file mode 100644 index 00000000..faa08b8e --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/EditModal.cshtml.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.Dtos; +using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalePlans.FlashSalePlan.ViewModels; +using Microsoft.AspNetCore.Mvc; + +namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalePlans.FlashSalePlan; + +public class EditModalModel : FlashSalesPageModel +{ + [HiddenInput] + [BindProperty(SupportsGet = true)] + public Guid Id { get; set; } + + [BindProperty] + public EditFlashSalePlanViewModel ViewModel { get; set; } + + protected IFlashSalePlanAppService Service { get; } + + public EditModalModel(IFlashSalePlanAppService service) + { + Service = service; + } + + public virtual async Task OnGetAsync() + { + var dto = await Service.GetAsync(Id); + ViewModel = ObjectMapper.Map(dto); + } + + public virtual async Task OnPostAsync() + { + var dto = ObjectMapper.Map(ViewModel); + await Service.UpdateAsync(Id, dto); + return NoContent(); + } +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/Index.cshtml b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/Index.cshtml new file mode 100644 index 00000000..ece592b6 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/Index.cshtml @@ -0,0 +1,50 @@ +@page +@using EasyAbp.EShop.Plugins.FlashSales.Permissions +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.AspNetCore.Mvc.UI.Layout +@using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalePlans.FlashSalePlan +@using EasyAbp.EShop.Plugins.FlashSales.Localization +@using EasyAbp.EShop.Plugins.FlashSales.Web.Menus + +@model IndexModel + +@inject IPageLayout PageLayout +@inject IHtmlLocalizer L +@inject IAuthorizationService Authorization +@{ + PageLayout.Content.Title = L["FlashSalePlan"].Value; + PageLayout.Content.BreadCrumb.Add(L["Menu:FlashSalePlan"].Value); + PageLayout.Content.MenuItemName = FlashSalesMenus.FlashSalePlan; +} + +@section scripts +{ + +} +@section styles +{ + +} + + + + + + @L["FlashSalePlan"] + + + @if (await Authorization.IsGrantedAsync(FlashSalesPermissions.FlashSalePlan.Create)) + { + + } + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/Index.cshtml.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/Index.cshtml.cs new file mode 100644 index 00000000..c45df6d6 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/Index.cshtml.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; + +namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalePlans.FlashSalePlan; + +public class IndexModel : FlashSalesPageModel +{ + public virtual async Task OnGetAsync() + { + await Task.CompletedTask; + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/ViewModels/CreateFlashSalePlanViewModel.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/ViewModels/CreateFlashSalePlanViewModel.cs new file mode 100644 index 00000000..8f167e89 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/ViewModels/CreateFlashSalePlanViewModel.cs @@ -0,0 +1,25 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalePlans.FlashSalePlan.ViewModels; + +public class CreateFlashSalePlanViewModel +{ + [Display(Name = "FlashSalePlanStoreId")] + public Guid StoreId { get; set; } + + [Display(Name = "FlashSalePlanBeginTime")] + public DateTime BeginTime { get; set; } + + [Display(Name = "FlashSalePlanEndTime")] + public DateTime EndTime { get; set; } + + [Display(Name = "FlashSalePlanProductId")] + public Guid ProductId { get; set; } + + [Display(Name = "FlashSalePlanProductSkuId")] + public Guid ProductSkuId { get; set; } + + [Display(Name = "FlashSalePlanIsPublished")] + public bool IsPublished { get; set; } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/ViewModels/EditFlashSalePlanViewModel.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/ViewModels/EditFlashSalePlanViewModel.cs new file mode 100644 index 00000000..a7cab334 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/ViewModels/EditFlashSalePlanViewModel.cs @@ -0,0 +1,33 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; +using Volo.Abp.Domain.Entities; + +namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSalePlans.FlashSalePlan.ViewModels; + +public class EditFlashSalePlanViewModel : IHasConcurrencyStamp +{ + [DisabledInput] + [ReadOnlyInput] + [Display(Name = "FlashSalePlanStoreId")] + public Guid StoreId { get; set; } + + [Display(Name = "FlashSalePlanBeginTime")] + public DateTime BeginTime { get; set; } + + [Display(Name = "FlashSalePlanEndTime")] + public DateTime EndTime { get; set; } + + [Display(Name = "FlashSalePlanProductId")] + public Guid ProductId { get; set; } + + [Display(Name = "FlashSalePlanProductSkuId")] + public Guid ProductSkuId { get; set; } + + [Display(Name = "FlashSalePlanIsPublished")] + public bool IsPublished { get; set; } + + [HiddenInput] + public string ConcurrencyStamp { get; set; } +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/index.css b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/index.css new file mode 100644 index 00000000..e69de29b diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/index.js b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/index.js new file mode 100644 index 00000000..ff9c2d89 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/index.js @@ -0,0 +1,93 @@ +$(function () { + + var l = abp.localization.getResource('EasyAbpEShopPluginsFlashSales'); + + var service = easyAbp.eShop.plugins.flashSales.flashSalePlans.flashSalePlan; + var createModal = new abp.ModalManager(abp.appPath + 'EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/CreateModal'); + var editModal = new abp.ModalManager(abp.appPath + 'EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlan/EditModal'); + + var dataTable = $('#FlashSalePlanTable').DataTable(abp.libs.datatables.normalizeConfiguration({ + processing: true, + serverSide: true, + paging: true, + searching: false, + autoWidth: false, + scrollCollapse: true, + order: [[2, "asc"]], + ajax: abp.libs.datatables.createAjax(service.getList, function () { + return { + includeUnpublished: abp.auth.isGranted('EasyAbp.EShop.Plugins.FlashSales.FlashSalePlan.Manage') + }; + }), + columnDefs: [ + { + rowAction: { + items: + [ + { + text: l('Edit'), + visible: abp.auth.isGranted('EasyAbp.EShop.Plugins.FlashSales.FlashSalePlan.Update'), + action: function (data) { + editModal.open({ id: data.record.id }); + } + }, + { + text: l('Delete'), + visible: abp.auth.isGranted('EasyAbp.EShop.Plugins.FlashSales.FlashSalePlan.Delete'), + confirmMessage: function (data) { + return l('FlashSalePlanDeletionConfirmationMessage', data.record.id); + }, + action: function (data) { + service.delete(data.record.id) + .then(function () { + abp.notify.info(l('SuccessfullyDeleted')); + dataTable.ajax.reload(); + }); + } + } + ] + } + }, + { + title: l('FlashSalePlanStoreId'), + data: "storeId" + }, + { + title: l('FlashSalePlanBeginTime'), + data: "beginTime", + dataFormat: 'datetime' + }, + { + title: l('FlashSalePlanEndTime'), + data: "endTime", + dataFormat: 'datetime' + }, + { + title: l('FlashSalePlanProductId'), + data: "productId" + }, + { + title: l('FlashSalePlanProductSkuId'), + data: "productSkuId" + }, + { + title: l('FlashSalePlanIsPublished'), + data: "isPublished", + dataFormat: 'boolean' + }, + ] + })); + + createModal.onResult(function () { + dataTable.ajax.reload(); + }); + + editModal.onResult(function () { + dataTable.ajax.reload(); + }); + + $('#NewFlashSalePlanButton').click(function (e) { + e.preventDefault(); + createModal.open(); + }); +}); diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/Index.cshtml b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/Index.cshtml new file mode 100644 index 00000000..88d70cfa --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/Index.cshtml @@ -0,0 +1,41 @@ +@page +@using EasyAbp.EShop.Plugins.FlashSales.Permissions +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.AspNetCore.Mvc.UI.Layout +@using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSaleResults.FlashSaleResult +@using EasyAbp.EShop.Plugins.FlashSales.Localization +@using EasyAbp.EShop.Plugins.FlashSales.Web.Menus + +@model IndexModel + +@inject IPageLayout PageLayout +@inject IHtmlLocalizer L +@inject IAuthorizationService Authorization +@{ + PageLayout.Content.Title = L["FlashSaleResult"].Value; + PageLayout.Content.BreadCrumb.Add(L["Menu:FlashSaleResult"].Value); + PageLayout.Content.MenuItemName = FlashSalesMenus.FlashSaleResult; +} + +@section scripts +{ + +} +@section styles +{ + +} + + + + + + @L["FlashSaleResult"] + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/Index.cshtml.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/Index.cshtml.cs new file mode 100644 index 00000000..def3ff36 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/Index.cshtml.cs @@ -0,0 +1,11 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSaleResults.FlashSaleResult; + +public class IndexModel : PageModel +{ + public void OnGet() + { + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/ViewModal.cshtml b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/ViewModal.cshtml new file mode 100644 index 00000000..789f6864 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/ViewModal.cshtml @@ -0,0 +1,22 @@ +@page +@using EasyAbp.EShop.Plugins.FlashSales.Localization +@using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSaleResults.FlashSaleResult + +@inject IHtmlLocalizer L +@model ViewModalModel +@{ + Layout = null; +} + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/ViewModal.cshtml.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/ViewModal.cshtml.cs new file mode 100644 index 00000000..40d67ff5 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/ViewModal.cshtml.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading.Tasks; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults.Dtos; +using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSaleResults.FlashSaleResult.ViewModels; +using Microsoft.AspNetCore.Mvc; + +namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSaleResults.FlashSaleResult; + +public class ViewModalModel : FlashSalesPageModel +{ + [HiddenInput] + [BindProperty(SupportsGet = true)] + public Guid Id { get; set; } + + [BindProperty] + public ViewFlashSaleResultViewModel ViewModel { get; set; } + + protected IFlashSaleResultAppService Service { get; } + + public ViewModalModel(IFlashSaleResultAppService service) + { + Service = service; + } + + public virtual async Task OnGetAsync() + { + var dto = await Service.GetAsync(Id); + ViewModel = ObjectMapper.Map(dto); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/ViewModels/ViewFlashSaleResultViewModel.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/ViewModels/ViewFlashSaleResultViewModel.cs new file mode 100644 index 00000000..49b9aae1 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/ViewModels/ViewFlashSaleResultViewModel.cs @@ -0,0 +1,26 @@ +using System; +using System.ComponentModel.DataAnnotations; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; + +namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.FlashSaleResults.FlashSaleResult.ViewModels; + +public class ViewFlashSaleResultViewModel +{ + [Display(Name = "FlashSaleResultStoreId")] + public Guid StoreId { get; set; } + + [Display(Name = "FlashSaleResultPlanId")] + public Guid PlanId { get; set; } + + [Display(Name = "FlashSaleResultStatus")] + public FlashSaleResultStatus Status { get; set; } + + [Display(Name = "FlashSaleResultReason")] + public string Reason { get; set; } + + [Display(Name = "FlashSaleResultUserId")] + public Guid UserId { get; set; } + + [Display(Name = "FlashSaleResultOrderId")] + public Guid? OrderId { get; set; } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/index.css b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/index.css new file mode 100644 index 00000000..e69de29b diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/index.js b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/index.js new file mode 100644 index 00000000..aac69835 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/index.js @@ -0,0 +1,69 @@ +$(function () { + + var l = abp.localization.getResource('EasyAbpEShopPluginsFlashSales'); + + var service = easyAbp.eShop.plugins.flashSales.flashSaleResults.flashSaleResult; + var viewModal = new abp.ModalManager(abp.appPath + 'EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult/ViewModal'); + + var dataTable = $('#FlashSaleResultTable').DataTable(abp.libs.datatables.normalizeConfiguration({ + processing: true, + serverSide: true, + paging: true, + searching: false, + autoWidth: false, + scrollCollapse: true, + order: [[6, "desc"]], + ajax: abp.libs.datatables.createAjax(service.getList), + columnDefs: [ + { + rowAction: { + items: + [ + { + text: l('ViewFlashSaleResult'), + action: function (data) { + viewModal.open({ id: data.record.id }); + } + } + ] + } + }, + { + title: l('FlashSaleResultStoreId'), + data: "storeId" + }, + { + title: l('FlashSaleResultPlanId'), + data: "planId" + }, + { + title: l('FlashSaleResultStatus'), + data: "status", + render: function (data) { + return l('Enum:FlashSaleResultStatus.' + data); + } + }, + { + title: l('FlashSaleResultReason'), + data: "reason" + }, + { + title: l('FlashSaleResultUserId'), + data: "userId" + }, + { + title: l('FlashSaleResultOrderId'), + data: "orderId" + }, + { + title: l('FlashSaleResultCreationTime'), + data: "creationTime", + dataFormat: 'datetime' + }, + ] + })); + + viewModal.onResult(function () { + dataTable.ajax.reload(); + }); +}); diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/Index.cshtml b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/Index.cshtml new file mode 100644 index 00000000..fed6c363 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/Index.cshtml @@ -0,0 +1,12 @@ +@page +@using Microsoft.Extensions.Localization +@using EasyAbp.EShop.Plugins.FlashSales.Localization +@using EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales + +@model IndexModel +@inject IStringLocalizer L +@{ +} + +

FlashSales

+

@L["SamplePageMessage"]

diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/Index.cshtml.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/Index.cshtml.cs new file mode 100644 index 00000000..a4551d28 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/Index.cshtml.cs @@ -0,0 +1,8 @@ +namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales; + +public class IndexModel : FlashSalesPageModel +{ + public void OnGet() + { + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/_ViewImports.cshtml b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/_ViewImports.cshtml new file mode 100644 index 00000000..c1da1f5f --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/_ViewImports.cshtml @@ -0,0 +1,4 @@ +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/FlashSalesPageModel.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/FlashSalesPageModel.cs new file mode 100644 index 00000000..2844b9d8 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/FlashSalesPageModel.cs @@ -0,0 +1,15 @@ +using EasyAbp.EShop.Plugins.FlashSales.Localization; +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; + +namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages; + +/* Inherit your PageModel classes from this class. + */ +public abstract class FlashSalesPageModel : AbpPageModel +{ + protected FlashSalesPageModel() + { + LocalizationResourceType = typeof(FlashSalesResource); + ObjectMapperContext = typeof(EShopPluginsFlashSalesWebModule); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Properties/launchSettings.json b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Properties/launchSettings.json new file mode 100644 index 00000000..100df60b --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64779/", + "sslPort": 44326 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "EasyAbp.EShop.Plugins.FlashSales.Web": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions.csproj b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions.csproj new file mode 100644 index 00000000..ceb72923 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions.csproj @@ -0,0 +1,14 @@ + + + + + + netstandard2.0 + + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions/EasyAbp/Eshop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesApplicationModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions/EasyAbp/Eshop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesApplicationModule.cs new file mode 100644 index 00000000..6926ce29 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions/EasyAbp/Eshop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesApplicationModule.cs @@ -0,0 +1,11 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace EasyAbp.EShop.Products.Plugins.FlashSales; + +[DependsOn( + typeof(EShopProductsApplicationContractsModule) +)] +public class EShopProductsPluginsFlashSalesAbstractionsModule : AbpModule +{ +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions/EasyAbp/Eshop/Products/Products/IFlashSaleInventoryManager.cs b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions/EasyAbp/Eshop/Products/Products/IFlashSaleInventoryManager.cs new file mode 100644 index 00000000..2fa36304 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions/EasyAbp/Eshop/Products/Products/IFlashSaleInventoryManager.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading.Tasks; + +namespace EasyAbp.Eshop.Products.Products; + +public interface IFlashSaleInventoryManager +{ + Task TryReduceInventoryAsync(Guid? tenantId, string providerName, Guid storeId, Guid productId, Guid productSkuId, int quantity, bool increaseSold); + + Task TryRollBackInventoryAsync(Guid? tenantId, string providerName, Guid storeId, Guid productId, Guid productSkuId, int quantity, bool decreaseSold); +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions/FodyWeavers.xml b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions/FodyWeavers.xml new file mode 100644 index 00000000..be0de3a9 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions/FodyWeavers.xsd b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions/FodyWeavers.xsd new file mode 100644 index 00000000..3f3946e2 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts.csproj b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts.csproj new file mode 100644 index 00000000..91c56364 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts.csproj @@ -0,0 +1,14 @@ + + + + + + netstandard2.0 + + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesApplicationContractsModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesApplicationContractsModule.cs new file mode 100644 index 00000000..efd5d81b --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesApplicationContractsModule.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Modularity; + +namespace EasyAbp.EShop.Products.Plugins.FlashSales; + +[DependsOn( + typeof(EShopProductsApplicationContractsModule) +)] +public class EShopProductsPluginsFlashSalesApplicationContractsModule : AbpModule +{ +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Products/Plugins/FlashSales/Permissions/ProductsPluginsFlashSalesPermissionDefinitionProvider.cs b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Products/Plugins/FlashSales/Permissions/ProductsPluginsFlashSalesPermissionDefinitionProvider.cs new file mode 100644 index 00000000..aedf09da --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Products/Plugins/FlashSales/Permissions/ProductsPluginsFlashSalesPermissionDefinitionProvider.cs @@ -0,0 +1,26 @@ +using EasyAbp.EShop.Products.Localization; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Localization; + +namespace EasyAbp.EShop.Products.Permissions +{ + public class ProductsPluginsFlashSalesPermissionDefinitionProvider : PermissionDefinitionProvider + { + public override void Define(IPermissionDefinitionContext context) + { + var moduleGroup = context.AddGroup(ProductsPluginsFlashSalesPermissions.GroupName, L("Permission:Products")); + + var flashSaleInventoryPermission = moduleGroup.AddPermission(ProductsPluginsFlashSalesPermissions.FlashSaleInventory.Default, L("Permission:FlashSaleInventory")) + .WithProviders(ClientPermissionValueProvider.ProviderName); + flashSaleInventoryPermission.AddChild(ProductsPluginsFlashSalesPermissions.FlashSaleInventory.Increase, L("Permission:InventoryIncrease")) + .WithProviders(ClientPermissionValueProvider.ProviderName); + flashSaleInventoryPermission.AddChild(ProductsPluginsFlashSalesPermissions.FlashSaleInventory.Reduce, L("Permission:InventoryReduce")) + .WithProviders(ClientPermissionValueProvider.ProviderName); + } + + private static LocalizableString L(string name) + { + return LocalizableString.Create(name); + } + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Products/Plugins/FlashSales/Permissions/ProductsPluginsFlashSalesPermissions.cs b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Products/Plugins/FlashSales/Permissions/ProductsPluginsFlashSalesPermissions.cs new file mode 100644 index 00000000..66e90962 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Products/Plugins/FlashSales/Permissions/ProductsPluginsFlashSalesPermissions.cs @@ -0,0 +1,21 @@ +using Volo.Abp.Reflection; + +namespace EasyAbp.EShop.Products.Permissions +{ + public class ProductsPluginsFlashSalesPermissions + { + public const string GroupName = "EasyAbp.EShop.Products.Plugins.FlashSales"; + + public class FlashSaleInventory + { + public const string Default = GroupName + ".FlashSaleInventory"; + public const string Reduce = Default + ".Reduce"; + public const string Increase = Default + ".Increase"; + } + + public static string[] GetAll() + { + return ReflectionHelper.GetPublicConstantsRecursively(typeof(ProductsPluginsFlashSalesPermissions)); + } + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/IncreaseInventoryInput.cs b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/IncreaseInventoryInput.cs new file mode 100644 index 00000000..e520d5bb --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/IncreaseInventoryInput.cs @@ -0,0 +1,37 @@ +using System; +using EasyAbp.EShop.Stores.Stores; +using Volo.Abp.MultiTenancy; + +namespace EasyAbp.EShop.Products.Products.Dtos; + +public class IncreaseInventoryInput : IMultiTenant, IMultiStore +{ + public Guid? TenantId { get; set; } + + public string ProviderName { get; set; } + + public Guid StoreId { get; set; } + + public Guid ProductId { get; set; } + + public Guid ProductSkuId { get; set; } + + public int Quantity { get; set; } + + public bool ReduceSold { get; set; } + + public IncreaseInventoryInput() + { + } + + public IncreaseInventoryInput(Guid? tenantId, string providerName, Guid storeId, Guid productId, Guid productSkuId, int quantity, bool reduceSold) + { + TenantId = tenantId; + ProviderName = providerName; + StoreId = storeId; + ProductId = productId; + ProductSkuId = productSkuId; + Quantity = quantity; + ReduceSold = reduceSold; + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ReduceInventoryInput.cs b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ReduceInventoryInput.cs new file mode 100644 index 00000000..fa5ac94c --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ReduceInventoryInput.cs @@ -0,0 +1,37 @@ +using System; +using EasyAbp.EShop.Stores.Stores; +using Volo.Abp.MultiTenancy; + +namespace EasyAbp.EShop.Products.Products.Dtos; + +public class ReduceInventoryInput : IMultiTenant, IMultiStore +{ + public Guid? TenantId { get; set; } + + public string ProviderName { get; set; } + + public Guid StoreId { get; set; } + + public Guid ProductId { get; set; } + + public Guid ProductSkuId { get; set; } + + public int Quantity { get; set; } + + public bool IncreaseSold { get; set; } + + public ReduceInventoryInput() + { + } + + public ReduceInventoryInput(Guid? tenantId, string providerName, Guid storeId, Guid productId, Guid productSkuId, int quantity, bool increaseSold) + { + TenantId = tenantId; + ProviderName = providerName; + StoreId = storeId; + ProductId = productId; + ProductSkuId = productSkuId; + Quantity = quantity; + IncreaseSold = increaseSold; + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Products/Products/IFlashSaleInventoryAppService.cs b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Products/Products/IFlashSaleInventoryAppService.cs new file mode 100644 index 00000000..5cd43aed --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Products/Products/IFlashSaleInventoryAppService.cs @@ -0,0 +1,13 @@ +using System; +using System.Threading.Tasks; +using EasyAbp.EShop.Products.Products.Dtos; +using Volo.Abp.Application.Services; + +namespace EasyAbp.EShop.Products.Products; + +public interface IFlashSaleInventoryAppService : IApplicationService +{ + Task TryReduceAsync(ReduceInventoryInput input); + + Task TryIncreaseAsync(IncreaseInventoryInput input); +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/FodyWeavers.xml b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/FodyWeavers.xml new file mode 100644 index 00000000..be0de3a9 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/FodyWeavers.xsd b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/FodyWeavers.xsd new file mode 100644 index 00000000..3f3946e2 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application.Contracts/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp.EShop.Products.Plugins.FlashSales.Application.csproj b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp.EShop.Products.Plugins.FlashSales.Application.csproj new file mode 100644 index 00000000..88afd375 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp.EShop.Products.Plugins.FlashSales.Application.csproj @@ -0,0 +1,17 @@ + + + + + + net6.0 + + + + + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesApplicationModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesApplicationModule.cs new file mode 100644 index 00000000..dcee02c4 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesApplicationModule.cs @@ -0,0 +1,14 @@ +using EasyAbp.EShop.Plugins.FlashSales; +using Volo.Abp.Modularity; + +namespace EasyAbp.EShop.Products.Plugins.FlashSales; + +[DependsOn( + typeof(EShopProductsApplicationModule), + typeof(EShopPluginsFlashSalesApplicationContractsModule), + typeof(EShopProductsPluginsFlashSalesApplicationContractsModule), + typeof(EShopProductsPluginsFlashSalesAbstractionsModule) +)] +public class EShopProductsPluginsFlashSalesApplicationModule : AbpModule +{ +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/FlashSaleInventoryAppService.cs b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/FlashSaleInventoryAppService.cs new file mode 100644 index 00000000..cbac9f41 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/FlashSaleInventoryAppService.cs @@ -0,0 +1,45 @@ +using System.Threading.Tasks; +using EasyAbp.EShop.Products.Permissions; +using EasyAbp.EShop.Products.Products.Dtos; + +namespace EasyAbp.EShop.Products.Products; + +public class FlashSaleInventoryAppService : ProductsAppService, IFlashSaleInventoryAppService +{ + protected ILocalFlashSaleInventoryManager LocalFlashSaleInventoryManager { get; } + + public FlashSaleInventoryAppService(ILocalFlashSaleInventoryManager localFlashSaleInventoryManager) + { + LocalFlashSaleInventoryManager = localFlashSaleInventoryManager; + } + + public virtual async Task TryReduceAsync(ReduceInventoryInput input) + { + await CheckPolicyAsync(ProductsPluginsFlashSalesPermissions.FlashSaleInventory.Reduce); + + return await LocalFlashSaleInventoryManager.TryReduceInventoryAsync( + input.TenantId, + input.ProviderName, + input.StoreId, + input.ProductId, + input.ProductSkuId, + input.Quantity, + input.IncreaseSold + ); + } + + public virtual async Task TryIncreaseAsync(IncreaseInventoryInput input) + { + await CheckPolicyAsync(ProductsPluginsFlashSalesPermissions.FlashSaleInventory.Increase); + + return await LocalFlashSaleInventoryManager.TryRollBackInventoryAsync( + input.TenantId, + input.ProviderName, + input.StoreId, + input.ProductId, + input.ProductSkuId, + input.Quantity, + input.ReduceSold + ); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/ILocalFlashSaleInventoryManager.cs b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/ILocalFlashSaleInventoryManager.cs new file mode 100644 index 00000000..95982ed4 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/ILocalFlashSaleInventoryManager.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using EasyAbp.Eshop.Products.Products; + +namespace EasyAbp.EShop.Products.Products; + +public interface ILocalFlashSaleInventoryManager : IFlashSaleInventoryManager +{ +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/LocalFlashSaleInventoryManager.cs b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/LocalFlashSaleInventoryManager.cs new file mode 100644 index 00000000..093b7ed3 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/LocalFlashSaleInventoryManager.cs @@ -0,0 +1,33 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using EasyAbp.EShop.Products.ProductInventories; +using Volo.Abp.DependencyInjection; + +namespace EasyAbp.EShop.Products.Products; + +public class LocalFlashSaleInventoryManager : ILocalFlashSaleInventoryManager, ITransientDependency +{ + protected IProductInventoryProviderResolver ProductInventoryProviderResolver { get; } + + public LocalFlashSaleInventoryManager(IProductInventoryProviderResolver productInventoryProviderResolver) + { + ProductInventoryProviderResolver = productInventoryProviderResolver; + } + + public virtual async Task TryReduceInventoryAsync(Guid? tenantId, string providerName, Guid storeId, Guid productId, + Guid productSkuId, int quantity, bool increaseSold) + { + var model = new InventoryQueryModel(tenantId, storeId, productId, productSkuId); + return await (await ProductInventoryProviderResolver.GetAsync(providerName)) + .TryReduceInventoryAsync(model, quantity, increaseSold); + } + + public virtual async Task TryRollBackInventoryAsync(Guid? tenantId, string providerName, Guid storeId, Guid productId, + Guid productSkuId, int quantity, bool decreaseSold) + { + var model = new InventoryQueryModel(tenantId, storeId, productId, productSkuId); + return await (await ProductInventoryProviderResolver.GetAsync(providerName)) + .TryIncreaseInventoryAsync(model, quantity, decreaseSold); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/FodyWeavers.xml b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/FodyWeavers.xml new file mode 100644 index 00000000..be0de3a9 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/FodyWeavers.xsd b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/FodyWeavers.xsd new file mode 100644 index 00000000..3f3946e2 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.Client/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.Client.csproj b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.Client/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.Client.csproj new file mode 100644 index 00000000..ef715f1d --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.Client/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.Client.csproj @@ -0,0 +1,23 @@ + + + + + + netstandard2.1 + + + + + + + + + + + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.Client/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesHttpApiClientModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.Client/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesHttpApiClientModule.cs new file mode 100644 index 00000000..0268b99a --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.Client/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesHttpApiClientModule.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; +using Volo.Abp.VirtualFileSystem; + +namespace EasyAbp.EShop.Products.Plugins.FlashSales; + +[DependsOn( + typeof(EShopProductsPluginsFlashSalesApplicationContractsModule) +)] +public class EShopProductsPluginsFlashSalesHttpApiClientModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddHttpClientProxies( + typeof(EShopProductsApplicationContractsModule).Assembly, + EShopProductsRemoteServiceConsts.RemoteServiceName + ); + + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.Client/FodyWeavers.xml b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.Client/FodyWeavers.xml new file mode 100644 index 00000000..be0de3a9 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.Client/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.Client/FodyWeavers.xsd b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.Client/FodyWeavers.xsd new file mode 100644 index 00000000..3f3946e2 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.Client/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.csproj b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.csproj new file mode 100644 index 00000000..b96c1c89 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi.csproj @@ -0,0 +1,16 @@ + + + + + + net6.0 + + + + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesHttpApiModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesHttpApiModule.cs new file mode 100644 index 00000000..3d2bf12d --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesHttpApiModule.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace EasyAbp.EShop.Products.Plugins.FlashSales; + +[DependsOn( + typeof(EShopProductsHttpApiModule), + typeof(EShopProductsPluginsFlashSalesApplicationContractsModule) +)] +public class EShopProductsPluginsFlashSalesHttpApiModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + PreConfigure(mvcBuilder => + { + mvcBuilder.AddApplicationPartIfNotExists(typeof(EShopProductsPluginsFlashSalesHttpApiModule).Assembly); + }); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Products/Products/FlashSaleInventoryController.cs b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Products/Products/FlashSaleInventoryController.cs new file mode 100644 index 00000000..4806a4ba --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Products/Products/FlashSaleInventoryController.cs @@ -0,0 +1,30 @@ +using System.Threading.Tasks; +using EasyAbp.EShop.Products.Products.Dtos; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp; + +namespace EasyAbp.EShop.Products.Products; + +[RemoteService(Name = EShopProductsRemoteServiceConsts.RemoteServiceName)] +[Route("/api/e-shop/products/plugins/flash-sale-inventory")] +public class FlashSaleInventoryController : ProductsController, IFlashSaleInventoryAppService +{ + protected IFlashSaleInventoryAppService AppService { get; } + + public FlashSaleInventoryController(IFlashSaleInventoryAppService appService) + { + AppService = appService; + } + + [HttpPost("try-increase")] + public virtual Task TryIncreaseAsync(IncreaseInventoryInput input) + { + return AppService.TryIncreaseAsync(input); + } + + [HttpPost("try-reduce")] + public virtual Task TryReduceAsync(ReduceInventoryInput input) + { + return AppService.TryReduceAsync(input); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi/FodyWeavers.xml b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi/FodyWeavers.xml new file mode 100644 index 00000000..be0de3a9 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi/FodyWeavers.xsd b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi/FodyWeavers.xsd new file mode 100644 index 00000000..3f3946e2 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.HttpApi/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests.abppkg.json b/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests.abppkg.json new file mode 100644 index 00000000..a686451f --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests.abppkg.json @@ -0,0 +1,3 @@ +{ + "role": "lib.test" +} \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests.csproj b/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests.csproj new file mode 100644 index 00000000..bd18bfdc --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + + + + + + + + + + diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Orders/Orders/CreateFlashSaleOrderEventHandlerTests.cs b/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Orders/Orders/CreateFlashSaleOrderEventHandlerTests.cs new file mode 100644 index 00000000..b7643685 --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Orders/Orders/CreateFlashSaleOrderEventHandlerTests.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using EasyAbp.EShop.Plugins.FlashSales; +using EasyAbp.EShop.Products.ProductDetails.Dtos; +using EasyAbp.EShop.Products.ProductDetails; +using EasyAbp.EShop.Products.Products; +using EasyAbp.EShop.Products.Products.Dtos; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using NSubstitute; +using Xunit; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; +using Volo.Abp.Users; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.MultiTenancy; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; + +namespace EasyAbp.EShop.Orders.Orders; + +public class CreateFlashSaleOrderEventHandlerTests : OrdersPluginsFlashSalesApplicationTestBase +{ + protected CreateFlashSaleOrderEventHandler EventHandler { get; } + + protected ICurrentUser CurrentUser { get; } + + protected ICurrentTenant CurrentTenant { get; } + + protected IDistributedEventBus DistributedEventBus { get; } + + protected IFlashSalePlanHasher FlashSalePlanHasher { get; } + + protected IOrderRepository OrderRepository { get; } + + protected ProductDto Product1 { get; set; } + + public CreateFlashSaleOrderEventHandlerTests() + { + EventHandler = GetRequiredService(); + CurrentUser = GetRequiredService(); + CurrentTenant = GetRequiredService(); + DistributedEventBus = GetRequiredService(); + FlashSalePlanHasher = GetRequiredService(); + OrderRepository = GetRequiredService(); + } + + protected override void AfterAddApplication(IServiceCollection services) + { + Product1 = CreateMockProductDto(); + + var productAppService = Substitute.For(); + productAppService.GetAsync(FlashSalesTestData.Product1Id).Returns(Task.FromResult(Product1)); + services.Replace(ServiceDescriptor.Singleton(productAppService)); + + var productDetailAppService = Substitute.For(); + services.Replace(ServiceDescriptor.Singleton(productDetailAppService)); + productDetailAppService.GetAsync(FlashSalesTestData.ProductDetail1Id).Returns(Task.FromResult( + new ProductDetailDto + { + Id = FlashSalesTestData.ProductDetail1Id, + CreationTime = FlashSalesTestData.ProductDetailLastModificationTime, + LastModificationTime = FlashSalesTestData.ProductDetailLastModificationTime, + StoreId = FlashSalesTestData.Store1Id, + Description = "My Details 1" + })); + productDetailAppService.GetAsync(FlashSalesTestData.ProductDetail2Id).Returns(Task.FromResult( + new ProductDetailDto + { + Id = FlashSalesTestData.ProductDetail2Id, + StoreId = FlashSalesTestData.Store1Id, + Description = "My Details 2" + })); + + var distributedEventBus = Substitute.For(); + services.Replace(ServiceDescriptor.Singleton(distributedEventBus)); + + var flashSalePlanHasher = Substitute.For(); + services.Replace(ServiceDescriptor.Singleton(flashSalePlanHasher)); + + var orderRepository = Substitute.For(); + services.Replace(ServiceDescriptor.Singleton(orderRepository)); + + base.AfterAddApplication(services); + } + + [Fact] + public async Task HandleEventAsync() + { + FlashSalePlanHasher.HashAsync(default, default, default) + .ReturnsForAnyArgs("My Hash Token"); + OrderRepository.InsertAsync(default, default, default) + .ReturnsForAnyArgs(callInfo => callInfo.Arg()); + + var createFlashSaleOrderEto = new CreateFlashSaleOrderEto() + { + TenantId = CurrentTenant.Id, + PlanId = FlashSalesTestData.Plan1Id, + StoreId = FlashSalesTestData.Store1Id, + UserId = CurrentUser.GetId(), + PendingResultId = FlashSalesTestData.Result1Id, + CreateTime = DateTime.Now, + CustomerRemark = "My Remark", + HashToken = "My Hash Token", + Plan = new FlashSalePlanEto + { + Id = FlashSalesTestData.Plan1Id, + TenantId = CurrentTenant.Id, + StoreId = FlashSalesTestData.Store1Id, + BeginTime = DateTime.Now, + EndTime = DateTime.Now.AddMinutes(30), + ProductId = FlashSalesTestData.Product1Id, + ProductSkuId = FlashSalesTestData.ProductSku1Id, + IsPublished = true + } + }; + + await EventHandler.HandleEventAsync(createFlashSaleOrderEto); + + await DistributedEventBus.Received() + .PublishAsync(Arg.Is(eto => + eto.TenantId == CurrentTenant.Id && + eto.PendingResultId == FlashSalesTestData.Result1Id && + eto.Success && + eto.StoreId == FlashSalesTestData.Store1Id && + eto.PlanId == FlashSalesTestData.Plan1Id && + eto.UserId == CurrentUser.GetId() && + eto.OrderId != null && + eto.Reason == null + )); + } + + [Fact] + public async Task HandleEventAsync_Should_Publish_False_When_ValidateHashToken_Failed() + { + FlashSalePlanHasher.HashAsync(default, default, default) + .ReturnsForAnyArgs("My Hash Token"); + + var createFlashSaleOrderEto = new CreateFlashSaleOrderEto() + { + TenantId = CurrentTenant.Id, + PlanId = FlashSalesTestData.Plan1Id, + StoreId = FlashSalesTestData.Store1Id, + UserId = CurrentUser.GetId(), + PendingResultId = FlashSalesTestData.Result1Id, + CreateTime = DateTime.Now, + CustomerRemark = "My Remark", + HashToken = "My Hash Token Failed", + Plan = new FlashSalePlanEto + { + Id = FlashSalesTestData.Plan1Id, + TenantId = CurrentTenant.Id, + StoreId = FlashSalesTestData.Store1Id, + BeginTime = DateTime.Now, + EndTime = DateTime.Now.AddMinutes(30), + ProductId = FlashSalesTestData.Product1Id, + ProductSkuId = FlashSalesTestData.ProductSku1Id, + IsPublished = true + } + }; + + await EventHandler.HandleEventAsync(createFlashSaleOrderEto); + + await DistributedEventBus.Received() + .PublishAsync(Arg.Is(eto => + eto.TenantId == CurrentTenant.Id && + eto.PendingResultId == FlashSalesTestData.Result1Id && + !eto.Success && + eto.StoreId == FlashSalesTestData.Store1Id && + eto.PlanId == FlashSalesTestData.Plan1Id && + eto.UserId == CurrentUser.GetId() && + eto.OrderId == null && + eto.Reason == FlashSaleResultFailedReason.InvalidHashToken + )); + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Orders/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationTestsModule.cs b/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Orders/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationTestsModule.cs new file mode 100644 index 00000000..3ac63e8e --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Orders/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationTestsModule.cs @@ -0,0 +1,13 @@ +using EasyAbp.EShop.Orders.Plugins.FlashSales; +using Volo.Abp.Modularity; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +[DependsOn( + typeof(EShopOrdersPluginsFlashSalesApplicationModule), + typeof(EShopPluginsFlashSalesTestBaseModule) + )] +public class EShopOrdersPluginsFlashSalesApplicationTestsModule : AbpModule +{ + +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Orders/Plugins/FlashSales/OrdersPluginsFlashSalesApplicationTestBase.cs b/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Orders/Plugins/FlashSales/OrdersPluginsFlashSalesApplicationTestBase.cs new file mode 100644 index 00000000..5abed316 --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Orders/Plugins/FlashSales/OrdersPluginsFlashSalesApplicationTestBase.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using EasyAbp.EShop.Products.Products; +using EasyAbp.EShop.Products.Products.Dtos; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +/* Inherit from this class for your application layer tests. + * See SampleAppService_Tests for example. + */ +public abstract class OrdersPluginsFlashSalesApplicationTestBase : FlashSalesTestBase +{ + protected virtual ProductDto CreateMockProductDto() + { + return new ProductDto + { + CreationTime = DateTime.Now, + IsPublished = true, + Id = FlashSalesTestData.Product1Id, + StoreId = FlashSalesTestData.Store1Id, + ProductGroupName = "Default", + ProductGroupDisplayName = "Default", + UniqueName = "Pencil", + DisplayName = "Hello pencil", + ProductDetailId = FlashSalesTestData.ProductDetail1Id, + ProductSkus = new List + { + new ProductSkuDto + { + Id = FlashSalesTestData.ProductSku1Id, + Name = "My SKU", + OrderMinQuantity = 0, + OrderMaxQuantity = 100, + AttributeOptionIds = new List(), + Price = 1m, + Currency = "USD", + ProductDetailId = null, + Inventory = 10, + }, + new ProductSkuDto + { + Id = FlashSalesTestData.ProductSku2Id, + Name = "My SKU 2", + OrderMinQuantity = 0, + OrderMaxQuantity = 100, + AttributeOptionIds = new List(), + Price = 2m, + Currency = "USD", + ProductDetailId = FlashSalesTestData.ProductDetail2Id, + Inventory = 0 + }, + new ProductSkuDto + { + Id = FlashSalesTestData.ProductSku3Id, + Name = "My SKU 3", + OrderMinQuantity = 0, + OrderMaxQuantity = 100, + AttributeOptionIds = new List(), + Price = 3m, + Currency = "USD", + ProductDetailId = FlashSalesTestData.ProductDetail2Id, + Inventory = 1 + } + }, + InventoryStrategy = InventoryStrategy.FlashSales, + LastModificationTime = FlashSalesTestData.ProductLastModificationTime + }; + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/FodyWeavers.xml b/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/FodyWeavers.xml new file mode 100644 index 00000000..1715698c --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/FodyWeavers.xsd b/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/FodyWeavers.xsd new file mode 100644 index 00000000..ffa6fc4b --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Orders.Plugins.FlashSales.Application.Tests/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp.EShop.Plugins.FlashSales.Application.Tests.abppkg.json b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp.EShop.Plugins.FlashSales.Application.Tests.abppkg.json new file mode 100644 index 00000000..a686451f --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp.EShop.Plugins.FlashSales.Application.Tests.abppkg.json @@ -0,0 +1,3 @@ +{ + "role": "lib.test" +} \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp.EShop.Plugins.FlashSales.Application.Tests.csproj b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp.EShop.Plugins.FlashSales.Application.Tests.csproj new file mode 100644 index 00000000..0719f417 --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp.EShop.Plugins.FlashSales.Application.Tests.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + + + + + + + + + + diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Plugins/FlashSales/CreateTimeRange.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Plugins/FlashSales/CreateTimeRange.cs new file mode 100644 index 00000000..2dbc3aa4 --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Plugins/FlashSales/CreateTimeRange.cs @@ -0,0 +1,9 @@ +namespace EasyAbp.EShop.Plugins.FlashSales; + +public enum CreateTimeRange +{ + Starting, + NotStart, + Expired, + WillBeExpired, +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesApplicationTestModule.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesApplicationTestModule.cs new file mode 100644 index 00000000..cf845dff --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesApplicationTestModule.cs @@ -0,0 +1,12 @@ +using Volo.Abp.Modularity; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +[DependsOn( + typeof(EShopPluginsFlashSalesApplicationModule), + typeof(EShopPluginsFlashSalesDomainTestModule) + )] +public class EShopPluginsFlashSalesApplicationTestModule : AbpModule +{ + +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/CreateFlashSaleOrderFailedEventHandlerTest.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/CreateFlashSaleOrderFailedEventHandlerTest.cs new file mode 100644 index 00000000..41e2981f --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/CreateFlashSaleOrderFailedEventHandlerTest.cs @@ -0,0 +1,165 @@ +using System; +using System.Threading.Tasks; +using EasyAbp.Eshop.Products.Products; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; +using EasyAbp.EShop.Products.Products; +using EasyAbp.EShop.Products.Products.Dtos; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using NSubstitute; +using NSubstitute.ReceivedExtensions; +using Shouldly; +using Volo.Abp.Users; +using Xunit; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +public class CreateFlashSaleOrderFailedEventHandlerTest : FlashSalesApplicationTestBase +{ + protected CreateFlashSaleOrderFailedEventHandler EventHandler { get; } + + protected IDistributedCache DistributedCache { get; } + + protected IFlashSaleInventoryManager FlashSaleInventoryManager { get; set; } + + private ProductDto Product1 { get; set; } + + public CreateFlashSaleOrderFailedEventHandlerTest() + { + EventHandler = GetRequiredService(); + DistributedCache = GetRequiredService(); + FlashSaleInventoryManager = GetRequiredService(); + } + + protected override void AfterAddApplication(IServiceCollection services) + { + Product1 = CreateMockProductDto(); + + var productAppService = Substitute.For(); + productAppService.GetAsync(FlashSalesTestData.Product1Id).Returns(Task.FromResult(Product1)); + services.Replace(ServiceDescriptor.Singleton(productAppService)); + + FlashSaleInventoryManager = Substitute.For(); + services.Replace(ServiceDescriptor.Singleton(FlashSaleInventoryManager)); + base.AfterAddApplication(services); + } + + [Fact] + public async Task HandleEventAsync() + { + var plan = await CreateFlashSalePlanAsync(); + var pendingFlashResult = await CreatePendingResultAsync(plan.Id, plan.StoreId, CurrentUser.GetId()); + var userFlashSaleResultCacheKey = string.Format(FlashSalePlanAppService.UserFlashSaleResultCacheKeyFormat, plan.TenantId, plan.Id, CurrentUser.GetId()); + await DistributedCache.SetStringAsync(userFlashSaleResultCacheKey, pendingFlashResult.Id.ToString()); + var createFlashSaleOrderCompleteEto = new CreateFlashSaleOrderCompleteEto() + { + TenantId = pendingFlashResult.TenantId, + PendingResultId = pendingFlashResult.Id, + Success = false, + StoreId = pendingFlashResult.StoreId, + PlanId = pendingFlashResult.PlanId, + OrderId = null, + Reason = FlashSaleResultFailedReason.InvalidHashToken, + UserId = pendingFlashResult.UserId + }; + FlashSaleInventoryManager + .TryRollBackInventoryAsync(plan.TenantId, Product1.InventoryProviderName, plan.StoreId, plan.ProductId, plan.ProductSkuId, 1, true) + .Returns(Task.FromResult(true)); + + await EventHandler.HandleEventAsync(createFlashSaleOrderCompleteEto); + + var userFlashSaleResultCache = await DistributedCache.GetStringAsync(userFlashSaleResultCacheKey); + userFlashSaleResultCache.ShouldBeNull(); + + await FlashSaleInventoryManager.Received() + .TryRollBackInventoryAsync(plan.TenantId, Product1.InventoryProviderName, plan.StoreId, plan.ProductId, plan.ProductSkuId, 1, true); + } + + [Fact] + public async Task HandleEventAsync_Should_Not_Remove_UserFlashSaleResultCache_When_TryRollBackInventory_Failed() + { + var plan = await CreateFlashSalePlanAsync(); + var pendingFlashResult = await CreatePendingResultAsync(plan.Id, plan.StoreId, CurrentUser.GetId()); + var userFlashSaleResultCacheKey = string.Format(FlashSalePlanAppService.UserFlashSaleResultCacheKeyFormat, plan.TenantId, plan.Id, CurrentUser.GetId()); + await DistributedCache.SetStringAsync(userFlashSaleResultCacheKey, pendingFlashResult.Id.ToString()); + var createFlashSaleOrderCompleteEto = new CreateFlashSaleOrderCompleteEto() + { + TenantId = pendingFlashResult.TenantId, + PendingResultId = pendingFlashResult.Id, + Success = false, + StoreId = pendingFlashResult.StoreId, + PlanId = pendingFlashResult.PlanId, + OrderId = null, + Reason = FlashSaleResultFailedReason.InvalidHashToken, + UserId = pendingFlashResult.UserId + }; + FlashSaleInventoryManager + .TryRollBackInventoryAsync(plan.TenantId, Product1.InventoryProviderName, plan.StoreId, plan.ProductId, plan.ProductSkuId, 1, true) + .Returns(Task.FromResult(false)); + + await EventHandler.HandleEventAsync(createFlashSaleOrderCompleteEto); + + var userFlashSaleResultCache = await DistributedCache.GetStringAsync(userFlashSaleResultCacheKey); + userFlashSaleResultCache.ShouldBe(pendingFlashResult.Id.ToString()); + + await FlashSaleInventoryManager.Received() + .TryRollBackInventoryAsync(plan.TenantId, Product1.InventoryProviderName, plan.StoreId, plan.ProductId, plan.ProductSkuId, 1, true); + } + + [Fact] + public async Task HandleEventAsync_Should_Ignore_When_Success_Is_True() + { + var plan = await CreateFlashSalePlanAsync(); + var pendingFlashResult = await CreatePendingResultAsync(plan.Id, plan.StoreId, CurrentUser.GetId()); + var userFlashSaleResultCacheKey = string.Format(FlashSalePlanAppService.UserFlashSaleResultCacheKeyFormat, plan.TenantId, plan.Id, CurrentUser.GetId()); + await DistributedCache.SetStringAsync(userFlashSaleResultCacheKey, pendingFlashResult.Id.ToString()); + var createFlashSaleOrderCompleteEto = new CreateFlashSaleOrderCompleteEto() + { + TenantId = pendingFlashResult.TenantId, + PendingResultId = pendingFlashResult.Id, + Success = true, + StoreId = pendingFlashResult.StoreId, + PlanId = pendingFlashResult.PlanId, + OrderId = Guid.NewGuid(), + Reason = null, + UserId = pendingFlashResult.UserId + }; + + await EventHandler.HandleEventAsync(createFlashSaleOrderCompleteEto); + + var userFlashSaleResultCache = await DistributedCache.GetStringAsync(userFlashSaleResultCacheKey); + userFlashSaleResultCache.ShouldBe(pendingFlashResult.Id.ToString()); + + await FlashSaleInventoryManager.DidNotReceiveWithAnyArgs() + .TryRollBackInventoryAsync(default, default, Guid.Empty, Guid.Empty, Guid.Empty, default, default); + } + + [Fact] + public async Task HandleEventAsync_Should_Ignore_When_Reason_Not_InvalidHashToken() + { + var plan = await CreateFlashSalePlanAsync(); + var pendingFlashResult = await CreatePendingResultAsync(plan.Id, plan.StoreId, CurrentUser.GetId()); + var userFlashSaleResultCacheKey = string.Format(FlashSalePlanAppService.UserFlashSaleResultCacheKeyFormat, plan.TenantId, plan.Id, CurrentUser.GetId()); + await DistributedCache.SetStringAsync(userFlashSaleResultCacheKey, pendingFlashResult.Id.ToString()); + var createFlashSaleOrderCompleteEto = new CreateFlashSaleOrderCompleteEto() + { + TenantId = pendingFlashResult.TenantId, + PendingResultId = pendingFlashResult.Id, + Success = false, + StoreId = pendingFlashResult.StoreId, + PlanId = pendingFlashResult.PlanId, + OrderId = null, + Reason = "Other", + UserId = pendingFlashResult.UserId + }; + + await EventHandler.HandleEventAsync(createFlashSaleOrderCompleteEto); + + var userFlashSaleResultCache = await DistributedCache.GetStringAsync(userFlashSaleResultCacheKey); + userFlashSaleResultCache.ShouldBe(pendingFlashResult.Id.ToString()); + + await FlashSaleInventoryManager.DidNotReceiveWithAnyArgs() + .TryRollBackInventoryAsync(default, default, Guid.Empty, Guid.Empty, Guid.Empty, default, default); + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanAppServiceTests.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanAppServiceTests.cs new file mode 100644 index 00000000..844fe01c --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanAppServiceTests.cs @@ -0,0 +1,486 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using EasyAbp.Eshop.Products.Products; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.Dtos; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; +using EasyAbp.EShop.Products.Products; +using EasyAbp.EShop.Products.Products.Dtos; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; +using NSubstitute; +using Shouldly; +using Volo.Abp; +using Volo.Abp.Caching; +using Volo.Abp.DistributedLocking; +using Volo.Abp.Domain.Entities; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.Users; +using Xunit; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; +public class FlashSalePlanAppServiceTests : FlashSalesApplicationTestBase +{ + protected IFlashSalePlanAppService AppService { get; } + + protected IDistributedEventBus DistributedEventBus { get; } + + private ProductDto Product1 { get; set; } + + public FlashSalePlanAppServiceTests() + { + AppService = GetRequiredService(); + DistributedEventBus = GetRequiredService(); + } + + protected override void AfterAddApplication(IServiceCollection services) + { + Product1 = CreateMockProductDto(); + + var productAppService = Substitute.For(); + productAppService.GetAsync(FlashSalesTestData.Product1Id).Returns(Task.FromResult(Product1)); + services.Replace(ServiceDescriptor.Singleton(productAppService)); + + services.Replace(ServiceDescriptor.Transient()); + + var distributedEventBus = Substitute.For(); + services.Replace(ServiceDescriptor.Singleton(distributedEventBus)); + base.AfterAddApplication(services); + } + + [Fact] + public async Task GetAsync() + { + var returnFlashSalePlan = await CreateFlashSalePlanAsync(); + + var flashSalePlan = await AppService.GetAsync(returnFlashSalePlan.Id); + + flashSalePlan.ShouldNotBeNull(); + flashSalePlan.Id.ShouldBe(returnFlashSalePlan.Id); + flashSalePlan.StoreId.ShouldBe(returnFlashSalePlan.StoreId); + flashSalePlan.BeginTime.ShouldBe(returnFlashSalePlan.BeginTime); + flashSalePlan.EndTime.ShouldBe(returnFlashSalePlan.EndTime); + flashSalePlan.ProductId.ShouldBe(returnFlashSalePlan.ProductId); + flashSalePlan.ProductSkuId.ShouldBe(returnFlashSalePlan.ProductSkuId); + flashSalePlan.IsPublished.ShouldBe(returnFlashSalePlan.IsPublished); + + returnFlashSalePlan = await CreateFlashSalePlanAsync(isPublished: false); + + flashSalePlan = await AppService.GetAsync(returnFlashSalePlan.Id); + + flashSalePlan.ShouldNotBeNull(); + flashSalePlan.Id.ShouldBe(returnFlashSalePlan.Id); + flashSalePlan.StoreId.ShouldBe(returnFlashSalePlan.StoreId); + flashSalePlan.BeginTime.ShouldBe(returnFlashSalePlan.BeginTime); + flashSalePlan.EndTime.ShouldBe(returnFlashSalePlan.EndTime); + flashSalePlan.ProductId.ShouldBe(returnFlashSalePlan.ProductId); + flashSalePlan.ProductSkuId.ShouldBe(returnFlashSalePlan.ProductSkuId); + flashSalePlan.IsPublished.ShouldBe(returnFlashSalePlan.IsPublished); + } + + [Fact] + public async Task GetListAsync() + { + var publishedPlan = await CreateFlashSalePlanAsync(isPublished: true); + var unpublishedPlan = await CreateFlashSalePlanAsync(isPublished: false); + + var allListDto = await AppService.GetListAsync(new FlashSalePlanGetListInput() + { + IncludeUnpublished = true + }); + + allListDto.TotalCount.ShouldBeGreaterThan(0); + allListDto.Items.FirstOrDefault(x => x.Id == publishedPlan.Id) + .ShouldNotBeNull() + .Id.ShouldBe(publishedPlan.Id); + + allListDto.Items.FirstOrDefault(x => x.Id == unpublishedPlan.Id) + .ShouldNotBeNull() + .Id.ShouldBe(unpublishedPlan.Id); + + var publishedListDto = await AppService.GetListAsync(new FlashSalePlanGetListInput()); + + publishedListDto.TotalCount.ShouldBeGreaterThan(0); + publishedListDto.Items.FirstOrDefault(x => x.Id == publishedPlan.Id) + .ShouldNotBeNull() + .Id.ShouldBe(publishedPlan.Id); + + publishedListDto.Items.FirstOrDefault(x => x.Id == unpublishedPlan.Id) + .ShouldBeNull(); + } + + [Fact] + public async Task CreateAsync() + { + var createDto = new FlashSalePlanCreateDto() + { + StoreId = FlashSalesTestData.Store1Id, + BeginTime = DateTime.Now, + EndTime = DateTime.Now.AddMinutes(30), + ProductId = FlashSalesTestData.Product1Id, + ProductSkuId = FlashSalesTestData.ProductSku1Id, + IsPublished = true + }; + var returnFlashSalePlanDto = await AppService.CreateAsync(createDto); + + var flashSalePlan = await AppService.GetAsync(returnFlashSalePlanDto.Id); + + flashSalePlan.Id.ShouldBe(returnFlashSalePlanDto.Id); + flashSalePlan.StoreId.ShouldBe(createDto.StoreId); + flashSalePlan.BeginTime.ShouldBe(createDto.BeginTime); + flashSalePlan.EndTime.ShouldBe(createDto.EndTime); + flashSalePlan.ProductId.ShouldBe(createDto.ProductId); + flashSalePlan.ProductSkuId.ShouldBe(createDto.ProductSkuId); + flashSalePlan.IsPublished.ShouldBe(createDto.IsPublished); + + return flashSalePlan; + } + + [Fact] + public async Task CreateAsync_Should_Throw_Exception_When_Validate_Product_Failed() + { + var createDto = new FlashSalePlanCreateDto() + { + StoreId = FlashSalesTestData.Store1Id, + BeginTime = DateTime.Now, + EndTime = DateTime.Now.AddMinutes(30), + ProductId = FlashSalesTestData.Product1Id, + ProductSkuId = FlashSalesTestData.ProductSku1Id, + IsPublished = true + }; + + Product1.StoreId = Guid.NewGuid(); + await AppService.CreateAsync(createDto).ShouldThrowAsync(); + Product1.StoreId = FlashSalesTestData.Store1Id; + + Product1.InventoryStrategy = InventoryStrategy.ReduceAfterPlacing; + await AppService.CreateAsync(createDto).ShouldThrowAsync(); + + Product1.StoreId = FlashSalesTestData.Store1Id; + Product1.InventoryStrategy = InventoryStrategy.FlashSales; + } + + [Fact] + public async Task UpdateAsync() + { + var createDto = new FlashSalePlanCreateDto() + { + StoreId = FlashSalesTestData.Store1Id, + BeginTime = DateTime.Now, + EndTime = DateTime.Now.AddMinutes(30), + ProductId = FlashSalesTestData.Product1Id, + ProductSkuId = FlashSalesTestData.ProductSku1Id, + IsPublished = true + }; + var returnFlashSalePlanDto = await AppService.CreateAsync(createDto); + var updateDto = new FlashSalePlanUpdateDto() + { + BeginTime = DateTime.Now.AddMinutes(30), + EndTime = DateTime.Now.AddMinutes(60), + ProductId = FlashSalesTestData.Product1Id, + ProductSkuId = FlashSalesTestData.ProductSku1Id, + IsPublished = false + }; + + var flashSalePlan = await AppService.UpdateAsync(returnFlashSalePlanDto.Id, updateDto); + + flashSalePlan.Id.ShouldBe(returnFlashSalePlanDto.Id); + flashSalePlan.StoreId.ShouldBe(createDto.StoreId); + flashSalePlan.BeginTime.ShouldBe(updateDto.BeginTime); + flashSalePlan.EndTime.ShouldBe(updateDto.EndTime); + flashSalePlan.ProductId.ShouldBe(updateDto.ProductId); + flashSalePlan.ProductSkuId.ShouldBe(updateDto.ProductSkuId); + flashSalePlan.IsPublished.ShouldBe(updateDto.IsPublished); + } + + [Fact] + public async Task UpdateAsync_Should_Throw_Exception_When_Validate_Product_Failed() + { + var createDto = new FlashSalePlanCreateDto() + { + StoreId = FlashSalesTestData.Store1Id, + BeginTime = DateTime.Now, + EndTime = DateTime.Now.AddMinutes(30), + ProductId = FlashSalesTestData.Product1Id, + ProductSkuId = FlashSalesTestData.ProductSku1Id, + IsPublished = true + }; + var returnFlashSalePlanDto = await AppService.CreateAsync(createDto); + var updateDto = new FlashSalePlanUpdateDto() + { + BeginTime = DateTime.Now.AddMinutes(30), + EndTime = DateTime.Now.AddMinutes(60), + ProductId = FlashSalesTestData.Product1Id, + ProductSkuId = FlashSalesTestData.ProductSku1Id, + IsPublished = false + }; + + Product1.StoreId = Guid.NewGuid(); + await AppService.UpdateAsync(returnFlashSalePlanDto.Id, updateDto).ShouldThrowAsync(); + Product1.StoreId = FlashSalesTestData.Store1Id; + + Product1.InventoryStrategy = InventoryStrategy.ReduceAfterPlacing; + await AppService.UpdateAsync(returnFlashSalePlanDto.Id, updateDto).ShouldThrowAsync(); + + Product1.StoreId = FlashSalesTestData.Store1Id; + Product1.InventoryStrategy = InventoryStrategy.FlashSales; + } + + [Fact] + public async Task UpdateAsync_Should_Throw_Exception_When_Has_Result_And_Change_Product() + { + var createDto = new FlashSalePlanCreateDto() + { + StoreId = FlashSalesTestData.Store1Id, + BeginTime = DateTime.Now, + EndTime = DateTime.Now.AddMinutes(30), + ProductId = FlashSalesTestData.Product1Id, + ProductSkuId = FlashSalesTestData.ProductSku1Id, + IsPublished = true + }; + var returnFlashSalePlanDto = await AppService.CreateAsync(createDto); + var updateDto = new FlashSalePlanUpdateDto() + { + BeginTime = DateTime.Now.AddMinutes(30), + EndTime = DateTime.Now.AddMinutes(value: 60), + ProductId = FlashSalesTestData.Product1Id, + ProductSkuId = FlashSalesTestData.ProductSku2Id, + IsPublished = false + }; + await CreatePendingResultAsync(returnFlashSalePlanDto.Id, returnFlashSalePlanDto.StoreId, Guid.NewGuid()); + + await AppService.UpdateAsync(returnFlashSalePlanDto.Id, updateDto).ShouldThrowAsync(); + } + + [Fact] + public async Task DeleteAsync() + { + var createDto = new FlashSalePlanCreateDto() + { + StoreId = FlashSalesTestData.Store1Id, + BeginTime = DateTime.Now, + EndTime = DateTime.Now.AddMinutes(30), + ProductId = FlashSalesTestData.Product1Id, + ProductSkuId = FlashSalesTestData.ProductSku1Id, + IsPublished = true + }; + var returnFlashSalePlanDto = await AppService.CreateAsync(createDto); + + await AppService.DeleteAsync(returnFlashSalePlanDto.Id); + await AppService.GetAsync(returnFlashSalePlanDto.Id).ShouldThrowAsync(); + } + + [Fact] + public async Task DeleteAsync_Should_Throw_Exception_When_Has_Result() + { + var createDto = new FlashSalePlanCreateDto() + { + StoreId = FlashSalesTestData.Store1Id, + BeginTime = DateTime.Now, + EndTime = DateTime.Now.AddMinutes(30), + ProductId = FlashSalesTestData.Product1Id, + ProductSkuId = FlashSalesTestData.ProductSku1Id, + IsPublished = true + }; + var returnFlashSalePlanDto = await AppService.CreateAsync(createDto); + await CreatePendingResultAsync(returnFlashSalePlanDto.Id, returnFlashSalePlanDto.StoreId, Guid.NewGuid()); + + await AppService.DeleteAsync(returnFlashSalePlanDto.Id).ShouldThrowAsync(); + } + + [Fact] + public async Task PreOrderAsync() + { + var options = GetRequiredService>().Value; + var distributedCache = GetRequiredService>(); + var plan = await CreateFlashSalePlanAsync(); + var preOrderCacheKey = string.Format(FlashSalePlanAppService.PreOrderCacheKeyFormat, plan.Id, CurrentUser.Id); + var hashToken = await GetRequiredService() + .HashAsync(plan.LastModificationTime, Product1.LastModificationTime, Product1.GetSkuById(plan.ProductSkuId).LastModificationTime); + + var dto = await AppService.PreOrderAsync(plan.Id); + + dto.ExpiresInSeconds.ShouldBe(options.PreOrderExpires.TotalSeconds); + var preOrderCacheItem = await distributedCache.GetAsync(preOrderCacheKey); + preOrderCacheItem.ShouldNotBeNull(); + preOrderCacheItem.PlanId.ShouldBe(plan.Id); + preOrderCacheItem.TenantId.ShouldBe(plan.TenantId); + preOrderCacheItem.HashToken.ShouldBe(hashToken); + preOrderCacheItem.ProductId.ShouldBe(plan.ProductId); + preOrderCacheItem.ProductSkuId.ShouldBe(plan.ProductSkuId); + preOrderCacheItem.InventoryProviderName = Product1.InventoryProviderName; + } + + [Fact] + public async Task PreOrderAsync_Should_Throw_Exception_When_Validate_PreOrder_Failed() + { + var plan = await CreateFlashSalePlanAsync(); + + Product1.IsPublished = false; + + (await AppService.PreOrderAsync(plan.Id) + .ShouldThrowAsync()) + .Code.ShouldBe(FlashSalesErrorCodes.ProductIsNotPublished); + + Product1.IsPublished = true; + + Product1.InventoryStrategy = InventoryStrategy.ReduceAfterPlacing; + + await AppService.PreOrderAsync(plan.Id) + .ShouldThrowAsync(); + + Product1.InventoryStrategy = InventoryStrategy.FlashSales; + + var plan2 = await CreateFlashSalePlanAsync(isPublished: false); + await AppService.PreOrderAsync(plan2.Id) + .ShouldThrowAsync(); + + var plan3 = await CreateFlashSalePlanAsync(timeRange: CreateTimeRange.Expired); + (await AppService.PreOrderAsync(plan3.Id) + .ShouldThrowAsync()) + .Code.ShouldBe(FlashSalesErrorCodes.FlashSaleIsOver); + + var plan4 = await CreateFlashSalePlanAsync(useSku2: true); + (await AppService.PreOrderAsync(plan4.Id) + .ShouldThrowAsync()) + .Code.ShouldBe(FlashSalesErrorCodes.ProductSkuInventoryExceeded); + } + + [Fact] + public async Task OrderAsync() + { + var plan = await CreateFlashSalePlanAsync(); + var hashToken = await GetRequiredService() + .HashAsync(plan.LastModificationTime, Product1.LastModificationTime, Product1.GetSkuById(plan.ProductSkuId).LastModificationTime); + var createOrderInput = new CreateOrderInput() + { + CustomerRemark = "remark1", + ExtraProperties = { { "key1", "value1" } } + }; + await AppService.PreOrderAsync(plan.Id); + + var result = await AppService.OrderAsync(plan.Id, createOrderInput); + + result.IsSuccess.ShouldBe(true); + result.FlashSaleResultId.ShouldNotBeNull(); + await DistributedEventBus.Received().PublishAsync(Arg.Is(eto => + eto.TenantId == plan.TenantId && + eto.StoreId == plan.StoreId && + eto.PlanId == plan.Id && + eto.UserId == CurrentUser.GetId() && + eto.HashToken == hashToken && + eto.CustomerRemark == createOrderInput.CustomerRemark && + eto.Plan != null && + eto.Plan.TenantId == plan.TenantId && + eto.Plan.StoreId == plan.StoreId && + eto.Plan.BeginTime == plan.BeginTime && + eto.Plan.EndTime == plan.EndTime && + eto.Plan.ProductId == plan.ProductId && + eto.Plan.ProductSkuId == plan.ProductSkuId && + eto.Plan.IsPublished == plan.IsPublished && + eto.ExtraProperties.ContainsKey("key1") && + eto.ExtraProperties["key1"].ToString() == "value1" + )); + } + + [Fact] + public async Task OrderAsync_Throw_Exception_When_Not_PreOrder() + { + var plan = await CreateFlashSalePlanAsync(); + var createOrderInput = new CreateOrderInput(); + + (await AppService.OrderAsync(plan.Id, createOrderInput) + .ShouldThrowAsync()) + .Code.ShouldBe(FlashSalesErrorCodes.PreOrderExpired); + } + + [Fact] + public async Task OrderAsync_Throw_Exception_When_FlashSaleNotStarted() + { + var plan = await CreateFlashSalePlanAsync(timeRange: CreateTimeRange.NotStart); + var createOrderInput = new CreateOrderInput(); + await AppService.PreOrderAsync(plan.Id); + + (await AppService.OrderAsync(plan.Id, createOrderInput) + .ShouldThrowAsync()) + .Code.ShouldBe(FlashSalesErrorCodes.FlashSaleNotStarted); + } + + [Fact] + public async Task OrderAsync_Throw_Exception_When_FlashSaleIsOver() + { + var plan = await CreateFlashSalePlanAsync(timeRange: CreateTimeRange.WillBeExpired); + var createOrderInput = new CreateOrderInput(); + await AppService.PreOrderAsync(plan.Id); + + await Task.Delay(TimeSpan.FromSeconds(1.2)); + + (await AppService.OrderAsync(plan.Id, createOrderInput) + .ShouldThrowAsync()) + .Code.ShouldBe(FlashSalesErrorCodes.FlashSaleIsOver); + } + + [Fact] + public async Task OrderAsync_Throw_Exception_When_BusyToCreateFlashSaleOrder() + { + var plan = await CreateFlashSalePlanAsync(); + var createOrderInput = new CreateOrderInput(); + await AppService.PreOrderAsync(plan.Id); + var distributedLock = GetRequiredService(); + var lockKey = $"create-flash-sale-order-{plan.Id}-{CurrentUser.GetId()}"; + + await using var handle = await distributedLock.TryAcquireAsync(lockKey); + + (await AppService.OrderAsync(plan.Id, createOrderInput) + .ShouldThrowAsync()) + .Code.ShouldBe(FlashSalesErrorCodes.BusyToCreateFlashSaleOrder); + } + + [Fact] + public async Task OrderAsync_Throw_Exception_When_Exist_UserFlashSaleResultCache() + { + var plan = await CreateFlashSalePlanAsync(); + var createOrderInput = new CreateOrderInput(); + await AppService.PreOrderAsync(plan.Id); + var distributedCache = GetRequiredService(); + var userId = CurrentUser.GetId(); + var userFlashSaleResultCacheKey = string.Format(FlashSalePlanAppService.UserFlashSaleResultCacheKeyFormat, plan.TenantId, plan.Id, userId); + await distributedCache.SetStringAsync(userFlashSaleResultCacheKey, Guid.NewGuid().ToString()); + + (await AppService.OrderAsync(plan.Id, createOrderInput) + .ShouldThrowAsync()) + .Code.ShouldBe(FlashSalesErrorCodes.DuplicateFlashSalesOrder); + } + + [Fact] + public async Task OrderAsync_Return_False_When_TryReduceInventory_Failed() + { + var plan = await CreateFlashSalePlanAsync(); + var createOrderInput = new CreateOrderInput(); + await AppService.PreOrderAsync(plan.Id); + + FakeFlashSaleInventoryManager.ShouldReduceSuccess = false; + + var result = await AppService.OrderAsync(plan.Id, createOrderInput); + result.IsSuccess.ShouldBe(false); + result.FlashSaleResultId.ShouldBeNull(); + } + + [Fact] + public async Task OrderAsync_Return_False_When_Exist_Not_Failed_Result() + { + var plan = await CreateFlashSalePlanAsync(); + var createOrderInput = new CreateOrderInput(); + await AppService.PreOrderAsync(plan.Id); + var userId = GetRequiredService().GetId(); + + await CreatePendingResultAsync(plan.Id, plan.StoreId, userId); + + (await AppService.OrderAsync(plan.Id, createOrderInput) + .ShouldThrowAsync()) + .Code.ShouldBe(FlashSalesErrorCodes.DuplicateFlashSalesOrder); + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalesApplicationTestBase.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalesApplicationTestBase.cs new file mode 100644 index 00000000..e0877a3d --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalesApplicationTestBase.cs @@ -0,0 +1,149 @@ +using System.Threading.Tasks; +using System; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; +using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; +using Volo.Abp; +using Volo.Abp.Guids; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Timing; +using Volo.Abp.Users; +using EasyAbp.EShop.Products.Products.Dtos; +using EasyAbp.EShop.Products.Products; +using System.Collections.Generic; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +/* Inherit from this class for your application layer tests. + * See SampleAppService_Tests for example. + */ +public abstract class FlashSalesApplicationTestBase : FlashSalesTestBase +{ + protected IGuidGenerator GuidGenerator { get; } + + protected ICurrentTenant CurrentTenant { get; } + + protected IClock Clock { get; } + + protected ICurrentUser CurrentUser { get; } + + protected IFlashSaleResultRepository FlashSaleResultRepository { get; } + + protected IFlashSalePlanRepository FlashSalePlanRepository { get; } + + protected FlashSalesApplicationTestBase() + { + GuidGenerator = GetRequiredService(); + CurrentTenant = GetRequiredService(); + Clock = GetRequiredService(); + CurrentUser = GetRequiredService(); + FlashSalePlanRepository = GetRequiredService(); + FlashSaleResultRepository = GetRequiredService(); + } + + protected virtual ProductDto CreateMockProductDto() + { + return new ProductDto + { + CreationTime = DateTime.Now, + IsPublished = true, + Id = FlashSalesTestData.Product1Id, + StoreId = FlashSalesTestData.Store1Id, + ProductGroupName = "Default", + ProductGroupDisplayName = "Default", + UniqueName = "Pencil", + DisplayName = "Hello pencil", + ProductDetailId = FlashSalesTestData.ProductDetail1Id, + ProductSkus = new List + { + new ProductSkuDto + { + Id = FlashSalesTestData.ProductSku1Id, + Name = "My SKU", + OrderMinQuantity = 0, + OrderMaxQuantity = 100, + AttributeOptionIds = new List(), + Price = 1m, + Currency = "USD", + ProductDetailId = null, + Inventory = 10, + }, + new ProductSkuDto + { + Id = FlashSalesTestData.ProductSku2Id, + Name = "My SKU 2", + OrderMinQuantity = 0, + OrderMaxQuantity = 100, + AttributeOptionIds = new List(), + Price = 2m, + Currency = "USD", + ProductDetailId = FlashSalesTestData.ProductDetail2Id, + Inventory = 0 + }, + new ProductSkuDto + { + Id = FlashSalesTestData.ProductSku3Id, + Name = "My SKU 3", + OrderMinQuantity = 0, + OrderMaxQuantity = 100, + AttributeOptionIds = new List(), + Price = 3m, + Currency = "USD", + ProductDetailId = FlashSalesTestData.ProductDetail2Id, + Inventory = 1 + } + }, + InventoryStrategy = InventoryStrategy.FlashSales, + LastModificationTime = FlashSalesTestData.ProductLastModificationTime + }; + } + + protected virtual async Task CreatePendingResultAsync(Guid planId, Guid storeId, Guid userId) + { + return await WithUnitOfWorkAsync(async () => + { + return await FlashSaleResultRepository.InsertAsync( + new FlashSaleResult(GuidGenerator.Create(), CurrentTenant.Id, storeId, planId, userId) + ); + }); + } + + protected virtual async Task CreateFlashSalePlanAsync(bool useSku2 = false, CreateTimeRange timeRange = CreateTimeRange.Starting, bool isPublished = true) + { + DateTime beginTime; + DateTime endTime; + + switch (timeRange) + { + case CreateTimeRange.Starting: + beginTime = Clock.Now; + endTime = beginTime.AddMinutes(30); + break; + case CreateTimeRange.NotStart: + beginTime = Clock.Now.AddMinutes(10); + endTime = beginTime.AddMinutes(30); + break; + case CreateTimeRange.Expired: + beginTime = Clock.Now.AddDays(-1); + endTime = beginTime.AddMinutes(30); + break; + case CreateTimeRange.WillBeExpired: + beginTime = Clock.Now.AddDays(-30); + endTime = Clock.Now.AddSeconds(1); + break; + default: + throw new AbpException(); + } + + var flashSalePlan = new FlashSalePlan( + GuidGenerator.Create(), + CurrentTenant.Id, + FlashSalesTestData.Store1Id, + beginTime, + endTime, + FlashSalesTestData.Product1Id, + useSku2 ? FlashSalesTestData.ProductSku2Id : FlashSalesTestData.ProductSku1Id, + isPublished + ); + return await WithUnitOfWorkAsync(async () => await FlashSalePlanRepository.InsertAsync(flashSalePlan, autoSave: true)); + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Products/Products/FakeFlashSaleInventoryManager.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Products/Products/FakeFlashSaleInventoryManager.cs new file mode 100644 index 00000000..ff159deb --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Products/Products/FakeFlashSaleInventoryManager.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading.Tasks; +using EasyAbp.Eshop.Products.Products; + +namespace EasyAbp.EShop.Products.Products; + +public class FakeFlashSaleInventoryManager : IFlashSaleInventoryManager +{ + public static bool ShouldReduceSuccess { get; set; } + + public FakeFlashSaleInventoryManager() + { + ShouldReduceSuccess = true; + } + + public Task TryReduceInventoryAsync( + Guid? tenantId, string providerName, Guid storeId, Guid productId, Guid productSkuId, int quantity, bool increaseSold) + { + return Task.FromResult(ShouldReduceSuccess); + } + + public Task TryRollBackInventoryAsync( + Guid? tenantId, string providerName, Guid storeId, Guid productId, Guid productSkuId, int quantity, bool decreaseSold) + { + return Task.FromResult(true); + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/FodyWeavers.xml b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/FodyWeavers.xml new file mode 100644 index 00000000..1715698c --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/FodyWeavers.xsd b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/FodyWeavers.xsd new file mode 100644 index 00000000..ffa6fc4b --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests.abppkg.json b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests.abppkg.json new file mode 100644 index 00000000..a686451f --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests.abppkg.json @@ -0,0 +1,3 @@ +{ + "role": "lib.test" +} \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests.csproj b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests.csproj new file mode 100644 index 00000000..72a84b4a --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + + + + + + + + + diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesDomainTestModule.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesDomainTestModule.cs new file mode 100644 index 00000000..3a033be8 --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesDomainTestModule.cs @@ -0,0 +1,16 @@ +using EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +/* Domain tests are configured to use the EF Core provider. + * You can switch to MongoDB, however your domain tests should be + * database independent anyway. + */ +[DependsOn( + typeof(EShopPluginsFlashSalesEntityFrameworkCoreTestModule) + )] +public class EShopPluginsFlashSalesDomainTestModule : AbpModule +{ + +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanHasherTests.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanHasherTests.cs new file mode 100644 index 00000000..b0868cd8 --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanHasherTests.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; +public class FlashSalePlanHasherTests : FlashSalesDomainTestBase +{ + protected IFlashSalePlanHasher FlashSalePlanHasher { get; } + + public FlashSalePlanHasherTests() + { + FlashSalePlanHasher = GetRequiredService(); + } + + [Fact] + public async Task HashAsync() + { + var time1 = DateTime.Now; + var time2 = DateTime.Now.AddTicks(10); + var time3 = DateTime.Now.AddTicks(20); + + (await FlashSalePlanHasher.HashAsync(time1, time2, time3)) + .ShouldBe(await FlashSalePlanHasher.HashAsync(time1, time2, time3)); + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanTests.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanTests.cs new file mode 100644 index 00000000..90b7694b --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanTests.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; + +public class FlashSalePlanTests +{ + [Fact] + public void Should_Throw_InvalidEndTimeException() + { + Assert.Throws(() => + { + new FlashSalePlan( + id: Guid.NewGuid(), + tenantId: null, + storeId: Guid.NewGuid(), + beginTime: DateTime.Now, + endTime: DateTime.Now.AddSeconds(-1), + productId: Guid.NewGuid(), + productSkuId: Guid.NewGuid(), + isPublished: true + ); + }); + } + + [Fact] + public void SetTimeRange() + { + var existPlan = new FlashSalePlan( + id: Guid.NewGuid(), + tenantId: null, + storeId: Guid.NewGuid(), + beginTime: DateTime.Now, + endTime: DateTime.Now.AddSeconds(1), + productId: Guid.NewGuid(), + productSkuId: Guid.NewGuid(), + isPublished: true + ); + + var newBeginTime = DateTime.Now; + var newEndTime = newBeginTime.AddMinutes(1); + + existPlan.SetTimeRange(newBeginTime, newEndTime); + + existPlan.BeginTime.ShouldBe(newBeginTime); + existPlan.EndTime.ShouldBe(newEndTime); + } + + [Fact] + public void SetTimeRange_Should_Throw_InvalidEndTimeException_When_Set_InvalidEndTime() + { + var existPlan = new FlashSalePlan( + id: Guid.NewGuid(), + tenantId: null, + storeId: Guid.NewGuid(), + beginTime: DateTime.Now, + endTime: DateTime.Now.AddSeconds(1), + productId: Guid.NewGuid(), + productSkuId: Guid.NewGuid(), + isPublished: true + ); + + Assert.Throws(() => existPlan.SetTimeRange(DateTime.Now, DateTime.Now.AddMinutes(-1))); + } + + [Fact] + public void SetProductSku() + { + var existPlan = new FlashSalePlan( + id: Guid.NewGuid(), + tenantId: null, + storeId: Guid.NewGuid(), + beginTime: DateTime.Now, + endTime: DateTime.Now.AddSeconds(1), + productId: Guid.NewGuid(), + productSkuId: Guid.NewGuid(), + isPublished: true + ); + + var newStoreId = Guid.NewGuid(); + var newProductId = Guid.NewGuid(); + var newProductSkuId = Guid.NewGuid(); + + existPlan.SetProductSku(newStoreId, newProductId, newProductSkuId); + + existPlan.StoreId.ShouldBe(newStoreId); + existPlan.ProductId.ShouldBe(newProductId); + existPlan.ProductSkuId.ShouldBe(newProductSkuId); + } + + [Fact] + public void SetPublished() + { + var existPlan = new FlashSalePlan( + id: Guid.NewGuid(), + tenantId: null, + storeId: Guid.NewGuid(), + beginTime: DateTime.Now, + endTime: DateTime.Now.AddSeconds(1), + productId: Guid.NewGuid(), + productSkuId: Guid.NewGuid(), + isPublished: true + ); + + existPlan.SetPublished(false); + + existPlan.IsPublished.ShouldBe(false); + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/CreateFlashSaleOrderCompleteEventHandlerTests.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/CreateFlashSaleOrderCompleteEventHandlerTests.cs new file mode 100644 index 00000000..5172e23f --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/CreateFlashSaleOrderCompleteEventHandlerTests.cs @@ -0,0 +1,85 @@ +using System.Threading.Tasks; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; +using Shouldly; +using Volo.Abp.Guids; +using Xunit; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; + +public class CreateFlashSaleOrderCompleteEventHandlerTests : FlashSalesDomainTestBase +{ + protected IFlashSaleResultRepository FlashSaleResultRepository { get; } + protected CreateFlashSaleOrderCompleteEventHandler CreateFlashSaleOrderCompleteEventHandler { get; } + protected IGuidGenerator GuidGenerator { get; } + + public CreateFlashSaleOrderCompleteEventHandlerTests() + { + FlashSaleResultRepository = GetRequiredService(); + CreateFlashSaleOrderCompleteEventHandler = GetRequiredService(); + GuidGenerator = GetRequiredService(); + } + + [Fact] + public async Task HandleEventAsync_When_Create_Order_Success() + { + var existFlashResult = await CreateFlashSaleResultAsync(); + var createFlashSaleOrderCompleteEto = new CreateFlashSaleOrderCompleteEto() + { + TenantId = existFlashResult.TenantId, + PendingResultId = existFlashResult.Id, + Success = true, + StoreId = existFlashResult.StoreId, + PlanId = existFlashResult.PlanId, + OrderId = GuidGenerator.Create(), + Reason = null, + UserId = existFlashResult.UserId, + }; + + await CreateFlashSaleOrderCompleteEventHandler.HandleEventAsync(createFlashSaleOrderCompleteEto); + + var flashResult = await FlashSaleResultRepository.GetAsync(existFlashResult.Id); + flashResult.Status.ShouldBe(FlashSaleResultStatus.Successful); + flashResult.OrderId.ShouldBe(createFlashSaleOrderCompleteEto.OrderId); + flashResult.Reason.ShouldBe(null); + } + + [Fact] + public async Task HandleEventAsync_When_Create_Order_Failed() + { + var existFlashResult = await CreateFlashSaleResultAsync(); + var createFlashSaleOrderCompleteEto = new CreateFlashSaleOrderCompleteEto() + { + TenantId = existFlashResult.TenantId, + PendingResultId = existFlashResult.Id, + Success = false, + StoreId = FlashSalesTestData.Store1Id, + PlanId = existFlashResult.PlanId, + OrderId = null, + Reason = "Failed reason", + UserId = existFlashResult.UserId, + }; + + await CreateFlashSaleOrderCompleteEventHandler.HandleEventAsync(createFlashSaleOrderCompleteEto); + + var flashResult = await FlashSaleResultRepository.GetAsync(existFlashResult.Id); + flashResult.Status.ShouldBe(FlashSaleResultStatus.Failed); + flashResult.OrderId.ShouldBe(null); + flashResult.Reason.ShouldBe("Failed reason"); + } + + public async Task CreateFlashSaleResultAsync() + { + return await WithUnitOfWorkAsync(async () => + { + var flashSaleResult = new FlashSaleResult( + GuidGenerator.Create(), + null, + FlashSalesTestData.Store1Id, + FlashSalesTestData.Plan1Id, + GuidGenerator.Create()); + await FlashSaleResultRepository.InsertAsync(flashSaleResult); + + return flashSaleResult; + }); + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultTests.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultTests.cs new file mode 100644 index 00000000..7464ce4b --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultTests.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; + +public class FlashSaleResultTests +{ + [Fact] + public void MarkAsSuccessful() + { + var flashSaleResult = new FlashSaleResult( + id: Guid.NewGuid(), + tenantId: null, + storeId: Guid.NewGuid(), + planId: Guid.NewGuid(), + userId: Guid.NewGuid() + ); + + flashSaleResult.Status.ShouldBe(FlashSaleResultStatus.Pending); + + var orderId = Guid.NewGuid(); + flashSaleResult.MarkAsSuccessful(orderId); + + flashSaleResult.Status.ShouldBe(FlashSaleResultStatus.Successful); + flashSaleResult.OrderId.ShouldBe(orderId); + } + + [Fact] + public void MarkAsSuccessful_Should_Throw_FlashSaleResultStatusNotPendingException_When_Status_Not_Pending() + { + var flashSaleResult = new FlashSaleResult( + id: Guid.NewGuid(), + tenantId: null, + storeId: Guid.NewGuid(), + planId: Guid.NewGuid(), + userId: Guid.NewGuid() + ); + + flashSaleResult.Status.ShouldBe(FlashSaleResultStatus.Pending); + + var orderId = Guid.NewGuid(); + flashSaleResult.MarkAsSuccessful(orderId); + flashSaleResult.Reason.ShouldBe(null); + + Assert.Throws(() => + { + flashSaleResult.MarkAsSuccessful(orderId); + }); + } + + [Fact] + public void MarkAsFailed() + { + var flashSaleResult = new FlashSaleResult( + id: Guid.NewGuid(), + tenantId: null, + storeId: Guid.NewGuid(), + planId: Guid.NewGuid(), + userId: Guid.NewGuid() + ); + + flashSaleResult.Status.ShouldBe(FlashSaleResultStatus.Pending); + + flashSaleResult.MarkAsFailed("reason"); + + flashSaleResult.Status.ShouldBe(FlashSaleResultStatus.Failed); + flashSaleResult.OrderId.ShouldBe(null); + flashSaleResult.Reason.ShouldBe("reason"); + } + + [Fact] + public void MarkAsFailed_Should_Throw_FlashSaleResultStatusNotPendingException_When_Status_Not_Pending() + { + var flashSaleResult = new FlashSaleResult( + id: Guid.NewGuid(), + tenantId: null, + storeId: Guid.NewGuid(), + planId: Guid.NewGuid(), + userId: Guid.NewGuid() + ); + + flashSaleResult.Status.ShouldBe(FlashSaleResultStatus.Pending); + + flashSaleResult.MarkAsFailed("reason"); + + Assert.Throws(() => + { + flashSaleResult.MarkAsFailed("reason"); + }); + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalesDomainTestBase.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalesDomainTestBase.cs new file mode 100644 index 00000000..58e775a1 --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalesDomainTestBase.cs @@ -0,0 +1,9 @@ +namespace EasyAbp.EShop.Plugins.FlashSales; + +/* Inherit from this class for your domain layer tests. + * See SampleManager_Tests for example. + */ +public abstract class FlashSalesDomainTestBase : FlashSalesTestBase +{ + +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/FodyWeavers.xml b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/FodyWeavers.xml new file mode 100644 index 00000000..1715698c --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/FodyWeavers.xsd b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/FodyWeavers.xsd new file mode 100644 index 00000000..ffa6fc4b --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Domain.Tests/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests.abppkg.json b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests.abppkg.json new file mode 100644 index 00000000..a686451f --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests.abppkg.json @@ -0,0 +1,3 @@ +{ + "role": "lib.test" +} \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests.csproj b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests.csproj new file mode 100644 index 00000000..15633c50 --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests.csproj @@ -0,0 +1,16 @@ + + + + net6.0 + + + + + + + + + + + + diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests/EasyAbp/EShop/Plugins/FlashSales/EntityFrameworkCore/EShopPluginsFlashSalesEntityFrameworkCoreTestModule.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests/EasyAbp/EShop/Plugins/FlashSales/EntityFrameworkCore/EShopPluginsFlashSalesEntityFrameworkCoreTestModule.cs new file mode 100644 index 00000000..39b1283c --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests/EasyAbp/EShop/Plugins/FlashSales/EntityFrameworkCore/EShopPluginsFlashSalesEntityFrameworkCoreTestModule.cs @@ -0,0 +1,42 @@ +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Sqlite; +using Volo.Abp.Modularity; + +namespace EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore; + +[DependsOn( + typeof(EShopPluginsFlashSalesTestBaseModule), + typeof(EShopPluginsFlashSalesEntityFrameworkCoreModule), + typeof(AbpEntityFrameworkCoreSqliteModule) + )] +public class EShopPluginsFlashSalesEntityFrameworkCoreTestModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + var sqliteConnection = CreateDatabaseAndGetConnection(); + + Configure(options => + { + options.Configure(abpDbContextConfigurationContext => + { + abpDbContextConfigurationContext.DbContextOptions.UseSqlite(sqliteConnection); + }); + }); + } + + private static SqliteConnection CreateDatabaseAndGetConnection() + { + var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + new FlashSalesDbContext( + new DbContextOptionsBuilder().UseSqlite(connection).Options + ).GetService().CreateTables(); + + return connection; + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests/EasyAbp/EShop/Plugins/FlashSales/EntityFrameworkCore/FlashSalesEntityFrameworkCoreTestBase.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests/EasyAbp/EShop/Plugins/FlashSales/EntityFrameworkCore/FlashSalesEntityFrameworkCoreTestBase.cs new file mode 100644 index 00000000..0ba954e8 --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests/EasyAbp/EShop/Plugins/FlashSales/EntityFrameworkCore/FlashSalesEntityFrameworkCoreTestBase.cs @@ -0,0 +1,9 @@ +namespace EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore; + +/* This class can be used as a base class for EF Core integration tests, + * while SampleRepository_Tests uses a different approach. + */ +public abstract class FlashSalesEntityFrameworkCoreTestBase : FlashSalesTestBase +{ + +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests/FodyWeavers.xml b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests/FodyWeavers.xml new file mode 100644 index 00000000..1715698c --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests/FodyWeavers.xsd b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests/FodyWeavers.xsd new file mode 100644 index 00000000..ffa6fc4b --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore.Tests/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp.abppkg.json b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp.abppkg.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp.abppkg.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp.csproj b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp.csproj new file mode 100644 index 00000000..7f9b26a7 --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp.csproj @@ -0,0 +1,32 @@ + + + + Exe + net6.0 + + + + + + + + + + + + + PreserveNewest + Always + + + + PreserveNewest + Always + + + + + + + + diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/EasyAbp/EShop/Plugins/FlashSales/ClientDemoService.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/EasyAbp/EShop/Plugins/FlashSales/ClientDemoService.cs new file mode 100644 index 00000000..e5c5c20b --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/EasyAbp/EShop/Plugins/FlashSales/ClientDemoService.cs @@ -0,0 +1,145 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using IdentityModel.Client; +using Microsoft.Extensions.Configuration; +using Volo.Abp.DependencyInjection; +using Volo.Abp.IdentityModel; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +public class ClientDemoService : ITransientDependency +{ + private readonly IIdentityModelAuthenticationService _authenticationService; + private readonly IConfiguration _configuration; + + public ClientDemoService( + IIdentityModelAuthenticationService authenticationService, + IConfiguration configuration) + { + _authenticationService = authenticationService; + _configuration = configuration; + } + + public async Task RunAsync() + { + await TestWithDynamicProxiesAsync(); + await TestWithHttpClientAndIdentityModelAuthenticationServiceAsync(); + await TestAllManuallyAsync(); + } + + /* Shows how to perform an HTTP request to the API using ABP's dynamic c# proxy + * feature. It is just simple as calling a local service method. + * Authorization and HTTP request details are handled by the ABP framework. + */ + private async Task TestWithDynamicProxiesAsync() + { + Console.WriteLine(); + Console.WriteLine($"***** {nameof(TestWithDynamicProxiesAsync)} *****"); + } + + /* Shows how to use HttpClient to perform a request to the HTTP API. + * It uses ABP's IIdentityModelAuthenticationService to simplify obtaining access tokens. + */ + private async Task TestWithHttpClientAndIdentityModelAuthenticationServiceAsync() + { + Console.WriteLine(); + Console.WriteLine($"***** {nameof(TestWithHttpClientAndIdentityModelAuthenticationServiceAsync)} *****"); + + //Get access token using ABP's IIdentityModelAuthenticationService + + var accessToken = await _authenticationService.GetAccessTokenAsync( + new IdentityClientConfiguration( + _configuration["IdentityClients:Default:Authority"], + _configuration["IdentityClients:Default:Scope"], + _configuration["IdentityClients:Default:ClientId"], + _configuration["IdentityClients:Default:ClientSecret"], + _configuration["IdentityClients:Default:GrantType"], + _configuration["IdentityClients:Default:UserName"], + _configuration["IdentityClients:Default:UserPassword"] + ) + ); + + //Perform the actual HTTP request + + using (var httpClient = new HttpClient()) + { + httpClient.SetBearerToken(accessToken); + + var url = _configuration["RemoteServices:FlashSales:BaseUrl"] + + "api/FlashSales/sample/authorized"; + + var responseMessage = await httpClient.GetAsync(url); + if (responseMessage.IsSuccessStatusCode) + { + var responseString = await responseMessage.Content.ReadAsStringAsync(); + Console.WriteLine("Result: " + responseString); + } + else + { + throw new Exception("Remote server returns error code: " + responseMessage.StatusCode); + } + } + } + + /* Shows how to use HttpClient to perform a request to the HTTP API. + * It obtains access token using IdentityServer's API. See its documentation: + * https://identityserver4.readthedocs.io/en/latest/quickstarts/2_resource_owner_passwords.html + */ + private async Task TestAllManuallyAsync() + { + Console.WriteLine(); + Console.WriteLine($"***** {nameof(TestAllManuallyAsync)} *****"); + + //Obtain access token from the IDS4 server + + // discover endpoints from metadata + var client = new HttpClient(); + var disco = await client.GetDiscoveryDocumentAsync(_configuration["IdentityClients:Default:Authority"]); + if (disco.IsError) + { + Console.WriteLine(disco.Error); + return; + } + + // request token + var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest + { + Address = disco.TokenEndpoint, + ClientId = _configuration["IdentityClients:Default:ClientId"], + ClientSecret = _configuration["IdentityClients:Default:ClientSecret"], + UserName = _configuration["IdentityClients:Default:UserName"], + Password = _configuration["IdentityClients:Default:UserPassword"], + Scope = _configuration["IdentityClients:Default:Scope"] + }); + + if (tokenResponse.IsError) + { + Console.WriteLine(tokenResponse.Error); + return; + } + + Console.WriteLine(tokenResponse.Json); + + //Perform the actual HTTP request + + using (var httpClient = new HttpClient()) + { + httpClient.SetBearerToken(tokenResponse.AccessToken); + + var url = _configuration["RemoteServices:FlashSales:BaseUrl"] + + "api/FlashSales/sample/authorized"; + + var responseMessage = await httpClient.GetAsync(url); + if (responseMessage.IsSuccessStatusCode) + { + var responseString = await responseMessage.Content.ReadAsStringAsync(); + Console.WriteLine("Result: " + responseString); + } + else + { + throw new Exception("Remote server returns error code: " + responseMessage.StatusCode); + } + } + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/EasyAbp/EShop/Plugins/FlashSales/ConsoleTestAppHostedService.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/EasyAbp/EShop/Plugins/FlashSales/ConsoleTestAppHostedService.cs new file mode 100644 index 00000000..f1d41e9b --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/EasyAbp/EShop/Plugins/FlashSales/ConsoleTestAppHostedService.cs @@ -0,0 +1,40 @@ +using Microsoft.Extensions.Hosting; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +public class ConsoleTestAppHostedService : IHostedService +{ + private readonly IConfiguration _configuration; + + public ConsoleTestAppHostedService(IConfiguration configuration) + { + _configuration = configuration; + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + using (var application = await AbpApplicationFactory.CreateAsync(options => + { + options.Services.ReplaceConfiguration(_configuration); + options.UseAutofac(); + })) + { + await application.InitializeAsync(); + + var demo = application.ServiceProvider.GetRequiredService(); + await demo.RunAsync(); + + await application.ShutdownAsync(); + } + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesConsoleApiClientModule.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesConsoleApiClientModule.cs new file mode 100644 index 00000000..80a95f2c --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesConsoleApiClientModule.cs @@ -0,0 +1,15 @@ +using Volo.Abp.Autofac; +using Volo.Abp.Http.Client.IdentityModel; +using Volo.Abp.Modularity; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +[DependsOn( + typeof(AbpAutofacModule), + typeof(EShopPluginsFlashSalesHttpApiClientModule), + typeof(AbpHttpClientIdentityModelModule) + )] +public class EShopPluginsFlashSalesConsoleApiClientModule : AbpModule +{ + +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/EasyAbp/EShop/Plugins/FlashSales/Program.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/EasyAbp/EShop/Plugins/FlashSales/Program.cs new file mode 100644 index 00000000..d1f86ddb --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/EasyAbp/EShop/Plugins/FlashSales/Program.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +class Program +{ + static async Task Main(string[] args) + { + await CreateHostBuilder(args).RunConsoleAsync(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .AddAppSettingsSecretsJson() + .ConfigureServices((hostContext, services) => + { + services.AddHostedService(); + }); +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/appsettings.json b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/appsettings.json new file mode 100644 index 00000000..9ec421ef --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/appsettings.json @@ -0,0 +1,21 @@ +{ + "RemoteServices": { + "Default": { + "BaseUrl": "https://localhost:44306/" + }, + "FlashSales": { + "BaseUrl": "https://localhost:44358/" + } + }, + "IdentityClients": { + "Default": { + "GrantType": "password", + "ClientId": "FlashSales_App", + "ClientSecret": "1q2w3e*", + "UserName": "admin", + "UserPassword": "1q2w3E*", + "Authority": "https://localhost:44306/", + "Scope": "FlashSales" + } + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/appsettings.secrets.json b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/appsettings.secrets.json new file mode 100644 index 00000000..7a73a41b --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.HttpApi.Client.ConsoleTestApp/appsettings.secrets.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests.abppkg.json b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests.abppkg.json new file mode 100644 index 00000000..a686451f --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests.abppkg.json @@ -0,0 +1,3 @@ +{ + "role": "lib.test" +} \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests.csproj b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests.csproj new file mode 100644 index 00000000..97ff3afc --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + + + + + + + + + + + diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/EasyAbp/EShop/Plugins/FlashSales/MongoDB/EShopPluginsFlashSalesMongoDbTestModule.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/EasyAbp/EShop/Plugins/FlashSales/MongoDB/EShopPluginsFlashSalesMongoDbTestModule.cs new file mode 100644 index 00000000..3bc8bcd3 --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/EasyAbp/EShop/Plugins/FlashSales/MongoDB/EShopPluginsFlashSalesMongoDbTestModule.cs @@ -0,0 +1,26 @@ +using System; +using Volo.Abp.Data; +using Volo.Abp.Modularity; +using Volo.Abp.Uow; + +namespace EasyAbp.EShop.Plugins.FlashSales.MongoDB; + +[DependsOn( + typeof(EShopPluginsFlashSalesTestBaseModule), + typeof(EShopPluginsFlashSalesMongoDbModule) + )] +public class EShopPluginsFlashSalesMongoDbTestModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + var stringArray = MongoDbFixture.ConnectionString.Split('?'); + var connectionString = stringArray[0].EnsureEndsWith('/') + + "Db_" + + Guid.NewGuid().ToString("N") + "/?" + stringArray[1]; + + Configure(options => + { + options.ConnectionStrings.Default = connectionString; + }); + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/EasyAbp/EShop/Plugins/FlashSales/MongoDB/FlashSalesMongoDbTestBase.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/EasyAbp/EShop/Plugins/FlashSales/MongoDB/FlashSalesMongoDbTestBase.cs new file mode 100644 index 00000000..585e5ef6 --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/EasyAbp/EShop/Plugins/FlashSales/MongoDB/FlashSalesMongoDbTestBase.cs @@ -0,0 +1,9 @@ +namespace EasyAbp.EShop.Plugins.FlashSales.MongoDB; + +/* This class can be used as a base class for MongoDB integration tests, + * while SampleRepository_Tests uses a different approach. + */ +public abstract class FlashSalesMongoDbTestBase : FlashSalesTestBase +{ + +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/EasyAbp/EShop/Plugins/FlashSales/MongoDB/MongoDbFixture.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/EasyAbp/EShop/Plugins/FlashSales/MongoDB/MongoDbFixture.cs new file mode 100644 index 00000000..24e29e8b --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/EasyAbp/EShop/Plugins/FlashSales/MongoDB/MongoDbFixture.cs @@ -0,0 +1,21 @@ +using System; +using Mongo2Go; + +namespace EasyAbp.EShop.Plugins.FlashSales.MongoDB; + +public class MongoDbFixture : IDisposable +{ + private static readonly MongoDbRunner MongoDbRunner; + public static readonly string ConnectionString; + + static MongoDbFixture() + { + MongoDbRunner = MongoDbRunner.Start(singleNodeReplSet: true, singleNodeReplSetWaitTimeout: 20); + ConnectionString = MongoDbRunner.ConnectionString; + } + + public void Dispose() + { + MongoDbRunner?.Dispose(); + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/EasyAbp/EShop/Plugins/FlashSales/MongoDB/MongoTestCollection.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/EasyAbp/EShop/Plugins/FlashSales/MongoDB/MongoTestCollection.cs new file mode 100644 index 00000000..3c9a8af5 --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/EasyAbp/EShop/Plugins/FlashSales/MongoDB/MongoTestCollection.cs @@ -0,0 +1,9 @@ +using Xunit; + +namespace EasyAbp.EShop.Plugins.FlashSales.MongoDB; + +[CollectionDefinition(Name)] +public class MongoTestCollection : ICollectionFixture +{ + public const string Name = "MongoDB Collection"; +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/FodyWeavers.xml b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/FodyWeavers.xml new file mode 100644 index 00000000..1715698c --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/FodyWeavers.xsd b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/FodyWeavers.xsd new file mode 100644 index 00000000..ffa6fc4b --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.MongoDB.Tests/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp.EShop.Plugins.FlashSales.TestBase.abppkg.json b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp.EShop.Plugins.FlashSales.TestBase.abppkg.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp.EShop.Plugins.FlashSales.TestBase.abppkg.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp.EShop.Plugins.FlashSales.TestBase.csproj b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp.EShop.Plugins.FlashSales.TestBase.csproj new file mode 100644 index 00000000..0c0c660b --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp.EShop.Plugins.FlashSales.TestBase.csproj @@ -0,0 +1,21 @@ + + + + net6.0 + + + + + + + + + + + + + + + + + diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesTestBaseModule.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesTestBaseModule.cs new file mode 100644 index 00000000..5bc24871 --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesTestBaseModule.cs @@ -0,0 +1,41 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp; +using Volo.Abp.Authorization; +using Volo.Abp.Autofac; +using Volo.Abp.Data; +using Volo.Abp.Modularity; +using Volo.Abp.Threading; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +[DependsOn( + typeof(AbpAutofacModule), + typeof(AbpTestBaseModule), + typeof(AbpAuthorizationModule), + typeof(EShopPluginsFlashSalesDomainModule) + )] +public class EShopPluginsFlashSalesTestBaseModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAlwaysAllowAuthorization(); + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + SeedTestData(context); + } + + private static void SeedTestData(ApplicationInitializationContext context) + { + AsyncHelper.RunSync(async () => + { + using (var scope = context.ServiceProvider.CreateScope()) + { + await scope.ServiceProvider + .GetRequiredService() + .SeedAsync(); + } + }); + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp/EShop/Plugins/FlashSales/FlashSalesDataSeedContributor.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp/EShop/Plugins/FlashSales/FlashSalesDataSeedContributor.cs new file mode 100644 index 00000000..5b21992c --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp/EShop/Plugins/FlashSales/FlashSalesDataSeedContributor.cs @@ -0,0 +1,32 @@ +using System.Threading.Tasks; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Guids; +using Volo.Abp.MultiTenancy; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +public class FlashSalesDataSeedContributor : IDataSeedContributor, ITransientDependency +{ + private readonly IGuidGenerator _guidGenerator; + private readonly ICurrentTenant _currentTenant; + + public FlashSalesDataSeedContributor( + IGuidGenerator guidGenerator, ICurrentTenant currentTenant) + { + _guidGenerator = guidGenerator; + _currentTenant = currentTenant; + } + + public Task SeedAsync(DataSeedContext context) + { + /* Instead of returning the Task.CompletedTask, you can insert your test data + * at this point! + */ + + using (_currentTenant.Change(context?.TenantId)) + { + return Task.CompletedTask; + } + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp/EShop/Plugins/FlashSales/FlashSalesTestBase.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp/EShop/Plugins/FlashSales/FlashSalesTestBase.cs new file mode 100644 index 00000000..5522e93c --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp/EShop/Plugins/FlashSales/FlashSalesTestBase.cs @@ -0,0 +1,59 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp; +using Volo.Abp.Modularity; +using Volo.Abp.Uow; +using Volo.Abp.Testing; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +/* All test classes are derived from this class, directly or indirectly. */ +public abstract class FlashSalesTestBase : AbpIntegratedTest + where TStartupModule : IAbpModule +{ + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + + protected virtual Task WithUnitOfWorkAsync(Func func) + { + return WithUnitOfWorkAsync(new AbpUnitOfWorkOptions(), func); + } + + protected virtual async Task WithUnitOfWorkAsync(AbpUnitOfWorkOptions options, Func action) + { + using (var scope = ServiceProvider.CreateScope()) + { + var uowManager = scope.ServiceProvider.GetRequiredService(); + + using (var uow = uowManager.Begin(options)) + { + await action(); + + await uow.CompleteAsync(); + } + } + } + + protected virtual Task WithUnitOfWorkAsync(Func> func) + { + return WithUnitOfWorkAsync(new AbpUnitOfWorkOptions(), func); + } + + protected virtual async Task WithUnitOfWorkAsync(AbpUnitOfWorkOptions options, Func> func) + { + using (var scope = ServiceProvider.CreateScope()) + { + var uowManager = scope.ServiceProvider.GetRequiredService(); + + using (var uow = uowManager.Begin(options)) + { + var result = await func(); + await uow.CompleteAsync(); + return result; + } + } + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp/EShop/Plugins/FlashSales/FlashSalesTestData.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp/EShop/Plugins/FlashSales/FlashSalesTestData.cs new file mode 100644 index 00000000..baa98bd9 --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp/EShop/Plugins/FlashSales/FlashSalesTestData.cs @@ -0,0 +1,28 @@ +using System; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +public static class FlashSalesTestData +{ + public static Guid Plan1Id { get; } = Guid.NewGuid(); + + public static Guid Result1Id { get; } = Guid.NewGuid(); + + public static Guid Store1Id { get; } = Guid.NewGuid(); + + public static Guid Product1Id { get; } = Guid.NewGuid(); + + public static Guid ProductSku1Id { get; } = Guid.NewGuid(); + + public static Guid ProductSku2Id { get; } = Guid.NewGuid(); + + public static Guid ProductSku3Id { get; } = Guid.NewGuid(); + + public static Guid ProductDetail1Id { get; } = Guid.NewGuid(); + + public static Guid ProductDetail2Id { get; } = Guid.NewGuid(); + + public static DateTime ProductLastModificationTime { get; } = DateTime.Today; + + public static DateTime ProductDetailLastModificationTime { get; } = DateTime.Today; +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp/EShop/Plugins/FlashSales/Security/FakeCurrentPrincipalAccessor.cs b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp/EShop/Plugins/FlashSales/Security/FakeCurrentPrincipalAccessor.cs new file mode 100644 index 00000000..db797d86 --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/EasyAbp/EShop/Plugins/FlashSales/Security/FakeCurrentPrincipalAccessor.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Security.Claims; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Security.Claims; + +namespace EasyAbp.EShop.Plugins.FlashSales.Security; + +[Dependency(ReplaceServices = true)] +public class FakeCurrentPrincipalAccessor : ThreadCurrentPrincipalAccessor +{ + protected override ClaimsPrincipal GetClaimsPrincipal() + { + return GetPrincipal(); + } + + private ClaimsPrincipal _principal; + + private ClaimsPrincipal GetPrincipal() + { + if (_principal == null) + { + lock (this) + { + if (_principal == null) + { + _principal = new ClaimsPrincipal( + new ClaimsIdentity( + new List + { + new Claim(AbpClaimTypes.UserId,"2e701e62-0953-4dd3-910b-dc6cc93ccb0d"), + new Claim(AbpClaimTypes.UserName,"admin"), + new Claim(AbpClaimTypes.Email,"admin@abp.io") + } + ) + ); + } + } + } + + return _principal; + } +} diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/FodyWeavers.xml b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/FodyWeavers.xml new file mode 100644 index 00000000..1715698c --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/FodyWeavers.xsd b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/FodyWeavers.xsd new file mode 100644 index 00000000..ffa6fc4b --- /dev/null +++ b/plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.TestBase/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Application.Contracts/EShopSample.Application.Contracts.csproj b/samples/EShopSample/aspnet-core/src/EShopSample.Application.Contracts/EShopSample.Application.Contracts.csproj index 3ae2ee02..7c4bd05c 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Application.Contracts/EShopSample.Application.Contracts.csproj +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Application.Contracts/EShopSample.Application.Contracts.csproj @@ -1,4 +1,4 @@ - + @@ -12,6 +12,8 @@ + +
diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Application.Contracts/EShopSampleApplicationContractsModule.cs b/samples/EShopSample/aspnet-core/src/EShopSample.Application.Contracts/EShopSampleApplicationContractsModule.cs index 7d7e80d5..2d6f4ff4 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Application.Contracts/EShopSampleApplicationContractsModule.cs +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Application.Contracts/EShopSampleApplicationContractsModule.cs @@ -2,6 +2,8 @@ using EasyAbp.EShop.Plugins.Baskets; using EasyAbp.EShop.Plugins.Booking; using EasyAbp.EShop.Plugins.Coupons; +using EasyAbp.EShop.Plugins.FlashSales; +using EasyAbp.EShop.Products.Plugins.FlashSales; using EasyAbp.PaymentService; using EasyAbp.PaymentService.Prepayment; using EasyAbp.PaymentService.WeChatPay; @@ -27,6 +29,8 @@ namespace EShopSample typeof(EShopPluginsBasketsApplicationContractsModule), typeof(EShopPluginsBookingApplicationContractsModule), typeof(EShopPluginsCouponsApplicationContractsModule), + typeof(EShopPluginsFlashSalesApplicationContractsModule), + typeof(EShopProductsPluginsFlashSalesApplicationContractsModule), typeof(PaymentServiceApplicationContractsModule), typeof(PaymentServiceWeChatPayApplicationContractsModule), typeof(PaymentServicePrepaymentApplicationContractsModule) diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSample.Application.csproj b/samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSample.Application.csproj index 887ab86b..567ce31c 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSample.Application.csproj +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSample.Application.csproj @@ -13,6 +13,9 @@ + + + diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSampleApplicationModule.cs b/samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSampleApplicationModule.cs index 4fbbaf6d..c731c151 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSampleApplicationModule.cs +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSampleApplicationModule.cs @@ -1,8 +1,11 @@ using EasyAbp.EShop; using EasyAbp.EShop.Orders.Plugins.Coupons; +using EasyAbp.EShop.Orders.Plugins.FlashSales; using EasyAbp.EShop.Plugins.Baskets; using EasyAbp.EShop.Plugins.Booking; using EasyAbp.EShop.Plugins.Coupons; +using EasyAbp.EShop.Plugins.FlashSales; +using EasyAbp.EShop.Products.Plugins.FlashSales; using EasyAbp.PaymentService; using EasyAbp.PaymentService.Prepayment; using EasyAbp.PaymentService.WeChatPay; @@ -29,6 +32,9 @@ namespace EShopSample typeof(EShopPluginsBookingApplicationModule), typeof(EShopPluginsCouponsApplicationModule), typeof(EShopOrdersPluginsCouponsModule), + typeof(EShopPluginsFlashSalesApplicationModule), + typeof(EShopOrdersPluginsFlashSalesApplicationModule), + typeof(EShopProductsPluginsFlashSalesApplicationModule), typeof(PaymentServiceApplicationModule), typeof(PaymentServiceWeChatPayApplicationModule), typeof(PaymentServicePrepaymentApplicationModule) diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/EShopSample.Domain.Shared.csproj b/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/EShopSample.Domain.Shared.csproj index 2bdc577e..765700ae 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/EShopSample.Domain.Shared.csproj +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/EShopSample.Domain.Shared.csproj @@ -24,6 +24,7 @@ + diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/EShopSampleDomainSharedModule.cs b/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/EShopSampleDomainSharedModule.cs index 2815b4f4..b5deb325 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/EShopSampleDomainSharedModule.cs +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/EShopSampleDomainSharedModule.cs @@ -2,6 +2,7 @@ using EasyAbp.EShop.Plugins.Baskets; using EasyAbp.EShop.Plugins.Booking; using EasyAbp.EShop.Plugins.Coupons; +using EasyAbp.EShop.Plugins.FlashSales; using EasyAbp.PaymentService; using EasyAbp.PaymentService.Prepayment; using EasyAbp.PaymentService.WeChatPay; @@ -34,6 +35,7 @@ namespace EShopSample typeof(EShopPluginsBasketsDomainSharedModule), typeof(EShopPluginsBookingDomainSharedModule), typeof(EShopPluginsCouponsDomainSharedModule), + typeof(EShopPluginsFlashSalesDomainSharedModule), typeof(PaymentServiceDomainSharedModule), typeof(PaymentServiceWeChatPayDomainSharedModule), typeof(PaymentServicePrepaymentDomainSharedModule) diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Domain/EShopSample.Domain.csproj b/samples/EShopSample/aspnet-core/src/EShopSample.Domain/EShopSample.Domain.csproj index 967c7ef9..7d3f9600 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Domain/EShopSample.Domain.csproj +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Domain/EShopSample.Domain.csproj @@ -12,6 +12,7 @@ + diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Domain/EShopSampleDomainModule.cs b/samples/EShopSample/aspnet-core/src/EShopSample.Domain/EShopSampleDomainModule.cs index 81750873..0ece9d9c 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Domain/EShopSampleDomainModule.cs +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Domain/EShopSampleDomainModule.cs @@ -2,6 +2,7 @@ using EasyAbp.EShop.Plugins.Baskets; using EasyAbp.EShop.Plugins.Booking; using EasyAbp.EShop.Plugins.Coupons; +using EasyAbp.EShop.Plugins.FlashSales; using EasyAbp.PaymentService; using EasyAbp.PaymentService.Options; using EasyAbp.PaymentService.Payments; @@ -42,6 +43,7 @@ namespace EShopSample typeof(EShopPluginsBasketsDomainModule), typeof(EShopPluginsBookingDomainModule), typeof(EShopPluginsCouponsDomainModule), + typeof(EShopPluginsFlashSalesDomainModule), typeof(PaymentServiceDomainModule), typeof(PaymentServiceWeChatPayDomainModule), typeof(PaymentServicePrepaymentDomainModule) diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/EShopSample.EntityFrameworkCore.csproj b/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/EShopSample.EntityFrameworkCore.csproj index 849adca5..391ab2be 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/EShopSample.EntityFrameworkCore.csproj +++ b/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/EShopSample.EntityFrameworkCore.csproj @@ -12,6 +12,7 @@ + diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/EntityFrameworkCore/EShopSampleDbContext.cs b/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/EntityFrameworkCore/EShopSampleDbContext.cs index 48c7032c..53fbd9a9 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/EntityFrameworkCore/EShopSampleDbContext.cs +++ b/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/EntityFrameworkCore/EShopSampleDbContext.cs @@ -2,6 +2,7 @@ using EasyAbp.EShop.Plugins.Baskets.EntityFrameworkCore; using EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore; using EasyAbp.EShop.Plugins.Coupons.EntityFrameworkCore; +using EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore; using EasyAbp.PaymentService.EntityFrameworkCore; using EasyAbp.PaymentService.Prepayment.EntityFrameworkCore; using EasyAbp.PaymentService.WeChatPay.EntityFrameworkCore; @@ -31,9 +32,9 @@ namespace EShopSample.EntityFrameworkCore ITenantManagementDbContext { /* Add DbSet properties for your Aggregate Roots / Entities here. */ - + #region Entities from the modules - + /* Notice: We only implemented IIdentityDbContext and ITenantManagementDbContext * and replaced them for this DbContext. This allows you to perform JOIN * queries for the entities of these modules over the repositories easily. You @@ -44,7 +45,7 @@ namespace EShopSample.EntityFrameworkCore * More info: Replacing a DbContext of a module ensures that the related module * uses this DbContext on runtime. Otherwise, it will use its own DbContext class. */ - + //Identity public DbSet Users { get; set; } public DbSet Roles { get; set; } @@ -52,7 +53,7 @@ namespace EShopSample.EntityFrameworkCore public DbSet OrganizationUnits { get; set; } public DbSet SecurityLogs { get; set; } public DbSet LinkUsers { get; set; } - + // Tenant Management public DbSet Tenants { get; set; } public DbSet TenantConnectionStrings { get; set; } @@ -62,7 +63,6 @@ namespace EShopSample.EntityFrameworkCore public EShopSampleDbContext(DbContextOptions options) : base(options) { - } protected override void OnModelCreating(ModelBuilder builder) @@ -84,10 +84,11 @@ namespace EShopSample.EntityFrameworkCore builder.ConfigureEShopPluginsBaskets(); builder.ConfigureEShopPluginsBooking(); builder.ConfigureEShopPluginsCoupons(); + builder.ConfigureEShopPluginsFlashSales(); builder.ConfigurePaymentService(); builder.ConfigurePaymentServiceWeChatPay(); builder.ConfigurePaymentServicePrepayment(); - + /* Configure your own tables/entities inside here */ } } diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/EntityFrameworkCore/EShopSampleEntityFrameworkCoreModule.cs b/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/EntityFrameworkCore/EShopSampleEntityFrameworkCoreModule.cs index 4e0171cf..3f9262a9 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/EntityFrameworkCore/EShopSampleEntityFrameworkCoreModule.cs +++ b/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/EntityFrameworkCore/EShopSampleEntityFrameworkCoreModule.cs @@ -2,6 +2,7 @@ using EasyAbp.EShop.Plugins.Baskets.EntityFrameworkCore; using EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore; using EasyAbp.EShop.Plugins.Coupons.EntityFrameworkCore; +using EasyAbp.EShop.Plugins.FlashSales.EntityFrameworkCore; using EasyAbp.PaymentService.EntityFrameworkCore; using EasyAbp.PaymentService.Prepayment.EntityFrameworkCore; using EasyAbp.PaymentService.WeChatPay.EntityFrameworkCore; @@ -35,6 +36,7 @@ namespace EShopSample.EntityFrameworkCore typeof(EShopPluginsBasketsEntityFrameworkCoreModule), typeof(EShopPluginsBookingEntityFrameworkCoreModule), typeof(EShopPluginsCouponsEntityFrameworkCoreModule), + typeof(EShopPluginsFlashSalesEntityFrameworkCoreModule), typeof(PaymentServiceEntityFrameworkCoreModule), typeof(PaymentServiceWeChatPayEntityFrameworkCoreModule), typeof(PaymentServicePrepaymentEntityFrameworkCoreModule) diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/20220706160700_AddedFlashSales.Designer.cs b/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/20220706160700_AddedFlashSales.Designer.cs new file mode 100644 index 00000000..f80e833e --- /dev/null +++ b/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/20220706160700_AddedFlashSales.Designer.cs @@ -0,0 +1,5323 @@ +// +using System; +using EShopSample.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace EShopSample.Migrations +{ + [DbContext(typeof(EShopSampleDbContext))] + [Migration("20220706160700_AddedFlashSales")] + partial class AddedFlashSales + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) + .HasAnnotation("ProductVersion", "6.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("EasyAbp.EShop.Orders.Orders.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActualTotalPrice") + .HasColumnType("decimal(20,8)"); + + b.Property("CanceledTime") + .HasColumnType("datetime2"); + + b.Property("CancellationReason") + .HasColumnType("nvarchar(max)"); + + b.Property("CompletionTime") + .HasColumnType("datetime2"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Currency") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomerRemark") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomerUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("OrderNumber") + .HasColumnType("nvarchar(450)"); + + b.Property("OrderStatus") + .HasColumnType("int"); + + b.Property("PaidTime") + .HasColumnType("datetime2"); + + b.Property("PaymentExpiration") + .HasColumnType("datetime2"); + + b.Property("PaymentId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductTotalPrice") + .HasColumnType("decimal(20,8)"); + + b.Property("ReducedInventoryAfterPaymentTime") + .HasColumnType("datetime2"); + + b.Property("ReducedInventoryAfterPlacingTime") + .HasColumnType("datetime2"); + + b.Property("RefundAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("StaffRemark") + .HasColumnType("nvarchar(max)"); + + b.Property("StoreId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TotalDiscount") + .HasColumnType("decimal(20,8)"); + + b.Property("TotalPrice") + .HasColumnType("decimal(20,8)"); + + b.HasKey("Id"); + + b.HasIndex("OrderNumber") + .IsUnique() + .HasFilter("[OrderNumber] IS NOT NULL"); + + b.ToTable("EasyAbpEShopOrdersOrders", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Orders.Orders.OrderExtraFee", b => + { + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Key") + .HasColumnType("nvarchar(450)"); + + b.Property("Fee") + .HasColumnType("decimal(20,8)"); + + b.Property("RefundAmount") + .HasColumnType("decimal(20,8)"); + + b.HasKey("OrderId", "Name", "Key"); + + b.ToTable("EasyAbpEShopOrdersOrderExtraFees", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Orders.Orders.OrderLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActualTotalPrice") + .HasColumnType("decimal(20,8)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Currency") + .HasColumnType("nvarchar(max)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MediaResources") + .HasColumnType("nvarchar(max)"); + + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductDetailId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductDetailModificationTime") + .HasColumnType("datetime2"); + + b.Property("ProductDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductGroupDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductGroupName") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductModificationTime") + .HasColumnType("datetime2"); + + b.Property("ProductSkuId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductUniqueName") + .HasColumnType("nvarchar(max)"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("RefundAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("RefundedQuantity") + .HasColumnType("int"); + + b.Property("SkuDescription") + .HasColumnType("nvarchar(max)"); + + b.Property("SkuName") + .HasColumnType("nvarchar(max)"); + + b.Property("TotalDiscount") + .HasColumnType("decimal(20,8)"); + + b.Property("TotalPrice") + .HasColumnType("decimal(20,8)"); + + b.Property("UnitPrice") + .HasColumnType("decimal(20,8)"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.ToTable("EasyAbpEShopOrdersOrderLines", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Payments.Payments.Payment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActualPaymentAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("CanceledTime") + .HasColumnType("datetime2"); + + b.Property("CompletionTime") + .HasColumnType("datetime2"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Currency") + .HasColumnType("nvarchar(max)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExternalTradingCode") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("OriginalPaymentAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("PayeeAccount") + .HasColumnType("nvarchar(max)"); + + b.Property("PaymentDiscount") + .HasColumnType("decimal(20,8)"); + + b.Property("PaymentMethod") + .HasColumnType("nvarchar(max)"); + + b.Property("PendingRefundAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("RefundAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("EasyAbpEShopPaymentsPayments", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Payments.Payments.PaymentItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActualPaymentAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("ItemKey") + .HasColumnType("nvarchar(max)"); + + b.Property("ItemType") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("OriginalPaymentAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("PaymentDiscount") + .HasColumnType("decimal(20,8)"); + + b.Property("PaymentId") + .HasColumnType("uniqueidentifier"); + + b.Property("PendingRefundAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("RefundAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("StoreId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("PaymentId"); + + b.ToTable("EasyAbpEShopPaymentsPaymentItems", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Payments.Refunds.Refund", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CanceledTime") + .HasColumnType("datetime2"); + + b.Property("CompletedTime") + .HasColumnType("datetime2"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Currency") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomerRemark") + .HasColumnType("nvarchar(max)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DisplayReason") + .HasColumnType("nvarchar(max)"); + + b.Property("ExternalTradingCode") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("PaymentId") + .HasColumnType("uniqueidentifier"); + + b.Property("RefundAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("RefundPaymentMethod") + .HasColumnType("nvarchar(max)"); + + b.Property("StaffRemark") + .HasColumnType("nvarchar(max)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("EasyAbpEShopPaymentsRefunds", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Payments.Refunds.RefundItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("CustomerRemark") + .HasColumnType("nvarchar(max)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + + b.Property("PaymentItemId") + .HasColumnType("uniqueidentifier"); + + b.Property("RefundAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("RefundId") + .HasColumnType("uniqueidentifier"); + + b.Property("StaffRemark") + .HasColumnType("nvarchar(max)"); + + b.Property("StoreId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RefundId"); + + b.ToTable("EasyAbpEShopPaymentsRefundItems", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Payments.Refunds.RefundItemOrderExtraFee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Key") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("RefundAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("RefundItemId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RefundItemId"); + + b.ToTable("EasyAbpEShopPaymentsRefundItemOrderExtraFees", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Payments.Refunds.RefundItemOrderLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("OrderLineId") + .HasColumnType("uniqueidentifier"); + + b.Property("RefundAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("RefundItemId") + .HasColumnType("uniqueidentifier"); + + b.Property("RefundedQuantity") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RefundItemId"); + + b.ToTable("EasyAbpEShopPaymentsRefundItemOrderLines", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Plugins.Baskets.BasketItems.BasketItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BasketName") + .HasColumnType("nvarchar(max)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Currency") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Inventory") + .HasColumnType("int"); + + b.Property("IsInvalid") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MediaResources") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductSkuId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductUniqueName") + .HasColumnType("nvarchar(max)"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("SkuDescription") + .HasColumnType("nvarchar(max)"); + + b.Property("SkuName") + .HasColumnType("nvarchar(max)"); + + b.Property("StoreId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TotalDiscount") + .HasColumnType("decimal(20,8)"); + + b.Property("TotalPrice") + .HasColumnType("decimal(20,8)"); + + b.Property("UnitPrice") + .HasColumnType("decimal(20,8)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("EasyAbpEShopPluginsBasketsBasketItems", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Plugins.Baskets.ProductUpdates.ProductUpdate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ProductSkuId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ProductSkuId"); + + b.ToTable("EasyAbpEShopPluginsBasketsProductUpdates", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Plugins.Coupons.Coupons.Coupon", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CouponTemplateId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Currency") + .HasColumnType("nvarchar(max)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DiscountedAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("ExpirationTime") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UsedTime") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("EasyAbpEShopPluginsCouponsCoupons", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Plugins.Coupons.CouponTemplates.CouponTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConditionAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("CouponType") + .HasColumnType("int"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Currency") + .HasColumnType("nvarchar(max)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("DiscountAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsUnscoped") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("StoreId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UniqueName") + .HasColumnType("nvarchar(max)"); + + b.Property("UsableBeginTime") + .HasColumnType("datetime2"); + + b.Property("UsableDuration") + .HasColumnType("time"); + + b.Property("UsableEndTime") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("EasyAbpEShopPluginsCouponsCouponTemplates", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Plugins.Coupons.CouponTemplates.CouponTemplateScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CouponTemplateId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ProductGroupName") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductSkuId") + .HasColumnType("uniqueidentifier"); + + b.Property("StoreId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("CouponTemplateId"); + + b.ToTable("EasyAbpEShopPluginsCouponsCouponTemplateScopes", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.FlashSalePlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BeginTime") + .HasColumnType("datetime2"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("EndTime") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsPublished") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductSkuId") + .HasColumnType("uniqueidentifier"); + + b.Property("StoreId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("EasyAbpEShopPluginsFlashSalesFlashSalePlans", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults.FlashSaleResult", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + + b.Property("PlanId") + .HasColumnType("uniqueidentifier"); + + b.Property("Reason") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("StoreId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("EasyAbpEShopPluginsFlashSalesFlashSaleResults", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Products.Categories.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .HasColumnType("nvarchar(max)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsHidden") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("MediaResources") + .HasColumnType("nvarchar(max)"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UniqueName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EasyAbpEShopProductsCategories", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Products.ProductCategories.ProductCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CategoryId") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DisplayOrder") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("EasyAbpEShopProductsProductCategories", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Products.ProductDetailHistories.ProductDetailHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("ModificationTime") + .HasColumnType("datetime2"); + + b.Property("ProductDetailId") + .HasColumnType("uniqueidentifier"); + + b.Property("SerializedEntityData") + .HasColumnType("nvarchar(max)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ModificationTime"); + + b.HasIndex("ProductDetailId"); + + b.ToTable("EasyAbpEShopProductsProductDetailHistories", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Products.ProductDetails.ProductDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("StoreId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("EasyAbpEShopProductsProductDetails", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Products.ProductHistories.ProductHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("ModificationTime") + .HasColumnType("datetime2"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("SerializedEntityData") + .HasColumnType("nvarchar(max)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ModificationTime"); + + b.HasIndex("ProductId"); + + b.ToTable("EasyAbpEShopProductsProductHistories", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Products.ProductInventories.ProductInventory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Inventory") + .HasColumnType("int"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductSkuId") + .HasColumnType("uniqueidentifier"); + + b.Property("Sold") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ProductSkuId"); + + b.ToTable("EasyAbpEShopProductsProductInventories", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Products.Products.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayOrder") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("InventoryProviderName") + .HasColumnType("nvarchar(max)"); + + b.Property("InventoryStrategy") + .HasColumnType("int"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsHidden") + .HasColumnType("bit"); + + b.Property("IsPublished") + .HasColumnType("bit"); + + b.Property("IsStatic") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MediaResources") + .HasColumnType("nvarchar(max)"); + + b.Property("PaymentExpireIn") + .HasColumnType("time"); + + b.Property("ProductDetailId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductGroupName") + .HasColumnType("nvarchar(max)"); + + b.Property("StoreId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UniqueName") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UniqueName"); + + b.ToTable("EasyAbpEShopProductsProducts", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Products.Products.ProductAttribute", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayOrder") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.ToTable("EasyAbpEShopProductsProductAttributes", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Products.Products.ProductAttributeOption", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayOrder") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ProductAttributeId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("ProductAttributeId"); + + b.ToTable("EasyAbpEShopProductsProductAttributeOptions", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Products.Products.ProductSku", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Currency") + .HasColumnType("nvarchar(max)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MediaResources") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("OrderMaxQuantity") + .HasColumnType("int"); + + b.Property("OrderMinQuantity") + .HasColumnType("int"); + + b.Property("OriginalPrice") + .HasColumnType("decimal(20,8)"); + + b.Property("PaymentExpireIn") + .HasColumnType("time"); + + b.Property("Price") + .HasColumnType("decimal(20,8)"); + + b.Property("ProductDetailId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("SerializedAttributeOptionIds") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.ToTable("EasyAbpEShopProductsProductSkus", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Products.Products.ProductView", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayOrder") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("InventoryProviderName") + .HasColumnType("nvarchar(max)"); + + b.Property("InventoryStrategy") + .HasColumnType("int"); + + b.Property("IsHidden") + .HasColumnType("bit"); + + b.Property("IsPublished") + .HasColumnType("bit"); + + b.Property("IsStatic") + .HasColumnType("bit"); + + b.Property("MaximumPrice") + .HasColumnType("decimal(20,8)"); + + b.Property("MediaResources") + .HasColumnType("nvarchar(max)"); + + b.Property("MinimumPrice") + .HasColumnType("decimal(20,8)"); + + b.Property("ProductDetailId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductGroupDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductGroupName") + .HasColumnType("nvarchar(max)"); + + b.Property("Sold") + .HasColumnType("bigint"); + + b.Property("StoreId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UniqueName") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UniqueName"); + + b.ToTable("EasyAbpEShopProductsProductViews", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Stores.StoreOwners.StoreOwner", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("OwnerUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("StoreId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("OwnerUserId", "StoreId") + .IsUnique(); + + b.ToTable("EasyAbpEShopStoresStoreOwners", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Stores.Stores.Store", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("EasyAbpEShopStoresStores", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Stores.Transactions.Transaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActionName") + .HasColumnType("nvarchar(max)"); + + b.Property("Amount") + .HasColumnType("decimal(20,8)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Currency") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + + b.Property("StoreId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TransactionType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("EasyAbpEShopStoresTransactions", (string)null); + }); + + modelBuilder.Entity("EasyAbp.PaymentService.Payments.Payment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActualPaymentAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("CanceledTime") + .HasColumnType("datetime2"); + + b.Property("CompletionTime") + .HasColumnType("datetime2"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Currency") + .HasColumnType("nvarchar(max)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExternalTradingCode") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("OriginalPaymentAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("PayeeAccount") + .HasColumnType("nvarchar(max)"); + + b.Property("PaymentDiscount") + .HasColumnType("decimal(20,8)"); + + b.Property("PaymentMethod") + .HasColumnType("nvarchar(max)"); + + b.Property("PendingRefundAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("RefundAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("EasyAbpPaymentServicePayments", (string)null); + }); + + modelBuilder.Entity("EasyAbp.PaymentService.Payments.PaymentItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActualPaymentAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("ItemKey") + .HasColumnType("nvarchar(max)"); + + b.Property("ItemType") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("OriginalPaymentAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("PaymentDiscount") + .HasColumnType("decimal(20,8)"); + + b.Property("PaymentId") + .HasColumnType("uniqueidentifier"); + + b.Property("PendingRefundAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("RefundAmount") + .HasColumnType("decimal(20,8)"); + + b.HasKey("Id"); + + b.HasIndex("PaymentId"); + + b.ToTable("EasyAbpPaymentServicePaymentItems", (string)null); + }); + + modelBuilder.Entity("EasyAbp.PaymentService.Prepayment.Accounts.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccountGroupName") + .HasColumnType("nvarchar(max)"); + + b.Property("Balance") + .HasColumnType("decimal(20,8)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LockedBalance") + .HasColumnType("decimal(20,8)"); + + b.Property("PendingTopUpPaymentId") + .HasColumnType("uniqueidentifier"); + + b.Property("PendingWithdrawalAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("PendingWithdrawalRecordId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("EasyAbpPaymentServicePrepaymentAccounts", (string)null); + }); + + modelBuilder.Entity("EasyAbp.PaymentService.Prepayment.Transactions.Transaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccountId") + .HasColumnType("uniqueidentifier"); + + b.Property("AccountUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("ActionName") + .HasColumnType("nvarchar(max)"); + + b.Property("ChangedBalance") + .HasColumnType("decimal(20,8)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Currency") + .HasColumnType("nvarchar(max)"); + + b.Property("ExternalTradingCode") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("OriginalBalance") + .HasColumnType("decimal(20,8)"); + + b.Property("PaymentId") + .HasColumnType("uniqueidentifier"); + + b.Property("PaymentMethod") + .HasColumnType("nvarchar(max)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TransactionType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("AccountUserId"); + + b.ToTable("EasyAbpPaymentServicePrepaymentTransactions", (string)null); + }); + + modelBuilder.Entity("EasyAbp.PaymentService.Prepayment.WithdrawalRecords.WithdrawalRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccountId") + .HasColumnType("uniqueidentifier"); + + b.Property("Amount") + .HasColumnType("decimal(20,8)"); + + b.Property("CancellationTime") + .HasColumnType("datetime2"); + + b.Property("CompletionTime") + .HasColumnType("datetime2"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ResultErrorCode") + .HasColumnType("nvarchar(max)"); + + b.Property("ResultErrorMessage") + .HasColumnType("nvarchar(max)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("WithdrawalMethod") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("EasyAbpPaymentServicePrepaymentWithdrawalRecords", (string)null); + }); + + modelBuilder.Entity("EasyAbp.PaymentService.Prepayment.WithdrawalRequests.WithdrawalRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccountId") + .HasColumnType("uniqueidentifier"); + + b.Property("AccountUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("Amount") + .HasColumnType("decimal(20,8)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsApproved") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ReviewTime") + .HasColumnType("datetime2"); + + b.Property("ReviewerUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("EasyAbpPaymentServicePrepaymentWithdrawalRequests", (string)null); + }); + + modelBuilder.Entity("EasyAbp.PaymentService.Refunds.Refund", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CanceledTime") + .HasColumnType("datetime2"); + + b.Property("CompletedTime") + .HasColumnType("datetime2"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Currency") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomerRemark") + .HasColumnType("nvarchar(max)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DisplayReason") + .HasColumnType("nvarchar(max)"); + + b.Property("ExternalTradingCode") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("PaymentId") + .HasColumnType("uniqueidentifier"); + + b.Property("RefundAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("RefundPaymentMethod") + .HasColumnType("nvarchar(max)"); + + b.Property("StaffRemark") + .HasColumnType("nvarchar(max)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("EasyAbpPaymentServiceRefunds", (string)null); + }); + + modelBuilder.Entity("EasyAbp.PaymentService.Refunds.RefundItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("CustomerRemark") + .HasColumnType("nvarchar(max)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("PaymentItemId") + .HasColumnType("uniqueidentifier"); + + b.Property("RefundAmount") + .HasColumnType("decimal(20,8)"); + + b.Property("RefundId") + .HasColumnType("uniqueidentifier"); + + b.Property("StaffRemark") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("RefundId"); + + b.ToTable("EasyAbpPaymentServiceRefundItems", (string)null); + }); + + modelBuilder.Entity("EasyAbp.PaymentService.WeChatPay.PaymentRecords.PaymentRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AppId") + .HasColumnType("nvarchar(max)"); + + b.Property("Attach") + .HasColumnType("nvarchar(max)"); + + b.Property("BankType") + .HasColumnType("nvarchar(max)"); + + b.Property("CashFee") + .HasColumnType("int"); + + b.Property("CashFeeType") + .HasColumnType("nvarchar(max)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CouponCount") + .HasColumnType("int"); + + b.Property("CouponFee") + .HasColumnType("int"); + + b.Property("CouponFees") + .HasColumnType("nvarchar(max)"); + + b.Property("CouponIds") + .HasColumnType("nvarchar(max)"); + + b.Property("CouponTypes") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeviceInfo") + .HasColumnType("nvarchar(max)"); + + b.Property("ErrCode") + .HasColumnType("nvarchar(max)"); + + b.Property("ErrCodeDes") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("FeeType") + .HasColumnType("nvarchar(max)"); + + b.Property("IsSubscribe") + .HasColumnType("nvarchar(max)"); + + b.Property("MchId") + .HasColumnType("nvarchar(max)"); + + b.Property("Openid") + .HasColumnType("nvarchar(max)"); + + b.Property("OutTradeNo") + .HasColumnType("nvarchar(max)"); + + b.Property("PaymentId") + .HasColumnType("uniqueidentifier"); + + b.Property("ResultCode") + .HasColumnType("nvarchar(max)"); + + b.Property("ReturnCode") + .HasColumnType("nvarchar(max)"); + + b.Property("ReturnMsg") + .HasColumnType("nvarchar(max)"); + + b.Property("SettlementTotalFee") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TimeEnd") + .HasColumnType("nvarchar(max)"); + + b.Property("TotalFee") + .HasColumnType("int"); + + b.Property("TradeType") + .HasColumnType("nvarchar(max)"); + + b.Property("TransactionId") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("PaymentId"); + + b.ToTable("EasyAbpPaymentServiceWeChatPayPaymentRecords", (string)null); + }); + + modelBuilder.Entity("EasyAbp.PaymentService.WeChatPay.RefundRecords.RefundRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AppId") + .HasColumnType("nvarchar(max)"); + + b.Property("CashFee") + .HasColumnType("int"); + + b.Property("CashFeeType") + .HasColumnType("nvarchar(max)"); + + b.Property("CashRefundFee") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CouponIds") + .HasColumnType("nvarchar(max)"); + + b.Property("CouponRefundCount") + .HasColumnType("int"); + + b.Property("CouponRefundFee") + .HasColumnType("int"); + + b.Property("CouponRefundFees") + .HasColumnType("nvarchar(max)"); + + b.Property("CouponTypes") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("FeeType") + .HasColumnType("nvarchar(max)"); + + b.Property("MchId") + .HasColumnType("nvarchar(max)"); + + b.Property("OutRefundNo") + .HasColumnType("nvarchar(max)"); + + b.Property("OutTradeNo") + .HasColumnType("nvarchar(max)"); + + b.Property("PaymentId") + .HasColumnType("uniqueidentifier"); + + b.Property("RefundAccount") + .HasColumnType("nvarchar(max)"); + + b.Property("RefundFee") + .HasColumnType("int"); + + b.Property("RefundId") + .HasColumnType("nvarchar(max)"); + + b.Property("RefundRecvAccout") + .HasColumnType("nvarchar(max)"); + + b.Property("RefundRequestSource") + .HasColumnType("nvarchar(max)"); + + b.Property("RefundStatus") + .HasColumnType("nvarchar(max)"); + + b.Property("ReturnCode") + .HasColumnType("nvarchar(max)"); + + b.Property("ReturnMsg") + .HasColumnType("nvarchar(max)"); + + b.Property("SettlementRefundFee") + .HasColumnType("int"); + + b.Property("SettlementTotalFee") + .HasColumnType("int"); + + b.Property("SuccessTime") + .HasColumnType("nvarchar(max)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TotalFee") + .HasColumnType("int"); + + b.Property("TransactionId") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("PaymentId"); + + b.ToTable("EasyAbpPaymentServiceWeChatPayRefundRecords", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)") + .HasColumnName("ApplicationName"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("BrowserInfo"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("ClientId"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("ClientIpAddress"); + + b.Property("ClientName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("ClientName"); + + b.Property("Comments") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Comments"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("CorrelationId"); + + b.Property("Exceptions") + .HasColumnType("nvarchar(max)"); + + b.Property("ExecutionDuration") + .HasColumnType("int") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasMaxLength(16) + .HasColumnType("nvarchar(16)") + .HasColumnName("HttpMethod"); + + b.Property("HttpStatusCode") + .HasColumnType("int") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorTenantName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("ImpersonatorTenantName"); + + b.Property("ImpersonatorUserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("ImpersonatorUserId"); + + b.Property("ImpersonatorUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("ImpersonatorUserName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("TenantName"); + + b.Property("Url") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Url"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AuditLogId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnType("int") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("datetime2") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("MethodName"); + + b.Property("Parameters") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)") + .HasColumnName("Parameters"); + + b.Property("ServiceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("ServiceName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AuditLogId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnType("datetime2") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnType("tinyint") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("EntityId"); + + b.Property("EntityTenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("EntityTypeFullName"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("EntityChangeId") + .HasColumnType("uniqueidentifier"); + + b.Property("NewValue") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("NewValue"); + + b.Property("OriginalValue") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("OriginalValue"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("PropertyName"); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("PropertyTypeFullName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.BackgroundJobs.BackgroundJobRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsAbandoned") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("JobArgs") + .IsRequired() + .HasMaxLength(1048576) + .HasColumnType("nvarchar(max)"); + + b.Property("JobName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("LastTryTime") + .HasColumnType("datetime2"); + + b.Property("NextTryTime") + .HasColumnType("datetime2"); + + b.Property("Priority") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint") + .HasDefaultValue((byte)15); + + b.Property("TryCount") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((short)0); + + b.HasKey("Id"); + + b.HasIndex("IsAbandoned", "NextTryTime"); + + b.ToTable("AbpBackgroundJobs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[ProviderName] IS NOT NULL AND [ProviderKey] IS NOT NULL"); + + b.ToTable("AbpFeatureValues", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Regex") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("RegexDescription") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Required") + .HasColumnType("bit"); + + b.Property("ValueType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityLinkUser", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("SourceTenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("SourceUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("TargetTenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("TargetUserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("SourceUserId", "SourceTenantId", "TargetUserId", "TargetTenantId") + .IsUnique() + .HasFilter("[SourceTenantId] IS NOT NULL AND [TargetTenantId] IS NOT NULL"); + + b.ToTable("AbpLinkUsers", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnType("bit") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnType("bit") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnType("bit") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentitySecurityLog", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Action") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("CreationTime") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Identity") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Action"); + + b.HasIndex("TenantId", "ApplicationName"); + + b.HasIndex("TenantId", "Identity"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AbpSecurityLogs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Email"); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("EmailConfirmed"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsExternal") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsExternal"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("LockoutEnabled"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("NormalizedEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("NormalizedEmail"); + + b.Property("NormalizedUserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("NormalizedUserName"); + + b.Property("PasswordHash") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("PasswordHash"); + + b.Property("PhoneNumber") + .HasMaxLength(16) + .HasColumnType("nvarchar(16)") + .HasColumnName("PhoneNumber"); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("PhoneNumberConfirmed"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("SecurityStamp"); + + b.Property("Surname") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Surname"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("TwoFactorEnabled"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("Email"); + + b.HasIndex("NormalizedEmail"); + + b.HasIndex("NormalizedUserName"); + + b.HasIndex("UserName"); + + b.ToTable("AbpUsers", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderDisplayName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196) + .HasColumnType("nvarchar(196)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "UserId"); + + b.HasIndex("UserId", "OrganizationUnitId"); + + b.ToTable("AbpUserOrganizationUnits", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(95) + .HasColumnType("nvarchar(95)") + .HasColumnName("Code"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("ParentId"); + + b.ToTable("AbpOrganizationUnits", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "RoleId"); + + b.HasIndex("RoleId", "OrganizationUnitId"); + + b.ToTable("AbpOrganizationUnitRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AllowedAccessTokenSigningAlgorithms") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerApiResources", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.Property("ApiResourceId") + .HasColumnType("uniqueidentifier"); + + b.Property("Type") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("ApiResourceId", "Type"); + + b.ToTable("IdentityServerApiResourceClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceProperty", b => + { + b.Property("ApiResourceId") + .HasColumnType("uniqueidentifier"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("nvarchar(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.HasKey("ApiResourceId", "Key", "Value"); + + b.ToTable("IdentityServerApiResourceProperties", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceScope", b => + { + b.Property("ApiResourceId") + .HasColumnType("uniqueidentifier"); + + b.Property("Scope") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("ApiResourceId", "Scope"); + + b.ToTable("IdentityServerApiResourceScopes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceSecret", b => + { + b.Property("ApiResourceId") + .HasColumnType("uniqueidentifier"); + + b.Property("Type") + .HasMaxLength(250) + .HasColumnType("nvarchar(250)"); + + b.Property("Value") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("Expiration") + .HasColumnType("datetime2"); + + b.HasKey("ApiResourceId", "Type", "Value"); + + b.ToTable("IdentityServerApiResourceSecrets", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Emphasize") + .HasColumnType("bit"); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Required") + .HasColumnType("bit"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerApiScopes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeClaim", b => + { + b.Property("ApiScopeId") + .HasColumnType("uniqueidentifier"); + + b.Property("Type") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("ApiScopeId", "Type"); + + b.ToTable("IdentityServerApiScopeClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeProperty", b => + { + b.Property("ApiScopeId") + .HasColumnType("uniqueidentifier"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("nvarchar(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.HasKey("ApiScopeId", "Key", "Value"); + + b.ToTable("IdentityServerApiScopeProperties", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.Client", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AbsoluteRefreshTokenLifetime") + .HasColumnType("int"); + + b.Property("AccessTokenLifetime") + .HasColumnType("int"); + + b.Property("AccessTokenType") + .HasColumnType("int"); + + b.Property("AllowAccessTokensViaBrowser") + .HasColumnType("bit"); + + b.Property("AllowOfflineAccess") + .HasColumnType("bit"); + + b.Property("AllowPlainTextPkce") + .HasColumnType("bit"); + + b.Property("AllowRememberConsent") + .HasColumnType("bit"); + + b.Property("AllowedIdentityTokenSigningAlgorithms") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("AlwaysIncludeUserClaimsInIdToken") + .HasColumnType("bit"); + + b.Property("AlwaysSendClientClaims") + .HasColumnType("bit"); + + b.Property("AuthorizationCodeLifetime") + .HasColumnType("int"); + + b.Property("BackChannelLogoutSessionRequired") + .HasColumnType("bit"); + + b.Property("BackChannelLogoutUri") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("ClientClaimsPrefix") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ClientName") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ClientUri") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsentLifetime") + .HasColumnType("int"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DeviceCodeLifetime") + .HasColumnType("int"); + + b.Property("EnableLocalLogin") + .HasColumnType("bit"); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("FrontChannelLogoutSessionRequired") + .HasColumnType("bit"); + + b.Property("FrontChannelLogoutUri") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("IdentityTokenLifetime") + .HasColumnType("int"); + + b.Property("IncludeJwtId") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LogoUri") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("PairWiseSubjectSalt") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ProtocolType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("RefreshTokenExpiration") + .HasColumnType("int"); + + b.Property("RefreshTokenUsage") + .HasColumnType("int"); + + b.Property("RequireClientSecret") + .HasColumnType("bit"); + + b.Property("RequireConsent") + .HasColumnType("bit"); + + b.Property("RequirePkce") + .HasColumnType("bit"); + + b.Property("RequireRequestObject") + .HasColumnType("bit"); + + b.Property("SlidingRefreshTokenLifetime") + .HasColumnType("int"); + + b.Property("UpdateAccessTokenClaimsOnRefresh") + .HasColumnType("bit"); + + b.Property("UserCodeType") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("UserSsoLifetime") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("IdentityServerClients", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.Property("ClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("Type") + .HasMaxLength(250) + .HasColumnType("nvarchar(250)"); + + b.Property("Value") + .HasMaxLength(250) + .HasColumnType("nvarchar(250)"); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.Property("ClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("Origin") + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.HasKey("ClientId", "Origin"); + + b.ToTable("IdentityServerClientCorsOrigins", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.Property("ClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("GrantType") + .HasMaxLength(250) + .HasColumnType("nvarchar(250)"); + + b.HasKey("ClientId", "GrantType"); + + b.ToTable("IdentityServerClientGrantTypes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.Property("ClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("Provider") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("ClientId", "Provider"); + + b.ToTable("IdentityServerClientIdPRestrictions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.Property("ClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("PostLogoutRedirectUri") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.HasKey("ClientId", "PostLogoutRedirectUri"); + + b.ToTable("IdentityServerClientPostLogoutRedirectUris", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.Property("ClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("nvarchar(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.HasKey("ClientId", "Key", "Value"); + + b.ToTable("IdentityServerClientProperties", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.Property("ClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("RedirectUri") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.HasKey("ClientId", "RedirectUri"); + + b.ToTable("IdentityServerClientRedirectUris", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.Property("ClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("Scope") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("ClientId", "Scope"); + + b.ToTable("IdentityServerClientScopes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.Property("ClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("Type") + .HasMaxLength(250) + .HasColumnType("nvarchar(250)"); + + b.Property("Value") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Description") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("Expiration") + .HasColumnType("datetime2"); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientSecrets", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Devices.DeviceFlowCodes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("DeviceCode") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Expiration") + .IsRequired() + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("UserCode") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("DeviceCode") + .IsUnique(); + + b.HasIndex("Expiration"); + + b.HasIndex("UserCode"); + + b.ToTable("IdentityServerDeviceFlowCodes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Grants.PersistedGrant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsumedTime") + .HasColumnType("datetime2"); + + b.Property("CreationTime") + .HasColumnType("datetime2"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Expiration") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Key"); + + b.HasIndex("Expiration"); + + b.HasIndex("SubjectId", "ClientId", "Type"); + + b.HasIndex("SubjectId", "SessionId", "Type"); + + b.ToTable("IdentityServerPersistedGrants", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Emphasize") + .HasColumnType("bit"); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Required") + .HasColumnType("bit"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerIdentityResources", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceClaim", b => + { + b.Property("IdentityResourceId") + .HasColumnType("uniqueidentifier"); + + b.Property("Type") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("IdentityResourceId", "Type"); + + b.ToTable("IdentityServerIdentityResourceClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceProperty", b => + { + b.Property("IdentityResourceId") + .HasColumnType("uniqueidentifier"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("nvarchar(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.HasKey("IdentityResourceId", "Key", "Value"); + + b.ToTable("IdentityServerIdentityResourceProperties", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpPermissionGrants", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[ProviderName] IS NOT NULL AND [ProviderKey] IS NOT NULL"); + + b.ToTable("AbpSettings", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("AbpTenants", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.Property("TenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.HasKey("TenantId", "Name"); + + b.ToTable("AbpTenantConnectionStrings", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Orders.Orders.OrderExtraFee", b => + { + b.HasOne("EasyAbp.EShop.Orders.Orders.Order", null) + .WithMany("OrderExtraFees") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("EasyAbp.EShop.Orders.Orders.OrderLine", b => + { + b.HasOne("EasyAbp.EShop.Orders.Orders.Order", null) + .WithMany("OrderLines") + .HasForeignKey("OrderId"); + }); + + modelBuilder.Entity("EasyAbp.EShop.Payments.Payments.PaymentItem", b => + { + b.HasOne("EasyAbp.EShop.Payments.Payments.Payment", null) + .WithMany("PaymentItems") + .HasForeignKey("PaymentId"); + }); + + modelBuilder.Entity("EasyAbp.EShop.Payments.Refunds.RefundItem", b => + { + b.HasOne("EasyAbp.EShop.Payments.Refunds.Refund", null) + .WithMany("RefundItems") + .HasForeignKey("RefundId"); + }); + + modelBuilder.Entity("EasyAbp.EShop.Payments.Refunds.RefundItemOrderExtraFee", b => + { + b.HasOne("EasyAbp.EShop.Payments.Refunds.RefundItem", null) + .WithMany("OrderExtraFees") + .HasForeignKey("RefundItemId"); + }); + + modelBuilder.Entity("EasyAbp.EShop.Payments.Refunds.RefundItemOrderLine", b => + { + b.HasOne("EasyAbp.EShop.Payments.Refunds.RefundItem", null) + .WithMany("OrderLines") + .HasForeignKey("RefundItemId"); + }); + + modelBuilder.Entity("EasyAbp.EShop.Plugins.Coupons.CouponTemplates.CouponTemplateScope", b => + { + b.HasOne("EasyAbp.EShop.Plugins.Coupons.CouponTemplates.CouponTemplate", null) + .WithMany("Scopes") + .HasForeignKey("CouponTemplateId"); + }); + + modelBuilder.Entity("EasyAbp.EShop.Products.Categories.Category", b => + { + b.HasOne("EasyAbp.EShop.Products.Categories.Category", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("EasyAbp.EShop.Products.Products.ProductAttribute", b => + { + b.HasOne("EasyAbp.EShop.Products.Products.Product", null) + .WithMany("ProductAttributes") + .HasForeignKey("ProductId"); + }); + + modelBuilder.Entity("EasyAbp.EShop.Products.Products.ProductAttributeOption", b => + { + b.HasOne("EasyAbp.EShop.Products.Products.ProductAttribute", null) + .WithMany("ProductAttributeOptions") + .HasForeignKey("ProductAttributeId"); + }); + + modelBuilder.Entity("EasyAbp.EShop.Products.Products.ProductSku", b => + { + b.HasOne("EasyAbp.EShop.Products.Products.Product", null) + .WithMany("ProductSkus") + .HasForeignKey("ProductId"); + }); + + modelBuilder.Entity("EasyAbp.PaymentService.Payments.PaymentItem", b => + { + b.HasOne("EasyAbp.PaymentService.Payments.Payment", null) + .WithMany("PaymentItems") + .HasForeignKey("PaymentId"); + }); + + modelBuilder.Entity("EasyAbp.PaymentService.Refunds.RefundItem", b => + { + b.HasOne("EasyAbp.PaymentService.Refunds.Refund", null) + .WithMany("RefundItems") + .HasForeignKey("RefundId"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange", null) + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("OrganizationUnits") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("ParentId"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany("Roles") + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("UserClaims") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("Properties") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("Scopes") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("Secrets") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiScopes.ApiScope", null) + .WithMany("UserClaims") + .HasForeignKey("ApiScopeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiScopes.ApiScope", null) + .WithMany("Properties") + .HasForeignKey("ApiScopeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("Claims") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("AllowedCorsOrigins") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("AllowedGrantTypes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("IdentityProviderRestrictions") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("PostLogoutRedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("Properties") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("RedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("AllowedScopes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("ClientSecrets") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", null) + .WithMany("UserClaims") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", null) + .WithMany("Properties") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.HasOne("Volo.Abp.TenantManagement.Tenant", null) + .WithMany("ConnectionStrings") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("EasyAbp.EShop.Orders.Orders.Order", b => + { + b.Navigation("OrderExtraFees"); + + b.Navigation("OrderLines"); + }); + + modelBuilder.Entity("EasyAbp.EShop.Payments.Payments.Payment", b => + { + b.Navigation("PaymentItems"); + }); + + modelBuilder.Entity("EasyAbp.EShop.Payments.Refunds.Refund", b => + { + b.Navigation("RefundItems"); + }); + + modelBuilder.Entity("EasyAbp.EShop.Payments.Refunds.RefundItem", b => + { + b.Navigation("OrderExtraFees"); + + b.Navigation("OrderLines"); + }); + + modelBuilder.Entity("EasyAbp.EShop.Plugins.Coupons.CouponTemplates.CouponTemplate", b => + { + b.Navigation("Scopes"); + }); + + modelBuilder.Entity("EasyAbp.EShop.Products.Categories.Category", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("EasyAbp.EShop.Products.Products.Product", b => + { + b.Navigation("ProductAttributes"); + + b.Navigation("ProductSkus"); + }); + + modelBuilder.Entity("EasyAbp.EShop.Products.Products.ProductAttribute", b => + { + b.Navigation("ProductAttributeOptions"); + }); + + modelBuilder.Entity("EasyAbp.PaymentService.Payments.Payment", b => + { + b.Navigation("PaymentItems"); + }); + + modelBuilder.Entity("EasyAbp.PaymentService.Refunds.Refund", b => + { + b.Navigation("RefundItems"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Navigation("Actions"); + + b.Navigation("EntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Navigation("PropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Navigation("Claims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Navigation("Claims"); + + b.Navigation("Logins"); + + b.Navigation("OrganizationUnits"); + + b.Navigation("Roles"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Navigation("Roles"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResource", b => + { + b.Navigation("Properties"); + + b.Navigation("Scopes"); + + b.Navigation("Secrets"); + + b.Navigation("UserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScope", b => + { + b.Navigation("Properties"); + + b.Navigation("UserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.Client", b => + { + b.Navigation("AllowedCorsOrigins"); + + b.Navigation("AllowedGrantTypes"); + + b.Navigation("AllowedScopes"); + + b.Navigation("Claims"); + + b.Navigation("ClientSecrets"); + + b.Navigation("IdentityProviderRestrictions"); + + b.Navigation("PostLogoutRedirectUris"); + + b.Navigation("Properties"); + + b.Navigation("RedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", b => + { + b.Navigation("Properties"); + + b.Navigation("UserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => + { + b.Navigation("ConnectionStrings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/20220706160700_AddedFlashSales.cs b/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/20220706160700_AddedFlashSales.cs new file mode 100644 index 00000000..bb11a583 --- /dev/null +++ b/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/20220706160700_AddedFlashSales.cs @@ -0,0 +1,76 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace EShopSample.Migrations +{ + public partial class AddedFlashSales : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "EasyAbpEShopPluginsFlashSalesFlashSalePlans", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + StoreId = table.Column(type: "uniqueidentifier", nullable: false), + BeginTime = table.Column(type: "datetime2", nullable: false), + EndTime = table.Column(type: "datetime2", nullable: false), + ProductId = table.Column(type: "uniqueidentifier", nullable: false), + ProductSkuId = table.Column(type: "uniqueidentifier", nullable: false), + IsPublished = table.Column(type: "bit", nullable: false), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_EasyAbpEShopPluginsFlashSalesFlashSalePlans", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "EasyAbpEShopPluginsFlashSalesFlashSaleResults", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + StoreId = table.Column(type: "uniqueidentifier", nullable: false), + PlanId = table.Column(type: "uniqueidentifier", nullable: false), + Status = table.Column(type: "int", nullable: false), + Reason = table.Column(type: "nvarchar(max)", nullable: true), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + OrderId = table.Column(type: "uniqueidentifier", nullable: true), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_EasyAbpEShopPluginsFlashSalesFlashSaleResults", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "EasyAbpEShopPluginsFlashSalesFlashSalePlans"); + + migrationBuilder.DropTable( + name: "EasyAbpEShopPluginsFlashSalesFlashSaleResults"); + } + } +} diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/EShopSampleDbContextModelSnapshot.cs b/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/EShopSampleDbContextModelSnapshot.cs index ea75b086..64cc3698 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/EShopSampleDbContextModelSnapshot.cs +++ b/samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/EShopSampleDbContextModelSnapshot.cs @@ -1261,6 +1261,152 @@ namespace EShopSample.Migrations b.ToTable("EasyAbpEShopPluginsCouponsCouponTemplateScopes", (string)null); }); + modelBuilder.Entity("EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.FlashSalePlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BeginTime") + .HasColumnType("datetime2"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("EndTime") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsPublished") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductSkuId") + .HasColumnType("uniqueidentifier"); + + b.Property("StoreId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("EasyAbpEShopPluginsFlashSalesFlashSalePlans", (string)null); + }); + + modelBuilder.Entity("EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults.FlashSaleResult", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + + b.Property("PlanId") + .HasColumnType("uniqueidentifier"); + + b.Property("Reason") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("StoreId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("EasyAbpEShopPluginsFlashSalesFlashSaleResults", (string)null); + }); + modelBuilder.Entity("EasyAbp.EShop.Products.Categories.Category", b => { b.Property("Id") diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.HttpApi.Client/EShopSample.HttpApi.Client.csproj b/samples/EShopSample/aspnet-core/src/EShopSample.HttpApi.Client/EShopSample.HttpApi.Client.csproj index d807ff43..c9508f1f 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.HttpApi.Client/EShopSample.HttpApi.Client.csproj +++ b/samples/EShopSample/aspnet-core/src/EShopSample.HttpApi.Client/EShopSample.HttpApi.Client.csproj @@ -12,6 +12,8 @@ + + diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.HttpApi.Client/EShopSampleHttpApiClientModule.cs b/samples/EShopSample/aspnet-core/src/EShopSample.HttpApi.Client/EShopSampleHttpApiClientModule.cs index 075e272c..699547c9 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.HttpApi.Client/EShopSampleHttpApiClientModule.cs +++ b/samples/EShopSample/aspnet-core/src/EShopSample.HttpApi.Client/EShopSampleHttpApiClientModule.cs @@ -2,6 +2,8 @@ using EasyAbp.EShop.Plugins.Baskets; using EasyAbp.EShop.Plugins.Booking; using EasyAbp.EShop.Plugins.Coupons; +using EasyAbp.EShop.Plugins.FlashSales; +using EasyAbp.EShop.Products.Plugins.FlashSales; using EasyAbp.PaymentService; using EasyAbp.PaymentService.Prepayment; using EasyAbp.PaymentService.WeChatPay; @@ -27,6 +29,8 @@ namespace EShopSample typeof(EShopPluginsBasketsHttpApiClientModule), typeof(EShopPluginsBookingHttpApiClientModule), typeof(EShopPluginsCouponsHttpApiClientModule), + typeof(EShopPluginsFlashSalesHttpApiClientModule), + typeof(EShopProductsPluginsFlashSalesHttpApiClientModule), typeof(PaymentServiceHttpApiClientModule), typeof(PaymentServiceWeChatPayHttpApiClientModule), typeof(PaymentServicePrepaymentHttpApiClientModule) diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.HttpApi/EShopSample.HttpApi.csproj b/samples/EShopSample/aspnet-core/src/EShopSample.HttpApi/EShopSample.HttpApi.csproj index 5fc4e33c..bf8dd050 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.HttpApi/EShopSample.HttpApi.csproj +++ b/samples/EShopSample/aspnet-core/src/EShopSample.HttpApi/EShopSample.HttpApi.csproj @@ -12,6 +12,8 @@ + + diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.HttpApi/EShopSampleHttpApiModule.cs b/samples/EShopSample/aspnet-core/src/EShopSample.HttpApi/EShopSampleHttpApiModule.cs index cef2e77a..e818d8d3 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.HttpApi/EShopSampleHttpApiModule.cs +++ b/samples/EShopSample/aspnet-core/src/EShopSample.HttpApi/EShopSampleHttpApiModule.cs @@ -2,6 +2,8 @@ using EasyAbp.EShop.Plugins.Baskets; using EasyAbp.EShop.Plugins.Booking; using EasyAbp.EShop.Plugins.Coupons; +using EasyAbp.EShop.Plugins.FlashSales; +using EasyAbp.EShop.Products.Plugins.FlashSales; using EasyAbp.PaymentService; using EasyAbp.PaymentService.Prepayment; using EasyAbp.PaymentService.WeChatPay; @@ -25,6 +27,8 @@ namespace EShopSample typeof(EShopPluginsBasketsHttpApiModule), typeof(EShopPluginsBookingHttpApiModule), typeof(EShopPluginsCouponsHttpApiModule), + typeof(EShopPluginsFlashSalesHttpApiModule), + typeof(EShopProductsPluginsFlashSalesHttpApiModule), typeof(PaymentServiceHttpApiModule), typeof(PaymentServiceWeChatPayHttpApiModule), typeof(PaymentServicePrepaymentHttpApiModule) diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Web/EShopSample.Web.csproj b/samples/EShopSample/aspnet-core/src/EShopSample.Web/EShopSample.Web.csproj index 2728d314..ed0765d8 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Web/EShopSample.Web.csproj +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Web/EShopSample.Web.csproj @@ -42,6 +42,7 @@ + diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Web/EShopSampleWebModule.cs b/samples/EShopSample/aspnet-core/src/EShopSample.Web/EShopSampleWebModule.cs index 6801e606..8f3f62a6 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Web/EShopSampleWebModule.cs +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Web/EShopSampleWebModule.cs @@ -54,6 +54,8 @@ using Volo.Abp.TenantManagement.Web; using Volo.Abp.UI.Navigation.Urls; using Volo.Abp.UI.Navigation; using Volo.Abp.VirtualFileSystem; +using EasyAbp.EShop.Plugins.FlashSales.Web; +using EasyAbp.EShop.Plugins.FlashSales; namespace EShopSample.Web { @@ -75,6 +77,7 @@ namespace EShopSample.Web typeof(EShopPluginsBasketsWebModule), typeof(EShopPluginsBookingWebModule), typeof(EShopPluginsCouponsWebModule), + typeof(EShopPluginsFlashSalesWebModule), typeof(PaymentServiceWebModule), typeof(PaymentServiceWeChatPayWebModule), typeof(PaymentServicePrepaymentWebModule) @@ -216,6 +219,12 @@ namespace EShopSample.Web options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}plugins{Path.DirectorySeparatorChar}Coupons{Path.DirectorySeparatorChar}src{Path.DirectorySeparatorChar}EasyAbp.EShop.Plugins.Coupons.Application.Contracts")); options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}plugins{Path.DirectorySeparatorChar}Coupons{Path.DirectorySeparatorChar}src{Path.DirectorySeparatorChar}EasyAbp.EShop.Plugins.Coupons.Application")); options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}plugins{Path.DirectorySeparatorChar}Coupons{Path.DirectorySeparatorChar}src{Path.DirectorySeparatorChar}EasyAbp.EShop.Plugins.Coupons.Web")); + + options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}plugins{Path.DirectorySeparatorChar}FlashSales{Path.DirectorySeparatorChar}src{Path.DirectorySeparatorChar}EasyAbp.EShop.Plugins.FlashSales.Domain.Shared")); + options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}plugins{Path.DirectorySeparatorChar}FlashSales{Path.DirectorySeparatorChar}src{Path.DirectorySeparatorChar}EasyAbp.EShop.Plugins.FlashSales.Domain")); + options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}plugins{Path.DirectorySeparatorChar}FlashSales{Path.DirectorySeparatorChar}src{Path.DirectorySeparatorChar}EasyAbp.EShop.Plugins.FlashSales.Application.Contracts")); + options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}plugins{Path.DirectorySeparatorChar}FlashSales{Path.DirectorySeparatorChar}src{Path.DirectorySeparatorChar}EasyAbp.EShop.Plugins.FlashSales.Application")); + options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}plugins{Path.DirectorySeparatorChar}FlashSales{Path.DirectorySeparatorChar}src{Path.DirectorySeparatorChar}EasyAbp.EShop.Plugins.FlashSales.Web")); }); } } diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml.cs b/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml.cs index c6d2443a..b796c5a8 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml.cs +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml.cs @@ -57,12 +57,12 @@ public class IndexModel : EShopSamplePageModel Sorting = "CreationTime DESC" }); - Order = OrderList.Items.FirstOrDefault(x => x.OrderStatus is OrderStatus.Pending); - Store = await _storeAppService.GetDefaultAsync(); CakeProduct = await _productAppService.GetByUniqueNameAsync(Store.Id, SampleDataConsts.CakeProductUniqueName); + Order = OrderList.Items.FirstOrDefault(x => x.OrderStatus is OrderStatus.Pending && x.OrderLines.Any(ol => ol.ProductId == CakeProduct.Id)); + Wallet = (await _accountAppService.GetListAsync(new GetAccountListInput { UserId = CurrentUser.Id })).Items[0]; } @@ -90,7 +90,7 @@ public class IndexModel : EShopSamplePageModel sb.Append(','); } } - + sb.Append('}'); sb.Append($",\"skuId\":\"{sku.Id}\"");