Selaa lähdekoodia

Merge branch 'test' of http://110.188.24.182:10023/Fengwo/hotline into test

田爽 4 päivää sitten
vanhempi
commit
10016145ac
100 muutettua tiedostoa jossa 7819 lisäystä ja 124 poistoa
  1. 13 13
      Hotline.sln
  2. 2 0
      src/Hotline.Api/Controllers/Bigscreen/JudicialManagementScreenController.cs
  3. 8 2
      src/Hotline.Api/Controllers/CorsJobController.cs
  4. 401 0
      src/Hotline.Api/Controllers/EarlyController.cs
  5. 105 0
      src/Hotline.Api/Controllers/Exam/ExamManageController.cs
  6. 81 0
      src/Hotline.Api/Controllers/Exam/ExamTagController.cs
  7. 123 0
      src/Hotline.Api/Controllers/Exam/ExtractRuleController.cs
  8. 125 0
      src/Hotline.Api/Controllers/Exam/PracticeController.cs
  9. 122 0
      src/Hotline.Api/Controllers/Exam/QuestionController.cs
  10. 82 0
      src/Hotline.Api/Controllers/Exam/SourcewareCategoryController.cs
  11. 82 0
      src/Hotline.Api/Controllers/Exam/SourcewaresController.cs
  12. 150 0
      src/Hotline.Api/Controllers/Exam/TestPaperController.cs
  13. 85 0
      src/Hotline.Api/Controllers/Exam/TrainPlanController.cs
  14. 114 0
      src/Hotline.Api/Controllers/Exam/TrainRecordController.cs
  15. 98 0
      src/Hotline.Api/Controllers/Exam/TrainTemplateController.cs
  16. 168 0
      src/Hotline.Api/Controllers/Exam/UserExamController.cs
  17. 1 0
      src/Hotline.Api/Controllers/HomeController.cs
  18. 33 3
      src/Hotline.Api/Controllers/JudicialManagementOrdersController.cs
  19. 114 2
      src/Hotline.Api/Controllers/OrderController.cs
  20. 1 0
      src/Hotline.Api/Controllers/Snapshot/SnapshotOrderController.cs
  21. 4 0
      src/Hotline.Api/Controllers/TestController.cs
  22. 14 16
      src/Hotline.Api/config/appsettings.Development.json
  23. 47 47
      src/Hotline.Application/CallCenter/Calls/CurrentWaitNumService.cs
  24. 41 41
      src/Hotline.Application/CallCenter/Calls/ToDayWaitNumService.cs
  25. 407 0
      src/Hotline.Application/Early/EarlyReportProvider.cs
  26. 14 0
      src/Hotline.Application/Early/IEarlyReportProvder.cs
  27. 9 0
      src/Hotline.Application/Exam/Constants/ApiRoutes/ExamManageApiRoute.cs
  28. 9 0
      src/Hotline.Application/Exam/Constants/ApiRoutes/ExamTagApiRoute.cs
  29. 9 0
      src/Hotline.Application/Exam/Constants/ApiRoutes/ExtractRuleApiRoute.cs
  30. 9 0
      src/Hotline.Application/Exam/Constants/ApiRoutes/GenerateApiRoute.cs
  31. 20 0
      src/Hotline.Application/Exam/Constants/ApiRoutes/PracticeApiRoute.cs
  32. 8 0
      src/Hotline.Application/Exam/Constants/ApiRoutes/QuestionApiRoute.cs
  33. 9 0
      src/Hotline.Application/Exam/Constants/ApiRoutes/SourcewareApiRoute.cs
  34. 9 0
      src/Hotline.Application/Exam/Constants/ApiRoutes/SourcewareCategoryApiRoute.cs
  35. 14 0
      src/Hotline.Application/Exam/Constants/ApiRoutes/TestPaperApiRoute.cs
  36. 9 0
      src/Hotline.Application/Exam/Constants/ApiRoutes/TrainPlanApiRoute.cs
  37. 22 0
      src/Hotline.Application/Exam/Constants/ApiRoutes/TrainRecordApiRoute.cs
  38. 8 0
      src/Hotline.Application/Exam/Constants/ApiRoutes/TrainTemplateApiRoute.cs
  39. 40 0
      src/Hotline.Application/Exam/Constants/ApiRoutes/UserExamApiRoute.cs
  40. 9 0
      src/Hotline.Application/Exam/Constants/Messages/BusinessErrorMessage.cs
  41. 9 0
      src/Hotline.Application/Exam/Constants/Messages/SystemConstants.cs
  42. 21 0
      src/Hotline.Application/Exam/Core/Constants/ApiRoute.cs
  43. 17 0
      src/Hotline.Application/Exam/Core/Constants/BusiConstants.cs
  44. 19 0
      src/Hotline.Application/Exam/Core/Extensions/ExpressionableExtensions.cs
  45. 17 0
      src/Hotline.Application/Exam/Core/Extensions/QuestionTypeExtensions.cs
  46. 26 0
      src/Hotline.Application/Exam/Core/Utilities/CodeUtility.cs
  47. 15 0
      src/Hotline.Application/Exam/Core/Utilities/ExpressionableUtility.cs
  48. 26 0
      src/Hotline.Application/Exam/Core/Utilities/TypeAdapterConfigUtility.cs
  49. 26 0
      src/Hotline.Application/Exam/Extensions/CollectionExtensions.cs
  50. 44 0
      src/Hotline.Application/Exam/Extensions/OperationStatusExtensions.cs
  51. 25 0
      src/Hotline.Application/Exam/Extensions/RepositoryExtensions.cs
  52. 15 0
      src/Hotline.Application/Exam/Extensions/StartExamViewResponseExtensions.cs
  53. 22 0
      src/Hotline.Application/Exam/Interface/ExamManages/IExamManageService.cs
  54. 15 0
      src/Hotline.Application/Exam/Interface/ExamManages/IExamTagService.cs
  55. 15 0
      src/Hotline.Application/Exam/Interface/ExamManages/IExtractRuleService.cs
  56. 12 0
      src/Hotline.Application/Exam/Interface/ExamManages/IUnExamUserService.cs
  57. 12 0
      src/Hotline.Application/Exam/Interface/ExamManages/IUserExamResultService.cs
  58. 104 0
      src/Hotline.Application/Exam/Interface/ExamManages/IUserExamService.cs
  59. 68 0
      src/Hotline.Application/Exam/Interface/Practices/IPracticeService.cs
  60. 21 0
      src/Hotline.Application/Exam/Interface/Questions/IQuestionAnswerService.cs
  61. 21 0
      src/Hotline.Application/Exam/Interface/Questions/IQuestionKnowladgeService.cs
  62. 21 0
      src/Hotline.Application/Exam/Interface/Questions/IQuestionOptionsService.cs
  63. 21 0
      src/Hotline.Application/Exam/Interface/Questions/IQuestionService.cs
  64. 21 0
      src/Hotline.Application/Exam/Interface/Questions/IQuestionSourcewareService.cs
  65. 21 0
      src/Hotline.Application/Exam/Interface/Questions/IQuestionTagService.cs
  66. 20 0
      src/Hotline.Application/Exam/Interface/Sourcewares/ISourcewareCategoryService.cs
  67. 19 0
      src/Hotline.Application/Exam/Interface/Sourcewares/ISourcewareService.cs
  68. 21 0
      src/Hotline.Application/Exam/Interface/Strategy/IExamStrategy.cs
  69. 35 0
      src/Hotline.Application/Exam/Interface/TestPapers/ITestPaperService.cs
  70. 13 0
      src/Hotline.Application/Exam/Interface/Train/ITrainPlanService.cs
  71. 11 0
      src/Hotline.Application/Exam/Interface/Train/ITrainRecordAnswerService.cs
  72. 57 0
      src/Hotline.Application/Exam/Interface/Train/ITrainRecordService.cs
  73. 13 0
      src/Hotline.Application/Exam/Interface/Train/ITrainTemplateService.cs
  74. 21 0
      src/Hotline.Application/Exam/Mappers/MapperConfigs.cs
  75. 236 0
      src/Hotline.Application/Exam/Proxy/ExamManageProxy.cs
  76. 234 0
      src/Hotline.Application/Exam/Proxy/TestPaperProxy.cs
  77. 38 0
      src/Hotline.Application/Exam/QueryExtensions/ExamManages/ExamManageQueryExtensions.cs
  78. 41 0
      src/Hotline.Application/Exam/QueryExtensions/ExamManages/ExamQuestionQueryExtensions.cs
  79. 20 0
      src/Hotline.Application/Exam/QueryExtensions/ExamManages/ExamTagQueryExtensions.cs
  80. 25 0
      src/Hotline.Application/Exam/QueryExtensions/ExamManages/ExtractRuleQueryExtensions.cs
  81. 22 0
      src/Hotline.Application/Exam/QueryExtensions/ExamManages/TagQuestionQueryExtensions.cs
  82. 113 0
      src/Hotline.Application/Exam/QueryExtensions/ExamManages/UserExamQueryExtensions.cs
  83. 70 0
      src/Hotline.Application/Exam/QueryExtensions/Practices/PracticeQueryExtensions.cs
  84. 20 0
      src/Hotline.Application/Exam/QueryExtensions/Questions/QuestionAnswerQueryExtensions.cs
  85. 20 0
      src/Hotline.Application/Exam/QueryExtensions/Questions/QuestionKnowladgeQueryExtensions.cs
  86. 20 0
      src/Hotline.Application/Exam/QueryExtensions/Questions/QuestionOptionsQueryExtensions.cs
  87. 56 0
      src/Hotline.Application/Exam/QueryExtensions/Questions/QuestionQueryExtesions.cs
  88. 20 0
      src/Hotline.Application/Exam/QueryExtensions/Questions/QuestionSourcewareQueryExtensions.cs
  89. 20 0
      src/Hotline.Application/Exam/QueryExtensions/Questions/QuestionTagQueryExtensions.cs
  90. 29 0
      src/Hotline.Application/Exam/QueryExtensions/Sourcewares/SourcewareCategoryQueryExtensions.cs
  91. 29 0
      src/Hotline.Application/Exam/QueryExtensions/Sourcewares/SourcewareQueryExtensions.cs
  92. 136 0
      src/Hotline.Application/Exam/QueryExtensions/TestPapers/TestPaperQueryExtensions.cs
  93. 63 0
      src/Hotline.Application/Exam/QueryExtensions/Trains/TrainPlanQueryExtensions.cs
  94. 52 0
      src/Hotline.Application/Exam/QueryExtensions/Trains/TrainRecordQueryExtensions.cs
  95. 30 0
      src/Hotline.Application/Exam/QueryExtensions/Trains/TrainTemplateQueryExtensions.cs
  96. 694 0
      src/Hotline.Application/Exam/Service/ExamManages/ExamManageService.cs
  97. 113 0
      src/Hotline.Application/Exam/Service/ExamManages/ExamTagService.cs
  98. 536 0
      src/Hotline.Application/Exam/Service/ExamManages/ExtractRuleService.cs
  99. 1022 0
      src/Hotline.Application/Exam/Service/ExamManages/UserExamService.cs
  100. 697 0
      src/Hotline.Application/Exam/Service/Practices/PracticeService.cs

+ 13 - 13
Hotline.sln

@@ -45,8 +45,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tr.Sdk", "src\Tr.Sdk\Tr.Sdk
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.Api.Sdk", "src\Hotline.Api.Sdk\Hotline.Api.Sdk.csproj", "{EEF30056-A626-43B2-9762-632935C1AF31}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.Ai.Jths", "src\Hotline.Ai.Jths\Hotline.Ai.Jths.csproj", "{1634234A-379C-44DC-BFEC-7BBDEF50B47B}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.YbEnterprise.Sdk", "src\Hotline.YbEnterprise.Sdk\Hotline.YbEnterprise.Sdk.csproj", "{C3F289D5-C50B-46DB-852C-9543EF9B0355}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XingTang.Sdk", "src\XingTang.Sdk\XingTang.Sdk.csproj", "{CF2A8B80-FF4E-4291-B383-D735BB629F32}"
@@ -59,9 +57,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TianQue.Sdk", "src\TianQue.
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.Ai.XingTang", "src\Hotline.Ai.XingTang\Hotline.Ai.XingTang.csproj", "{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hotline.Tests", "test\Hotline.Tests\Hotline.Tests.csproj", "{31855124-4EFC-47B9-A4D5-64822DE036E6}"
+EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.Pdf", "src\Hotline.Pdf\Hotline.Pdf.csproj", "{3AB75B51-A69D-4145-A564-1D9D1695992E}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.Tests", "test\Hotline.Tests\Hotline.Tests.csproj", "{31855124-4EFC-47B9-A4D5-64822DE036E6}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hotline.Ai.Jths", "src\Hotline.Ai.Jths\Hotline.Ai.Jths.csproj", "{B05DB089-31DB-2BF5-5959-C816ECD7EFA4}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -129,10 +129,6 @@ Global
 		{EEF30056-A626-43B2-9762-632935C1AF31}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{EEF30056-A626-43B2-9762-632935C1AF31}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{EEF30056-A626-43B2-9762-632935C1AF31}.Release|Any CPU.Build.0 = Release|Any CPU
-		{1634234A-379C-44DC-BFEC-7BBDEF50B47B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{1634234A-379C-44DC-BFEC-7BBDEF50B47B}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{1634234A-379C-44DC-BFEC-7BBDEF50B47B}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{1634234A-379C-44DC-BFEC-7BBDEF50B47B}.Release|Any CPU.Build.0 = Release|Any CPU
 		{C3F289D5-C50B-46DB-852C-9543EF9B0355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{C3F289D5-C50B-46DB-852C-9543EF9B0355}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{C3F289D5-C50B-46DB-852C-9543EF9B0355}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -157,14 +153,18 @@ Global
 		{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5}.Release|Any CPU.Build.0 = Release|Any CPU
-		{3AB75B51-A69D-4145-A564-1D9D1695992E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{3AB75B51-A69D-4145-A564-1D9D1695992E}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{3AB75B51-A69D-4145-A564-1D9D1695992E}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{3AB75B51-A69D-4145-A564-1D9D1695992E}.Release|Any CPU.Build.0 = Release|Any CPU
 		{31855124-4EFC-47B9-A4D5-64822DE036E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{31855124-4EFC-47B9-A4D5-64822DE036E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{31855124-4EFC-47B9-A4D5-64822DE036E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{31855124-4EFC-47B9-A4D5-64822DE036E6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3AB75B51-A69D-4145-A564-1D9D1695992E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3AB75B51-A69D-4145-A564-1D9D1695992E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3AB75B51-A69D-4145-A564-1D9D1695992E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3AB75B51-A69D-4145-A564-1D9D1695992E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{B05DB089-31DB-2BF5-5959-C816ECD7EFA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{B05DB089-31DB-2BF5-5959-C816ECD7EFA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B05DB089-31DB-2BF5-5959-C816ECD7EFA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{B05DB089-31DB-2BF5-5959-C816ECD7EFA4}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -189,15 +189,15 @@ Global
 		{40B6FBEC-0524-430C-8B28-22229681D0F8} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{BB901B26-EA97-4D6F-8CAC-14DB321E1733} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{EEF30056-A626-43B2-9762-632935C1AF31} = {25C73963-4D5E-4654-804A-D2E2D360134B}
-		{1634234A-379C-44DC-BFEC-7BBDEF50B47B} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{C3F289D5-C50B-46DB-852C-9543EF9B0355} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{CF2A8B80-FF4E-4291-B383-D735BB629F32} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{9F99C272-5BC2-452C-9D97-BC756AF04669} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{75215667-65AF-4B7B-85E7-3140239B30CC} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{6CF27647-D0E0-4D17-80FB-3EE57864A2B4} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
-		{3AB75B51-A69D-4145-A564-1D9D1695992E} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{31855124-4EFC-47B9-A4D5-64822DE036E6} = {08D63205-1445-430F-A4AB-EF1744E3AC11}
+		{3AB75B51-A69D-4145-A564-1D9D1695992E} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
+		{B05DB089-31DB-2BF5-5959-C816ECD7EFA4} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {4B8EA790-BD13-4422-8D63-D6DBB77B823F}

+ 2 - 0
src/Hotline.Api/Controllers/Bigscreen/JudicialManagementScreenController.cs

@@ -1,5 +1,7 @@
 using Hotline.Application.JudicialManagement;
 using Hotline.Api.Filter;
+using Exam.Infrastructure.Extensions;
+using Hotline.Application.JudicialManagement;
 using Hotline.JudicialManagement;
 using Hotline.Orders;
 using Hotline.Settings;

+ 8 - 2
src/Hotline.Api/Controllers/CorsJobController.cs

@@ -19,14 +19,16 @@ namespace Hotline.Api.Controllers
         private readonly IRealtimeService _realtimeService;
         private readonly ISystemSettingCacheManager _systemSettingCacheManager;
         private readonly ITrClient _trClient;
+        private readonly ILogger _logger;
 
-        public CorsJobController(IOrderDomainService orderDomainService, IRepository<TrCallRecord> trcallrecordRepoository, IRealtimeService realtimeService, ISystemSettingCacheManager systemSettingCacheManager, ITrClient trClient)
+        public CorsJobController(IOrderDomainService orderDomainService, IRepository<TrCallRecord> trcallrecordRepoository, IRealtimeService realtimeService, ISystemSettingCacheManager systemSettingCacheManager, ITrClient trClient, ILogger logger)
         {
             _orderDomainService = orderDomainService;
             _trcallrecordRepoository = trcallrecordRepoository;
             _realtimeService = realtimeService;
             _systemSettingCacheManager = systemSettingCacheManager;
             _trClient = trClient;
+            _logger = logger;
         }
 
         /// <summary>
@@ -43,6 +45,7 @@ namespace Hotline.Api.Controllers
 
         /// <summary>
         /// 推送当天等待(1分钟一次)
+        /// 0 0/1 * * * ?
         /// </summary>
         /// <returns></returns>
         [HttpGet("send-todaywaitnum")]
@@ -62,6 +65,7 @@ namespace Hotline.Api.Controllers
 
         /// <summary>
         /// 推送当前排队数(5秒一次)
+        /// 0/5 * * * * ?
         /// </summary>
         /// <returns></returns>
         [HttpGet("send-currentwaitnum")]
@@ -80,7 +84,9 @@ namespace Hotline.Api.Controllers
 
                 await _realtimeService.CurrentWaitNumAsync(count, HttpContext.RequestAborted);
             }
-            catch { }
+            catch(Exception ex) {
+                _logger.LogError(ex.Message);
+            }
         }
     }
 }

+ 401 - 0
src/Hotline.Api/Controllers/EarlyController.cs

@@ -0,0 +1,401 @@
+using DocumentFormat.OpenXml.Drawing.Charts;
+using DocumentFormat.OpenXml.Drawing.Diagrams;
+using DocumentFormat.OpenXml.Vml;
+using DocumentFormat.OpenXml.Wordprocessing;
+using Hotline.Application.Early;
+using Hotline.Application.Tools;
+using Hotline.Caching.Interfaces;
+using Hotline.Caching.Services;
+using Hotline.Early;
+using Hotline.File;
+using Hotline.Orders;
+using Hotline.Repository.SqlSugar.Extensions;
+using Hotline.Settings;
+using Hotline.Share.Dtos;
+using Hotline.Share.Dtos.Early;
+using Hotline.Share.Dtos.ExportWord;
+using Hotline.Share.Dtos.Order;
+using Hotline.Share.Enums.Article;
+using Hotline.Share.Enums.Early;
+using Hotline.Share.Enums.Order;
+using Hotline.Share.Tools;
+using MapsterMapper;
+using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
+using System.Threading;
+using XF.Domain.Authentications;
+using XF.Domain.Exceptions;
+using XF.Domain.Repository;
+using XF.Utility.EnumExtensions;
+
+namespace Hotline.Api.Controllers
+{
+    public class EarlyController: BaseController
+    {
+        private readonly IRepository<EarlyWarningSetting> _earlyWarningRepository;
+        private readonly IMapper _mapper;
+        private readonly ISystemDicDataCacheManager _sysDicDataCacheManager;
+        private readonly IRepository<OrderWord> _orderWrodRepository;
+        private readonly IRepository<EarlyWarningOrder> _earlyWarningOrderRepository;
+        private readonly IRepository<EarlyWarningOrderDetail> _earlyWarningOrderDetailRepository;
+        private readonly IRepository<EarlyWarningPush> _earlyWarningPushRepository;
+        private readonly IRepository<EarlyWarningPushDetail> _earlyWarningPushDetailRepository;
+        private readonly IEarlyReportProvder _earlyReportProvder;
+        private readonly IFileDomainService _fileDomainService;
+        private readonly ISessionContext _sessionContext;
+        private readonly IOrderRepository _orderRepository;
+
+
+
+        public EarlyController(IRepository<EarlyWarningSetting> earlyWarningRepository, IMapper mapper, ISystemDicDataCacheManager sysDicDataCacheManager, IRepository<OrderWord> orderWrodRepository, IRepository<EarlyWarningOrder> earlyWarningOrderRepository, IRepository<EarlyWarningOrderDetail> earlyWarningOrderDetailRepository, IRepository<EarlyWarningPush> earlyWarningPushRepository, IRepository<EarlyWarningPushDetail> earlyWarningPushDetailRepository, IEarlyReportProvder earlyReportProvder,IFileDomainService fileDomainService, ISessionContext sessionContext, IOrderRepository orderRepository)
+        {
+            _earlyWarningRepository = earlyWarningRepository;
+            _mapper = mapper;
+            _sysDicDataCacheManager = sysDicDataCacheManager;
+            _orderWrodRepository = orderWrodRepository;
+            _earlyWarningOrderRepository = earlyWarningOrderRepository;
+            _earlyWarningOrderDetailRepository = earlyWarningOrderDetailRepository;
+            _earlyWarningPushRepository = earlyWarningPushRepository;
+            _earlyWarningPushDetailRepository = earlyWarningPushDetailRepository;
+            this._earlyReportProvder = earlyReportProvder;
+            this._fileDomainService = fileDomainService;
+            _sessionContext = sessionContext;
+            _orderRepository = orderRepository;
+        }
+
+        #region 预警设置
+
+        /// <summary>
+        /// 预警设置列表
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpGet("earlysetting-pagelist")]
+        public async Task<PagedDto<EarlySettingDto>> EarlySettingPageList([FromQuery]EarlySettingPageListRequest dto)
+        {
+            var (total,items) = await _earlyWarningRepository.Queryable()
+                .WhereIF(string.IsNullOrEmpty(dto.EarlyWarningName)== false, d => d.EarlyWarningName.Contains(dto.EarlyWarningName))
+                .WhereIF(string.IsNullOrEmpty(dto.EarlyWarningLevelValue)==false ,d=> d.EarlyWarningLevelValue == dto.EarlyWarningLevelValue)
+                .WhereIF(dto.IsEnable.HasValue,d=>d.IsEnable == dto.IsEnable).ToPagedListAsync(dto.PageIndex,dto.PageSize,HttpContext.RequestAborted);
+
+            return new PagedDto<EarlySettingDto>(total, _mapper.Map<IReadOnlyList<EarlySettingDto>>(items));
+        }
+
+        /// <summary>
+        /// 新增预警设置
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost("add-ealysetting")]
+        public async Task AddEarlySetting([FromBody] AddEarlySettingRequest dto)
+        {
+            var earlySetting = _mapper.Map<EarlyWarningSetting>(dto);
+            await _earlyWarningRepository.AddAsync(earlySetting,HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 修改预警设置
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("update-ealysetting")]
+        public async Task UpdateEarlySetting([FromBody] UpdateEarlySettingRequest dto)
+        {
+            var model = await _earlyWarningRepository.GetAsync(dto.Id, HttpContext.RequestAborted);
+            if (model == null)
+                throw UserFriendlyException.SameMessage("无效信息");
+            _mapper.Map(dto, model);
+            await _earlyWarningRepository.UpdateAsync(model, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 逻辑删除预警设置
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        [HttpDelete("del-ealysetting")]
+        public async Task DelEarlySetting([FromQuery]string id)
+        {
+            var model = await _earlyWarningRepository.GetAsync(id, HttpContext.RequestAborted);
+            if (model == null)
+                throw UserFriendlyException.SameMessage("无效信息");
+            await _earlyWarningRepository.RemoveAsync(model,true, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 获取预警设置
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        [HttpGet("get-earlysetting/{id}")]
+        public async Task<EarlySettingDto> GetEarlySetting(string id)
+        {
+            var model = await _earlyWarningRepository.GetAsync(id, HttpContext.RequestAborted);
+            if (model == null)
+                throw UserFriendlyException.SameMessage("无效信息");
+            return _mapper.Map<EarlySettingDto>(model);
+        }
+
+        /// <summary>
+        /// 预警设置基础数据
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet("earlysetting-basedata")]
+        public object EarlySettingBaseData()
+        {
+            return new
+            {
+                EarlyWarningLevel = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.EarlyWarningLevel),
+                EarlyWarningType = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.EarlyWarningType),
+                EFrequency = EnumExts.GetDescriptions<EFrequency>(),
+                ECompareType = EnumExts.GetDescriptions<ECompareType>(),
+                SensitiveWords = _orderWrodRepository.Queryable().Where(x => x.IsEnable == 1 && x.Classify.Contains("敏感标签")).Select(x => x.Tag).ToList(),
+                ENextWhere = EnumExts.GetDescriptions<ENextWhere>(),
+            };
+        }
+
+
+        #endregion
+
+        #region 预警管理
+
+        /// <summary>
+        /// 预警管理
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpGet("earlyorder-pagelist")]
+        public async Task<PagedDto<EarlyOrderDto>> EarlyOrderPageList([FromQuery]EarlyOrderPageListRequest dto)
+        {
+            var (total,items) = await _earlyWarningOrderRepository.Queryable()
+                .Includes(d => d.EarlyWarningSetting)
+                .Includes(d=>d.EarlyWarningOrderDetails)
+                .WhereIF(string.IsNullOrEmpty(dto.EarlyWarningName) == false, d => d.EarlyWarningSetting.EarlyWarningName.Contains(dto.EarlyWarningName))
+                .WhereIF(string.IsNullOrEmpty(dto.EarlyWarningLevelValue) == false, d => d.EarlyWarningSetting.EarlyWarningLevelValue == dto.EarlyWarningLevelValue)
+                .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+            return new PagedDto<EarlyOrderDto>(total, _mapper.Map<IReadOnlyList<EarlyOrderDto>>(items));
+        }
+
+        /// <summary>
+        /// 获取预警管理对象
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        [HttpGet("get-earlyorder/{id}")]
+        public async Task<EarlyOrderDto> GetEarlyOrder(string id)
+        {
+            var model = await _earlyWarningOrderRepository.Queryable()
+                .Includes(x=>x.EarlyWarningSetting)
+                .Includes(x=>x.EarlyWarningOrderDetails,d=>d.Order)
+                .Where(x=>x.Id == id).FirstAsync(HttpContext.RequestAborted);
+            if (model == null)
+                throw UserFriendlyException.SameMessage("无效信息");
+            return _mapper.Map<EarlyOrderDto>(model);
+        }
+
+
+        #endregion
+
+        #region 推送管理
+
+        /// <summary>
+        /// 推送报告列表基础数据
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet("earlypush-pagelist-basedata")]
+        public async Task<object> EarlyPushPageListBaseData()
+        {
+            return new
+            {
+                EarlyPushStatus = EnumExts.GetDescriptions<EEarlyPushStatus>()
+            };
+        }
+
+        /// <summary>
+        /// 推送报告列表
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpGet("earlypush-pagelist")]
+        public async Task<PagedDto<EarlyPushDto>> EarlyPushPageList([FromQuery] EarlyPushPageListRequest dto)
+        {
+            var (total, items) = await _earlyWarningPushRepository.Queryable()
+               .WhereIF(string.IsNullOrEmpty(dto.EarlyWarningPushName) == false, d => d.EarlyWarningPushName.Contains(dto.EarlyWarningPushName))
+               .WhereIF(dto.EarlyPushStatus.HasValue, d => d.EarlyPushStatus == dto.EarlyPushStatus)
+               .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+
+            return new PagedDto<EarlyPushDto>(total, _mapper.Map<IReadOnlyList<EarlyPushDto>>(items));
+        }
+
+        /// <summary>
+        /// 可以选择推送的工单
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet("early-canchooseorderbasedata")]
+        public async Task<object> EarlyCanChooseOrderBaseData()
+        {
+            return new
+            {
+                OrderStatus = EnumExts.GetDescriptions<EOrderStatus>(),
+                OrderPushStatus = EnumExts.GetDescriptions<EOrderPushStatus>().Where(d => new int[] { 2 }.Contains(d.Key) == false).ToList()
+            };
+        }
+
+
+        /// <summary>
+        /// 可以选择推送的工单
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpGet("early-canchooseorder")]
+        public async Task<PagedDto<CanChooseOrderRsp>> CanChooseOrder([FromQuery] CanChooseOrderRequest dto)
+        {
+            var (total,items) = await _earlyWarningOrderDetailRepository.Queryable()
+                .Includes(x=>x.Order)
+                .Where(x => x.OrderPushStatus != EOrderPushStatus.FiledPush)
+                .Where(x=> dto.ChooseOrderId.Contains(x.OrderId) == false)
+                .WhereIF(string.IsNullOrEmpty(dto.No)==false,x=>x.Order.No.Contains(dto.No))
+                .WhereIF(string.IsNullOrEmpty(dto.Title)==false,x=>x.Order.Title.Contains(dto.Title))
+                .WhereIF(dto.OrderStatus.HasValue,x=>x.Order.Status == dto.OrderStatus)
+                .WhereIF(dto.OrderPushStatus.HasValue,x=>x.OrderPushStatus == dto.OrderPushStatus)
+                .ToPagedListAsync(dto.PageIndex,dto.PageSize,HttpContext.RequestAborted);
+
+            return new PagedDto<CanChooseOrderRsp>(total, _mapper.Map<IReadOnlyList<CanChooseOrderRsp>>(items));
+        }
+
+        /// <summary>
+        /// 新增推送报告 TODO
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("add-earlypush")]
+        public async Task AddEarlyPush([FromBody] AddEarlyPushRequest dto)
+        {
+            var earlySettingPush = _mapper.Map<EarlyWarningPush>(dto);
+            earlySettingPush.EarlyPushStatus = EEarlyPushStatus.Pushed;
+            if (dto.OperateModel==1)
+            {
+                earlySettingPush.EarlyPushStatus = EEarlyPushStatus.Draft;
+            }
+            if (dto.OperateModel!=1)
+            {
+                earlySettingPush.PushTime = DateTime.Now;
+            }
+            if (dto.OperateModel == 3)
+            {
+                //准备数据
+                var reportDto = await GetEarlyReport(dto.EarlyWarningPushDetails,earlySettingPush);               
+               
+                // 生成推送文件流
+                var html = _earlyReportProvder.PushReport(reportDto);
+
+                // 推送文件
+                var fileJson =  await _fileDomainService.UploadFileFromMemoryAsync(html.HtmlToWord(), "报表" + EFileType.word.GetFileExtension(), EFileType.word);
+               
+                //推送生成报告消息TODO
+                earlySettingPush.PushReportUrl = "";
+            }
+            await _earlyWarningPushRepository.AddAsync(earlySettingPush, HttpContext.RequestAborted);
+            dto.EarlyWarningPushDetails.ForEach(x =>
+            {
+                x.EarlyWarningPushId = earlySettingPush.Id;
+                if(dto.OperateModel != 1)
+                {
+                    if(x.Order.Status < EOrderStatus.Filed)
+                    {
+                        _earlyWarningOrderDetailRepository.Updateable().SetColumns(x => x.OrderPushStatus == EOrderPushStatus.ProcessingPush).Where(x => x.OrderId == x.OrderId);
+                    }
+                    if (x.Order.Status >= EOrderStatus.Filed)
+                    {
+                        _earlyWarningOrderDetailRepository.Updateable().SetColumns(x => x.OrderPushStatus == EOrderPushStatus.FiledPush).Where(x => x.OrderId == x.OrderId);
+                    }
+                }
+            });
+            var detail = _mapper.Map<List<EarlyWarningPushDetail>>(dto.EarlyWarningPushDetails);
+            await _earlyWarningPushDetailRepository.AddRangeAsync(detail,HttpContext.RequestAborted);
+        }
+
+        private async Task<EarlyReportDto> GetEarlyReport(List<EarlyWarningPushDetailDto> dto, EarlyWarningPush earlyPush)
+        {
+            var reportDto = new EarlyReportDto();
+            reportDto.ReportName = earlyPush.EarlyWarningPushName;
+            reportDto.ReportPushTime = earlyPush.PushTime.Value;
+            reportDto.ReportHotDetails = new List<ReportHotDetailDto>();
+            List<string> orderIds = dto.Select(x => x.OrderId).ToList();
+            //查询所有工单
+            var orderList = await _orderRepository.Queryable().Where(x => orderIds.Contains(x.Id)).ToListAsync();
+
+            var result = (from a in orderList
+                         join b in dto on a.Id equals b.OrderId
+                         select new ReportOrderListDto (){ Id = a.Id, ActualHandleOrgName = a.ActualHandleOrgName, IsSendWord = b.IsSendWord, County = a.County, OneHotspotName = a.HotspotSpliceName.Split("-")[0], HotspotName = a.HotspotName, Status = a.Status,FileOpinion = a.FileOpinion, Content = a.Content }).ToList();
+
+            //Title 热点汇总
+            var reportHotDetail = result.GroupBy(x => x.OneHotspotName).ToList();
+            reportDto.ReportHotDetails = new List<ReportHotDetailDto>();
+            reportHotDetail.ForEach(x =>
+            {
+                var detail = new ReportHotDetailDto();
+                detail.HotName = x.Key;
+                detail.Count = x.Count();
+                detail.Content = string.Join(",", result.Where(d => d.IsSendWord == true && d.OneHotspotName == x.Key).Select(d => d.Content));
+                reportDto.ReportHotDetails.Add(detail);
+            });
+
+            //Title 地区汇总
+            reportDto.ReportAreaDetails = result.GroupBy(x => x.County).Select(x=> new ReportAreaDetailDto() { AreaName = x.Key,Count = x.Count() }).ToList();
+
+            //附件1:重点诉求处置情况
+            reportDto.ReportOrderDetails = result.Where(x => x.Status < EOrderStatus.Filed && x.IsSendWord == true).Select(x => new ReportOrderDetailDto() { OneHotSportName = x.OneHotspotName, HotSportName = x.HotspotName, Content = x.Content, ActualOpinion = "已转发" + x.ActualHandleOrgName + ",正在处理中" }).ToList();
+
+            //附件2:
+            //查询往期
+            var earlyWarningPushDetail = await _earlyWarningPushDetailRepository.Queryable().Includes(x=>x.EarlyWarningPush).Where(x => result.Select(x => x.Id).Contains(x.OrderId)).ToListAsync();
+            var fujianTwoList = (from a in result
+                                 join b in earlyWarningPushDetail on a.Id equals b.OrderId
+                                 where a.Status>= EOrderStatus.Filed && a.IsSendWord == true
+                                 select new ReportFiledOrderDetailDto() { ReportName = b.EarlyWarningPush.EarlyWarningPushName, OneHotSportName = a.OneHotspotName, HotSportName = a.HotspotName,Content = a.Content, ActualOpinion = a.FileOpinion }).ToList();
+            reportDto.ReportFiledOrderDetails = fujianTwoList;
+
+            return reportDto;
+        }
+        /// <summary>
+        /// 获取推送报告
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        [HttpGet("getearlypush/{id}")]
+        public async Task<EarlyPushDto> GetEarlyPush(string id)
+        {
+            var model = await _earlyWarningPushRepository.Queryable()
+                .Includes(x => x.EarlyWarningPushDetails)
+                .Includes(x=>x.EarlyWarningPushDetails,d=>d.Order)
+                .FirstAsync(HttpContext.RequestAborted);
+            if (model == null)
+                throw UserFriendlyException.SameMessage("无效信息");
+            return _mapper.Map<EarlyPushDto>(model);
+        }
+
+        #endregion
+
+        #region 报告接收
+
+        /// <summary>
+        /// 报告接收
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpGet("report-receive")]
+        public async Task<PagedDto<EarlyPushDto>> ReportReceive([FromQuery] ReportReceiveRequest dto)
+        {
+            var (total, items) = await _earlyWarningPushRepository.Queryable()
+               .WhereIF(string.IsNullOrEmpty(dto.EarlyWarningPushName) == false, d => d.EarlyWarningPushName.Contains(dto.EarlyWarningPushName))
+               .Where(d=> d.EarlyPushStatus == EEarlyPushStatus.Pushed && SqlFunc.JsonListObjectAny(d.PushOrgs, "OrgCode", _sessionContext.OrgId))
+               .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+
+            return new PagedDto<EarlyPushDto>(total, _mapper.Map<IReadOnlyList<EarlyPushDto>>(items));
+        }
+
+
+
+        #endregion
+    }
+}

+ 105 - 0
src/Hotline.Api/Controllers/Exam/ExamManageController.cs

@@ -0,0 +1,105 @@
+using Exam.Infrastructure.Data.Entity;
+using Exam.Share;
+using Exam.Share.ViewResponses.Exam;
+using Hotline.Application.Exam.Constants.ApiRoutes;
+using Hotline.Application.Exam.Interface.ExamManages;
+using Hotline.Repository.SqlSugar.Entitys;
+using Hotline.Share.Requests.Exam;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Hotline.Api.Controllers.Exam
+{
+    public class ExamManageController : BaseController
+    {
+        private readonly IExamManageService _examManageService;
+        public ExamManageController(IExamManageService examManageService)
+        {
+            _examManageService = examManageService;
+        }
+
+        /// <summary>
+        /// 新增考试
+        /// </summary>
+        /// <param name="questionDto"></param>
+        /// <returns></returns>
+        [HttpPost(ExamManageApiRoute.Add)]
+        public async Task Add([FromBody] AddExamManageDto questionDto)
+        {
+            await _examManageService.AddAsync(questionDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 修改考试
+        /// </summary>
+        /// <param name="questionDto"></param>
+        /// <returns></returns>
+        [HttpPut(ExamManageApiRoute.Update)]
+        public async Task Update([FromBody] UpdateExamManageDto questionDto)
+        {
+            await _examManageService.UpdateAsync(questionDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 删除考试
+        /// </summary>
+        /// <param name="entityQueryRequest"></param>
+        /// <returns></returns>
+        [HttpDelete(ExamManageApiRoute.Delete)]
+        public async Task Delete([FromBody] EntityQueryRequest entityQueryRequest)
+        {
+            await _examManageService.DeleteAsync(entityQueryRequest, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 获取考试分页列表
+        /// </summary>
+        /// <param name="questionPagedRequest"></param>
+        /// <returns></returns>
+        [HttpPost(ExamManageApiRoute.GetPagedList)]
+        public async Task<ExamManagePageViewResponse> GetPagedList([FromBody] ExamManagePagedRequest questionPagedRequest)
+        {
+            var questionPageViewResponse = await _examManageService.GetPagedListAsync(questionPagedRequest);
+
+            return questionPageViewResponse as ExamManagePageViewResponse;
+        }
+
+        /// <summary>
+        /// 获取考试
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        [HttpGet(ExamManageApiRoute.Get)]
+        public async Task<ExamManageDto> Get(string id)
+        {
+            var questionDto = await _examManageService.GetAsync(new EntityQueryRequest
+            {
+                Id = id
+            });
+
+            return questionDto;
+        }
+
+        /// <summary>
+        /// 组卷
+        /// </summary>
+        /// <param name="generateExamTestPaperRequest"></param>
+        /// <returns></returns>
+        [HttpPost(ExamManageApiRoute.GenerateTestPaper)]
+        public async Task GenerateTestPaper([FromBody]GenerateExamTestPaperRequest generateExamTestPaperRequest)
+        {
+            await _examManageService.GenerateTestPaper(generateExamTestPaperRequest, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 更新开启状态
+        /// </summary>
+        /// <param name="statusActionRequests"></param>
+        /// <returns></returns>
+        [HttpPost(ExamManageApiRoute.UpdateStatus)]
+        public async Task UpdateStatus([FromBody] List<StatusActionRequest> statusActionRequests)
+        {
+            await _examManageService.UpdateStatus(statusActionRequests, HttpContext.RequestAborted);
+        }
+    }
+}

+ 81 - 0
src/Hotline.Api/Controllers/Exam/ExamTagController.cs

@@ -0,0 +1,81 @@
+using Exam.Application.Interface.Exam;
+using Exam.Infrastructure.Data.Entity;
+using Hotline.Application.Exam.Constants.ApiRoutes;
+using Hotline.Share.Dtos.ExamManages;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.ViewResponses.Exam;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Hotline.Api.Controllers.Exam
+{
+    public class ExamTagController : BaseController
+    {
+        private readonly IExamTagService _examTagService;
+        public ExamTagController(IExamTagService examTagService)
+        {
+            _examTagService = examTagService;
+        }
+
+        /// <summary>
+        /// 新增考试标签
+        /// </summary>
+        /// <param name="examTagDto"></param>
+        /// <returns></returns>
+        [HttpPost(ExamTagApiRoute.Add)]
+        public async Task Add([FromBody] AddExamTagDto examTagDto)
+        {
+            await _examTagService.AddAsync(examTagDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 修改考试标签
+        /// </summary>
+        /// <param name="examTagDto"></param>
+        /// <returns></returns>
+        [HttpPut(ExamTagApiRoute.Update)]
+        public async Task Update([FromBody] UpdateExamTagDto examTagDto)
+        {
+            await _examTagService.UpdateAsync(examTagDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 删除考试标签
+        /// </summary>
+        /// <param name="entityQueryRequest"></param>
+        /// <returns></returns>
+        [HttpDelete(ExamTagApiRoute.Delete)]
+
+        public async Task Delete(EntityQueryRequest entityQueryRequest)
+        {
+            await _examTagService.DeleteAsync(entityQueryRequest, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 获取树形列表
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost(ExamTagApiRoute.GetTreeList)]
+        public async Task<List<ExamTagViewResponse>> GetTreeList([FromBody] ExamTagRequest examTagRequest)
+        {
+            var examTags = await _examTagService.GetTreeAsync(examTagRequest);
+
+            return examTags;
+        }
+
+        /// <summary>
+        /// 获取课件分类
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        [HttpGet(ExamTagApiRoute.Get)]
+        public async Task<ExamTagDto> Get(string id)
+        {
+            var examTagDto = await _examTagService.GetAsync(new EntityQueryRequest
+            {
+                Id = id
+            });
+
+            return examTagDto;
+        }
+    }
+}

+ 123 - 0
src/Hotline.Api/Controllers/Exam/ExtractRuleController.cs

@@ -0,0 +1,123 @@
+using Exam.Application.Interface.Exam;
+using Exam.Infrastructure.Data.Entity;
+using Exam.Share.ViewResponses.Exam;
+using Hotline.Application.Exam.Constants.ApiRoutes;
+using Hotline.Repository.SqlSugar.Entitys;
+using Hotline.Share.Dtos.TestPapers;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.ViewResponses.Exam;
+using Mapster;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Hotline.Api.Controllers.Exam
+{
+    public class ExtractRuleController : BaseController
+    {
+        private readonly IExtractRuleService _extractRuleService;
+        public ExtractRuleController(IExtractRuleService extractRuleService)
+        {
+            _extractRuleService = extractRuleService;
+        }
+
+        /// <summary>
+        /// 新增考试标签
+        /// </summary>
+        /// <param name="extractRuleDto"></param>
+        /// <returns></returns>
+        [HttpPost(ExtractRuleApiRoute.Add)]
+        public async Task Add([FromBody] AddExtractRuleDto extractRuleDto)
+        {
+            await _extractRuleService.AddAsync(extractRuleDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 修改考试标签
+        /// </summary>
+        /// <param name="extractRuleDto"></param>
+        /// <returns></returns>
+        [HttpPut(ExtractRuleApiRoute.Update)]
+        public async Task Update([FromBody] UpdateExtractRuleDto extractRuleDto)
+        {
+            await _extractRuleService.UpdateAsync(extractRuleDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 删除考试标签
+        /// </summary>
+        /// <param name="entityQueryRequest"></param>
+        /// <returns></returns>
+        [HttpDelete(ExtractRuleApiRoute.Delete)]
+
+        public async Task Delete(EntityQueryRequest entityQueryRequest)
+        {
+            await _extractRuleService.DeleteAsync(entityQueryRequest, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 获取课件分页列表
+        /// </summary>
+        /// <param name="sourcewarePagedRequest"></param>
+        /// <returns></returns>
+        [HttpPost(ExtractRuleApiRoute.GetPagedList)]
+        public async Task<ExtractRulePageViewResponse> GetPagedList([FromBody] ExtractRulePagedRequest extractRulePagedRequest)
+        {
+            var sourcewarePageViewResponse = await _extractRuleService.GetPagedListAsync(extractRulePagedRequest);
+
+            return sourcewarePageViewResponse as ExtractRulePageViewResponse;
+        }
+
+
+        /// <summary>
+        /// 获取课件分类
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        [HttpGet(ExtractRuleApiRoute.Get)]
+        public async Task<ExtractRuleDto> Get(string id)
+        {
+            var extractRuleDto = await _extractRuleService.GetAsync(new EntityQueryRequest
+            {
+                Id = id
+            });
+
+            return extractRuleDto;
+        }
+
+        /// <summary>
+        /// 获取抽题规则列表
+        /// </summary>
+        /// <param name="extractRuleRequest"></param>
+        /// <returns></returns>
+        [HttpGet(ExtractRuleApiRoute.GetList)]
+        public async Task<List<ExtractRuleViewResponse>> GetList([FromQuery] ExtractRuleRequest extractRuleRequest)
+        {
+            var extractRulePagedRequest = extractRuleRequest.Adapt<ExtractRulePagedRequest>();
+
+            return (await _extractRuleService.GetListAsync(extractRulePagedRequest)).Item2;
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="tagQuestionRequest"></param>
+        /// <returns></returns>
+        [HttpPost(ExtractRuleApiRoute.GetTagQuestionCount)]
+        public async Task<List<TagQuestionViewResponse>> GetTagQuestionCount([FromBody] TagQuestionRequest tagQuestionRequest)
+        {
+            var tagQuestionViewResponses = await _extractRuleService.GetTagQuestionCount(tagQuestionRequest);
+
+            return tagQuestionViewResponses;
+        }
+
+        /// <summary>
+        /// 更新开启状态
+        /// </summary>
+        /// <param name="statusActionRequests"></param>
+        /// <returns></returns>
+        [HttpPost(ExtractRuleApiRoute.UpdateStatus)]
+        public async Task UpdateStatus([FromBody] List<StatusActionRequest> statusActionRequests)
+        {
+            await _extractRuleService.UpdateStatus(statusActionRequests, HttpContext.RequestAborted);
+        }
+    }
+}

+ 125 - 0
src/Hotline.Api/Controllers/Exam/PracticeController.cs

@@ -0,0 +1,125 @@
+using Amazon.Runtime.Internal;
+using Exam.Application.Interface.Practices;
+using Exam.Share;
+using Exam.Share.ViewResponses.Practices;
+using Hotline.Application.Exam.Constants.ApiRoutes;
+using Hotline.Share.Dtos.Practices;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.Requests.TestPaper;
+using Hotline.Share.ViewResponses.Exam;
+using Hotline.Share.ViewResponses.Practices;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Hotline.Api.Controllers.Exam
+{
+    public class PracticeController : BaseController
+    {
+        private readonly IPracticeService _practiceService;
+
+        public PracticeController(IPracticeService practiceService)
+        {
+            this._practiceService = practiceService;
+        }
+
+        /// <summary>
+        /// 获取自我练习试题类型和Id
+        /// </summary>
+        /// <param name="examQuestionRequest"></param>
+        /// <returns></returns>
+        [HttpGet(PracticeApiRoute.GetPracticeQuestionViewResponses)]
+        public async Task<List<PracticeQuestionViewResponse>> GetPracticeQuestionViewResponses([FromQuery] PracticeQuestionGroupRequest practiceQuestionGroupRequest)
+        {
+
+            return await _practiceService.GetPracticeQuestionViewResponses(practiceQuestionGroupRequest);
+        }
+
+        /// <summary>
+        /// 获取练习试题
+        /// </summary>
+        /// <param name="practiceQuestionRequest"></param>
+        /// <returns></returns>
+        [HttpGet(PracticeApiRoute.GetPracticeQuestionDto)]
+        public async Task<PracticeQuestionDto> GetPracticeQuestion([FromQuery] PracticeQuestionRequest practiceQuestionRequest)
+        {
+            return await _practiceService.GetPracticeQuestion(practiceQuestionRequest);
+        }
+
+        /// <summary>
+        /// 练习
+        /// </summary>
+        /// <param name="submitPracticeDto"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        [HttpPost(PracticeApiRoute.Practice)]
+        public async Task<PracticeQuestionDto> Practice([FromBody]SubmitPracticeDto submitPracticeDto)
+        {
+            return await _practiceService.Practice(submitPracticeDto,HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 获取练习记录
+        /// </summary>
+        /// <param name="practicePagedRequest"></param>
+        /// <returns></returns>
+        [HttpPost(PracticeApiRoute.GetPagedList)]
+        public async Task<PracticePageViewResponse> GetPagedList([FromBody]PracticePagedRequest practicePagedRequest)
+        {
+            var practicePageViewResponse = await _practiceService.GetPagedListAsync(practicePagedRequest);
+            return practicePageViewResponse as PracticePageViewResponse;
+        }
+
+        /// <summary>
+        /// 完成练习记录
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost(PracticeApiRoute.Complete)]
+        public async Task Complete([FromBody]CompletePracticeRecordDto completePracticeRecordDto)
+        {
+            await _practiceService.Complete(completePracticeRecordDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 获取查看练习习题
+        /// </summary>
+        /// <param name="practiceQuestionGroupRequest"></param>
+        /// <returns></returns>
+        [HttpGet(PracticeApiRoute.GetViewPracticeQuestions)]
+        public async Task<List<PracticeQuestionViewResponse>> GetViewPracticeQuestions([FromQuery] PracticeQuestionGroupRequest practiceQuestionGroupRequest)
+        {
+            return await _practiceService.GetViewPracticeQuestions(practiceQuestionGroupRequest);
+        }
+
+        /// <summary>
+        /// 获取查看练习习题
+        /// </summary>
+        /// <param name="practiceQuestionGroupRequest"></param>
+        /// <returns></returns>
+        [HttpGet(PracticeApiRoute.View)]
+        public async Task<ViewPracticeQuestionDto> View([FromQuery] PracticeQuestionRequest practiceQuestionRequest)
+        {
+            return await _practiceService.ViewPracticeQuestion(practiceQuestionRequest);
+        }
+
+        /// <summary>
+        /// 新增练习
+        /// </summary>
+        /// <param name="addPracticeDto"></param>
+        /// <returns></returns>
+        [HttpPost(PracticeApiRoute.Add)]
+        public async Task<string> Add([FromBody] AddPracticeDto addPracticeDto)
+        {
+           return  await _practiceService.AddAsync(addPracticeDto,HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 获取标签试题数
+        /// </summary>
+        /// <param name="tagQuestionCountForPracticeRequest"></param>
+        /// <returns></returns>
+        [HttpPost(PracticeApiRoute.GetTagQuestionCount)]
+        public async Task<TagQuestionCountViewResponse> GetTagQuestionCount([FromBody] TagQuestionCountForPracticeRequest tagQuestionCountForPracticeRequest)
+        {
+            return await _practiceService.GetTagQuestionCount(tagQuestionCountForPracticeRequest);
+        }
+    }
+}

+ 122 - 0
src/Hotline.Api/Controllers/Exam/QuestionController.cs

@@ -0,0 +1,122 @@
+using Exam.Application.Interface.Questions;
+using Exam.Infrastructure.Data.Entity;
+using Exam.Share.ViewResponses.Question;
+using Hotline.Application.Exam.Constants.ApiRoutes;
+using Hotline.Repository.SqlSugar.Entitys;
+using Hotline.Share.Dtos.Questions;
+using Hotline.Share.Requests.Question;
+using MapsterMapper;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Hotline.Api.Controllers.Exam
+{
+    public class QuestionController : BaseController
+    {
+        private readonly IQuestionService _questionService;
+        private readonly IMapper _mapper;
+
+        public QuestionController(IQuestionService questionService,IMapper mapper)
+        {
+            _questionService = questionService;
+            this._mapper = mapper;
+        }
+
+        /// <summary>
+        /// 新增题库
+        /// </summary>
+        /// <param name="questionDto"></param>
+        /// <returns></returns>
+        [HttpPost(QuestionApiRoute.Add)]
+        public async Task Add([FromBody] AddQuestionDto questionDto)
+        {
+            await _questionService.AddAsync(questionDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 修改题库
+        /// </summary>
+        /// <param name="questionDto"></param>
+        /// <returns></returns>
+        [HttpPut(QuestionApiRoute.Update)]
+        public async Task Update([FromBody] UpdateQuestionDto questionDto)
+        {
+            await _questionService.UpdateAsync(questionDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 删除题库
+        /// </summary>
+        /// <param name="entityQueryRequest"></param>
+        /// <returns></returns>
+        [HttpDelete(QuestionApiRoute.Delete)]
+        public async Task Delete([FromBody] EntityQueryRequest entityQueryRequest)
+        {
+            await _questionService.DeleteAsync(entityQueryRequest, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 获取题库分页列表
+        /// </summary>
+        /// <param name="questionPagedRequest"></param>
+        /// <returns></returns>
+        [HttpPost(QuestionApiRoute.GetPagedList)]
+        public async Task<QuestionPageViewResponse> GetPagedList([FromBody] QuestionPagedRequest questionPagedRequest)
+        {
+            var questionPageViewResponse = await _questionService.GetPagedListAsync(questionPagedRequest);
+
+            return questionPageViewResponse as QuestionPageViewResponse;
+        }
+
+        /// <summary>
+        /// 获取题库
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        [HttpGet(QuestionApiRoute.Get)]
+        public async Task<QuestionDto> Get(string id)
+        {
+            var questionDto = await _questionService.GetAsync(new EntityQueryRequest
+            {
+                Id = id
+            });
+
+            return questionDto;
+        }
+
+        /// <summary>
+        /// 获取题库
+        /// </summary>
+        /// <param name="questionRequest"></param>
+        /// <returns></returns>
+        [HttpPost(QuestionApiRoute.GetList)]
+        public async Task<(int,List<QuestionViewResponse>)> GetList([FromBody]QuestionRequest questionRequest)
+        {
+            var questionPagedRequest = _mapper.Map<QuestionRequest, QuestionPagedRequest>(questionRequest);
+
+            return await _questionService.GetListAsync(questionPagedRequest);
+        }
+
+        /// <summary>
+        /// 更新开启状态
+        /// </summary>
+        /// <param name="statusActionRequests"></param>
+        /// <returns></returns>
+        [HttpPost(QuestionApiRoute.UpdateStatus)]
+        public async Task UpdateStatus([FromBody] List<StatusActionRequest> statusActionRequests)
+        {
+            await _questionService.UpdateStatus(statusActionRequests, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 导入Excel
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost(ExamManageApiRoute.ImportExcel)]
+        public async Task ImportExcel(IFormFile files)
+        {
+            await _questionService.ImportExcel(files, HttpContext.RequestAborted);
+        }
+
+    }
+}

+ 82 - 0
src/Hotline.Api/Controllers/Exam/SourcewareCategoryController.cs

@@ -0,0 +1,82 @@
+using Exam.Application;
+using Exam.Infrastructure.Data.Entity;
+using Exam.Share.ViewResponses.Sourceware;
+using Hotline.Application.Exam.Constants.ApiRoutes;
+using Hotline.Share.Dtos.Sourcewares;
+using Hotline.Share.Requests.Sourceware;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Hotline.Api.Controllers.Exam
+{
+    public class SourcewareCategoryController : BaseController
+    {
+        private readonly ISourcewareCategoryService _sourcewareCategoryService;
+        public SourcewareCategoryController(ISourcewareCategoryService sourcewareCategoryService)
+        {
+            _sourcewareCategoryService = sourcewareCategoryService;
+        }
+
+        /// <summary>
+        /// 新增课件类型
+        /// </summary>
+        /// <param name="sourcewareCategoryDto"></param>
+        /// <returns></returns>
+        [HttpPost(SourcewareCategoryApiRoute.Add)]
+        public async Task Add([FromBody] AddSourcewareCategoryDto sourcewareCategoryDto)
+        {
+            await _sourcewareCategoryService.AddAsync(sourcewareCategoryDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 修改课件类型
+        /// </summary>
+        /// <param name="sourcewareCategoryDto"></param>
+        /// <returns></returns>
+        [HttpPut(SourcewareCategoryApiRoute.Update)]
+        public async Task Update([FromBody] UpdateSourcewareCategoryDto sourcewareCategoryDto)
+        {
+            await _sourcewareCategoryService.UpdateAsync(sourcewareCategoryDto,HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 删除课件类型
+        /// </summary>
+        /// <param name="entityQueryRequest"></param>
+        /// <returns></returns>
+        [HttpDelete(SourcewareCategoryApiRoute.Delete)]
+
+        public async Task Delete(EntityQueryRequest entityQueryRequest)
+        {
+            await _sourcewareCategoryService.DeleteAsync(entityQueryRequest, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 获取树形列表
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost(SourcewareCategoryApiRoute.GetTreeList)]
+        public async Task<List<SourcewareCategoryViewResponse>> GetTreeList([FromBody] SourcewareCategoryRequest sourcewareCategoryRequest)
+        {
+            var sourcewareCategorys = await _sourcewareCategoryService.GetTreeAsync(sourcewareCategoryRequest);
+
+            return sourcewareCategorys;
+        }
+
+        /// <summary>
+        /// 获取课件分类
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        [HttpGet(SourcewareCategoryApiRoute.Get)]
+        public async Task<SourcewareCategoryDto> Get(string id)
+        {
+            var sourcewareCategoryDto = await _sourcewareCategoryService.GetAsync(new EntityQueryRequest
+            {
+                Id =id
+            });
+
+            return sourcewareCategoryDto;
+        }
+
+    }
+}

+ 82 - 0
src/Hotline.Api/Controllers/Exam/SourcewaresController.cs

@@ -0,0 +1,82 @@
+using Exam.Application.Interface.Sourcewares;
+using Exam.Infrastructure.Data.Entity;
+using Hotline.Application.Exam.Constants.ApiRoutes;
+using Hotline.Share.Dtos.Sourcewares;
+using Hotline.Share.Requests.Sourceware;
+using Hotline.Share.ViewResponses.Sourcewares;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Hotline.Api.Controllers.Exam
+{
+    public class SourcewaresController : BaseController
+    {
+        private readonly ISourcewareService _sourcewareService;
+
+        public SourcewaresController(ISourcewareService sourcewareService)
+        {
+            _sourcewareService = sourcewareService;
+        }
+
+        /// <summary>
+        /// 新增课件
+        /// </summary>
+        /// <param name="sourcewareDto"></param>
+        /// <returns></returns>
+        [HttpPost(SourcewareApiRoute.Add)]
+        public async Task Add([FromBody] AddSourcewareDto sourcewareDto)
+        {
+            await _sourcewareService.AddAsync(sourcewareDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 修改课件
+        /// </summary>
+        /// <param name="sourcewareDto"></param>
+        /// <returns></returns>
+        [HttpPut(SourcewareApiRoute.Update)]
+        public async Task Update([FromBody] UpdateSourcewareDto sourcewareDto)
+        {
+            await _sourcewareService.UpdateAsync(sourcewareDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 删除课件
+        /// </summary>
+        /// <param name="entityQueryRequest"></param>
+        /// <returns></returns>
+        [HttpDelete(SourcewareApiRoute.Delete)]
+        public async Task Delete([FromBody] EntityQueryRequest entityQueryRequest)
+        {
+            await _sourcewareService.DeleteAsync(entityQueryRequest, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 获取课件分页列表
+        /// </summary>
+        /// <param name="sourcewarePagedRequest"></param>
+        /// <returns></returns>
+        [HttpPost(SourcewareApiRoute.GetPagedList)]
+        public async Task<SourcewarePageViewResponse> GetPagedList([FromBody] SourcewarePagedRequest sourcewarePagedRequest)
+        {
+            var sourcewarePageViewResponse = await _sourcewareService.GetPagedListAsync(sourcewarePagedRequest);
+
+            return sourcewarePageViewResponse as SourcewarePageViewResponse; 
+        }
+
+        /// <summary>
+        /// 获取课件
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        [HttpGet(SourcewareApiRoute.Get)]
+        public async Task<SourcewareDto> Get(string id)
+        {
+            var sourcewareDto = await _sourcewareService.GetAsync(new EntityQueryRequest
+            {
+                Id = id
+            });
+
+            return sourcewareDto;
+        }
+    }
+}

+ 150 - 0
src/Hotline.Api/Controllers/Exam/TestPaperController.cs

@@ -0,0 +1,150 @@
+using Exam.Application.Interface.Exam;
+using Exam.Application.Interface.TestPapers;
+using Exam.Infrastructure.Data.Entity;
+using Exam.Share.ViewResponses.Exam;
+using Exam.Share.ViewResponses.TestPaper;
+using Hotline.Application.Exam.Constants.ApiRoutes;
+using Hotline.Application.Exam.Service.ExamManages;
+using Hotline.Repository.SqlSugar.Entitys;
+using Hotline.Share.Dtos.Questions;
+using Hotline.Share.Dtos.TestPapers;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.Requests.TestPaper;
+using Hotline.Share.ViewResponses.Exam;
+using Hotline.Share.ViewResponses.Questions;
+using Mapster;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Hotline.Api.Controllers.Exam
+{
+    public class TestPaperController : BaseController
+    {
+        private readonly ITestPaperService _testPaperService;
+        public TestPaperController(ITestPaperService testPaperService)
+        {
+            _testPaperService = testPaperService;
+        }
+
+        /// <summary>
+        /// 新增试卷
+        /// </summary>
+        /// <param name="testPaperDto"></param>
+        /// <returns></returns>
+        [HttpPost(TestPaperApiRoute.Add)]
+        public async Task Add([FromBody] AddTestPaperDto testPaperDto)
+        {
+            await _testPaperService.AddAsync(testPaperDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 修改试卷
+        /// </summary>
+        /// <param name="testPaperDto"></param>
+        /// <returns></returns>
+        [HttpPut(TestPaperApiRoute.Update)]
+        public async Task Update([FromBody] UpdateTestPaperDto testPaperDto)
+        {
+            await _testPaperService.UpdateAsync(testPaperDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 删除试卷
+        /// </summary>
+        /// <param name="entityQueryRequest"></param>
+        /// <returns></returns>
+        [HttpDelete(TestPaperApiRoute.Delete)]
+
+        public async Task Delete(EntityQueryRequest entityQueryRequest)
+        {
+            await _testPaperService.DeleteAsync(entityQueryRequest, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 获取试卷分页列表
+        /// </summary>
+        /// <param name="sourcewarePagedRequest"></param>
+        /// <returns></returns>
+        [HttpPost(TestPaperApiRoute.GetPagedList)]
+        public async Task<TestPaperPageViewResponse> GetPagedList([FromBody] TestPaperPagedRequest sourcewarePagedRequest)
+        {
+            var testPaperPageViewResponse = await _testPaperService.GetPagedListAsync(sourcewarePagedRequest);
+
+            return testPaperPageViewResponse as TestPaperPageViewResponse;
+        }
+
+        /// <summary>
+        /// 获取试卷列表
+        /// </summary>
+        /// <param name="testPaperRequest"></param>
+        /// <returns></returns>
+        [HttpGet(TestPaperApiRoute.GetList)]
+        public async Task<List<TestPaperViewResponse>> GetList([FromQuery] TestPaperRequest testPaperRequest)
+        {
+            var testPaperPagedRequest = testPaperRequest.Adapt<TestPaperPagedRequest>();
+
+            return (await _testPaperService.GetListAsync(testPaperPagedRequest)).Item2;
+        }
+
+
+        /// <summary>
+        /// 获取试卷分类
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        [HttpGet(TestPaperApiRoute.Get)]
+        public async Task<TestPaperDto> Get(string id)
+        {
+            var testPaperDto = await _testPaperService.GetAsync(new EntityQueryRequest
+            {
+                Id = id
+            });
+
+            return testPaperDto;
+        }
+
+        /// <summary>
+        /// 获取试卷试题数
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet(TestPaperApiRoute.Count)]
+        public async Task<List<TestPaperQuestionCountViewResponse>> GetPagedList([FromQuery] TestPaperQuestionCountRequest testPaperQuestionCountRequest)
+        {
+            var testPaperQuestionCountViewResponses = await _testPaperService.GetTestPaperQuestionCount(testPaperQuestionCountRequest);
+
+            return testPaperQuestionCountViewResponses;
+        }
+
+        /// <summary>
+        /// 根据规则获取试题
+        /// </summary>
+        /// <param name="testPaperQuestionRequest"></param>
+        /// <returns></returns>
+        [HttpPost(TestPaperApiRoute.GetQuestions)]
+        public async Task<List<SimpleQuestionViewResponse>> GetQuestions([FromBody]TestPaperQuestionRequest testPaperQuestionRequest)
+        {
+           return await _testPaperService.GetQuestionDtos(testPaperQuestionRequest);
+        }
+
+        /// <summary>
+        /// 获取标签试题数
+        /// </summary>
+        /// <param name="tagQuestionCountRequest"></param>
+        /// <returns></returns>
+        [HttpPost(TestPaperApiRoute.GetTagQuestionCount)]
+        public async Task<TagQuestionCountViewResponse> GetTagQuestionCount([FromBody] TagQuestionCountRequest tagQuestionCountRequest)
+        {
+            return await _testPaperService.GetTagQuestionCount(tagQuestionCountRequest);
+        }
+
+        /// <summary>
+        /// 更新开启状态
+        /// </summary>
+        /// <param name="statusActionRequests"></param>
+        /// <returns></returns>
+        [HttpPost(TestPaperApiRoute.UpdateStatus)]
+        public async Task UpdateStatus([FromBody] List<StatusActionRequest> statusActionRequests)
+        {
+            await _testPaperService.UpdateStatus(statusActionRequests,HttpContext.RequestAborted);
+        }
+    }
+}

+ 85 - 0
src/Hotline.Api/Controllers/Exam/TrainPlanController.cs

@@ -0,0 +1,85 @@
+using Exam.Application.Interface.Train;
+using Exam.Infrastructure.Data.Entity;
+using Exam.Share;
+using Exam.Share.ViewResponses.Train;
+using Hotline.Application.Exam.Constants.ApiRoutes;
+using Hotline.Share.Dtos.Trains;
+using Hotline.Share.Requests.Train;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Hotline.Api.Controllers.Exam
+{
+    public class TrainPlanController : BaseController
+    {
+        private readonly ITrainPlanService _trainTemplateService;
+
+        public TrainPlanController(ITrainPlanService trainTemplateService)
+        {
+            this._trainTemplateService = trainTemplateService;
+        }
+
+        /// <summary>
+        /// 新增培训模版
+        /// </summary>
+        /// <param name="extractRuleDto"></param>
+        /// <returns></returns>
+        [HttpPost(TrainPlanApiRoute.Add)]
+        public async Task Add([FromBody] AddTrainPlanDto extractRuleDto)
+        {
+            await _trainTemplateService.AddAsync(extractRuleDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 修改培训模版
+        /// </summary>
+        /// <param name="extractRuleDto"></param>
+        /// <returns></returns>
+        [HttpPut(TrainPlanApiRoute.Update)]
+        public async Task Update([FromBody] UpdateTrainPlanDto extractRuleDto)
+        {
+            await _trainTemplateService.UpdateAsync(extractRuleDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 删除培训模版
+        /// </summary>
+        /// <param name="entityQueryRequest"></param>
+        /// <returns></returns>
+        [HttpDelete(TrainPlanApiRoute.Delete)]
+
+        public async Task Delete(EntityQueryRequest entityQueryRequest)
+        {
+            await _trainTemplateService.DeleteAsync(entityQueryRequest, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 获取课件分页列表
+        /// </summary>
+        /// <param name="sourcewarePagedRequest"></param>
+        /// <returns></returns>
+        [HttpPost(TrainPlanApiRoute.GetPagedList)]
+        public async Task<TrainPlanPageViewResponse> GetPagedList([FromBody] TrainPlanPagedRequest sourcewarePagedRequest)
+        {
+            var trainTemplatePageViewResponse = await _trainTemplateService.GetPagedListAsync(sourcewarePagedRequest);
+
+            return trainTemplatePageViewResponse as TrainPlanPageViewResponse;
+        }
+
+
+        /// <summary>
+        /// 获取课件分类
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        [HttpGet(TrainPlanApiRoute.Get)]
+        public async Task<TrainPlanDto> Get(string id)
+        {
+            var trainTemplateDto = await _trainTemplateService.GetAsync(new EntityQueryRequest
+            {
+                Id = id
+            });
+
+            return trainTemplateDto;
+        }
+    }
+}

+ 114 - 0
src/Hotline.Api/Controllers/Exam/TrainRecordController.cs

@@ -0,0 +1,114 @@
+using DocumentFormat.OpenXml.Office2010.ExcelAc;
+using Exam.Infrastructure.Data.Entity;
+using Exam.Share.Dtos.Trains;
+using Exam.Share.ViewResponses.Train;
+using Hotline.Application.Exam.Constants.ApiRoutes;
+using Hotline.Share.Dtos.Trains;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.Requests.Train;
+using Hotline.Share.ViewResponses.Trains;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Train.Application.Interface.Train;
+
+namespace Hotline.Api.Controllers.Exam
+{
+    public class TrainRecordController : BaseController
+    {
+        private readonly ITrainRecordService _trainRecordService;
+
+        public TrainRecordController(ITrainRecordService trainRecordService)
+        {
+            this._trainRecordService = trainRecordService;
+        }
+
+        /// <summary>
+        /// 获取培训记录
+        /// </summary>
+        /// <param name="trainRecordPagedRequest"></param>
+        /// <returns></returns>
+        [HttpPost(TrainRecordApiRoute.GetPagedList)]
+        public async Task<TrainRecordPageViewResponse> GetPagedList([FromBody]TrainRecordPagedRequest trainRecordPagedRequest)
+        {
+            var trainRecordPageViewResponse = await _trainRecordService.GetPagedListAsync(trainRecordPagedRequest);
+            return trainRecordPageViewResponse as TrainRecordPageViewResponse;
+        }
+
+        /// <summary>
+        /// 初次培训
+        /// </summary>
+        /// <param name="addTrainDto"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        [HttpPost(TrainRecordApiRoute.Train)]
+        public async Task Train([FromBody] AddTrainDto addTrainDto, CancellationToken cancellationToken)
+        {
+            await _trainRecordService.TrainAsync(addTrainDto, cancellationToken);
+        }
+
+        /// <summary>
+        /// 获取培训习题
+        /// </summary>
+        /// <param name="trainPracticeRequest"></param>
+        /// <returns></returns>
+        [HttpGet(TrainRecordApiRoute.GetTrainPractice)]
+        public async Task<TrainPracticeDto> GetTrainPractice([FromQuery]TrainPracticeRequest trainPracticeRequest)
+        {
+            return await _trainRecordService.GetTrainPracticeAsync(trainPracticeRequest);
+        }
+
+        /// <summary>
+        /// 完成培训课件学习
+        /// </summary>
+        /// <param name="completeTrainPracticeDto"></param>
+        /// <returns></returns>
+        [HttpPost(TrainRecordApiRoute.CompleteTrainKnowladge)]
+        public async Task CompleteTrainKnowladge([FromBody] CompleteTrainKnowladgeDto completeTrainPracticeDto, CancellationToken cancellationToken)
+        {
+             await _trainRecordService.CompleteTrainKnowladgeAsync(completeTrainPracticeDto, cancellationToken);
+        }
+
+        /// <summary>
+        /// 结束培训
+        /// </summary>
+        /// <param name="completeTrainRecordDto"></param>
+        /// <returns></returns>
+        [HttpPost(TrainRecordApiRoute.CompleteTrainRecord)]
+        public async Task CompleteTrainRecord(CompleteTrainRecordDto completeTrainRecordDto, CancellationToken cancellationToken)
+        {
+            await _trainRecordService.CompleteTrainRecordAsync(completeTrainRecordDto, cancellationToken);
+        }
+
+        /// <summary>
+        /// 培训结果统计
+        /// </summary>
+        /// <param name="trainResultReportPagedRequest"></param>
+        /// <returns></returns>
+        [HttpPost(TrainRecordApiRoute.AnalysisTrainResult)]
+        public async Task<TrainResultPagedViewResponse> AnalysisTrainResult([FromBody]TrainResultReportPagedRequest trainResultReportPagedRequest)
+        {
+            return await _trainRecordService.AnalysisTrainResult(trainResultReportPagedRequest);
+        }
+
+        /// <summary>
+        /// 计算培训结果比例
+        /// </summary>
+        /// <param name="trainResultReportPagedRequest"></param>
+        /// <returns></returns>
+        [HttpPost(TrainRecordApiRoute.CalcuteAnalysisRate)]
+        public async Task<TrainResultRateViewResponse> CalcuteAnalysisRate([FromBody] TrainResultReportPagedRequest trainResultReportPagedRequest)
+        {
+            return await _trainRecordService.CalcuteAnalysisRate(trainResultReportPagedRequest);
+        }
+
+        /// <summary>
+        /// 获取培训记录
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost(TrainRecordApiRoute.Get)]
+        public async Task<TrainRecordDto> GetAsync([FromBody] EntityQueryRequest entityQueryRequest)
+        {
+            return await _trainRecordService.GetAsync(entityQueryRequest);
+        }
+    }
+}

+ 98 - 0
src/Hotline.Api/Controllers/Exam/TrainTemplateController.cs

@@ -0,0 +1,98 @@
+using Exam.Application.Interface.Train;
+using Exam.Infrastructure.Data.Entity;
+using Exam.Share.ViewResponses.Train;
+using Hotline.Application.Exam.Constants.ApiRoutes;
+using Hotline.Share.Dtos.Trains;
+using Hotline.Share.Requests.Train;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Hotline.Api.Controllers.Exam
+{
+
+    public class TrainTemplateController : BaseController
+    {
+        private readonly ITrainTemplateService _trainTemplateService;
+
+        public TrainTemplateController(ITrainTemplateService trainTemplateService)
+        {
+            this._trainTemplateService = trainTemplateService;
+        }
+
+        /// <summary>
+        /// 新增培训模版
+        /// </summary>
+        /// <param name="extractRuleDto"></param>
+        /// <returns></returns>
+        [HttpPost(TrainTemplateApiRoute.Add)]
+        public async Task Add([FromBody] AddTrainTemplateDto extractRuleDto)
+        {
+            await _trainTemplateService.AddAsync(extractRuleDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 修改培训模版
+        /// </summary>
+        /// <param name="extractRuleDto"></param>
+        /// <returns></returns>
+        [HttpPut(TrainTemplateApiRoute.Update)]
+        public async Task Update([FromBody] UpdateTrainTemplateDto extractRuleDto)
+        {
+            await _trainTemplateService.UpdateAsync(extractRuleDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 删除培训模版
+        /// </summary>
+        /// <param name="entityQueryRequest"></param>
+        /// <returns></returns>
+        [HttpDelete(TrainTemplateApiRoute.Delete)]
+
+        public async Task Delete(EntityQueryRequest entityQueryRequest)
+        {
+            await _trainTemplateService.DeleteAsync(entityQueryRequest, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 获取课件分页列表
+        /// </summary>
+        /// <param name="sourcewarePagedRequest"></param>
+        /// <returns></returns>
+        [HttpPost(TrainTemplateApiRoute.GetPagedList)]
+        public async Task<TrainTemplatePageViewResponse> GetPagedList([FromBody] TrainTemplatePagedRequest sourcewarePagedRequest)
+        {
+            var trainTemplatePageViewResponse = await _trainTemplateService.GetPagedListAsync(sourcewarePagedRequest);
+
+            return trainTemplatePageViewResponse as TrainTemplatePageViewResponse;
+        }
+
+
+        /// <summary>
+        /// 获取课件分类
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        [HttpGet(TrainTemplateApiRoute.Get)]
+        public async Task<TrainTemplateDto> Get(string id)
+        {
+            var trainTemplateDto = await _trainTemplateService.GetAsync(new EntityQueryRequest
+            {
+                Id = id
+            });
+
+            return trainTemplateDto;
+        }
+
+        /// <summary>
+        /// 获取培训模版
+        /// </summary>
+        /// <param name="trainTemplatePagedRequest"></param>
+        /// <returns></returns>
+        [HttpGet(TrainTemplateApiRoute.GetList)]
+        public async Task<List<TrainTemplateViewResponse>> GetList([FromQuery] TrainTemplatePagedRequest trainTemplatePagedRequest)
+        {
+            var result = await _trainTemplateService.GetListAsync(trainTemplatePagedRequest);
+
+            return result.Item2;
+        }
+    }
+}

+ 168 - 0
src/Hotline.Api/Controllers/Exam/UserExamController.cs

@@ -0,0 +1,168 @@
+using Exam.Application.Interface.Exam;
+using Exam.Share;
+using Exam.Share.Dtos.ExamManage;
+using Exam.Share.ViewResponses.Exam;
+using Hotline.Application.Exam.Constants.ApiRoutes;
+using Hotline.Share.Dtos.ExamManages;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.ViewResponses.Exam;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using System.ComponentModel;
+
+namespace Hotline.Api.Controllers.Exam
+{
+    public class UserExamController : BaseController
+    {
+        private readonly IUserExamService _userExamService;
+
+        public UserExamController(IUserExamService userExamService)
+        {
+            this._userExamService = userExamService;
+        }
+
+        /// <summary>
+        /// 获取我的考试结果
+        /// </summary>
+        /// <param name="userExamPagedRequest"></param>
+        /// <returns></returns>
+        [HttpPost(UserExamApiRoute.GetPagedList)]
+        public async Task<UserExamResultPageViewResponse> GetPagedList([FromBody] UserExamPagedRequest userExamPagedRequest)
+        {
+            return await _userExamService.GetPagedListAsync(userExamPagedRequest) as UserExamResultPageViewResponse;
+        }
+
+        /// <summary>
+        /// 开始考试
+        /// </summary>
+        /// <param name="startUserExamDto"></param>
+        /// <returns></returns>
+        [HttpPost(UserExamApiRoute.Start)]
+        public async Task<StartExamViewResponse> Start([FromBody] StartUserExamDto startUserExamDto)
+        {
+            return await _userExamService.StartUserExamAsync(startUserExamDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 获取试题类型分组
+        /// </summary>
+        /// <param name="examQuestionGroupRequest"></param>
+        /// <returns></returns>
+        [HttpGet(UserExamApiRoute.GetExamQuestionGroup)]
+        public async Task<List<ExamQuestionViewResponse>> GetExamQuestionGroup([FromQuery]ExamQuestionGroupRequest examQuestionGroupRequest)
+        {
+            return await _userExamService.GetExamQuestionViewResponses(examQuestionGroupRequest);
+        }
+
+        /// <summary>
+        /// 获取试题详情
+        /// </summary>
+        /// <param name="examQuestionRequest"></param>
+        /// <returns></returns>
+        [HttpGet(UserExamApiRoute.GetExamQuestion)]
+        public async Task<ExamQuestionDto> GetExamQuestion([FromQuery]ExamQuestionRequest examQuestionRequest)
+        {
+            return await _userExamService.GetExamQuestionDto(examQuestionRequest);
+        }
+
+        /// <summary>
+        /// 考试
+        /// </summary>
+        /// <param name="addUserExamItemDto"></param>
+        /// <returns></returns>
+        [HttpPost(UserExamApiRoute.Exam)]
+        public async Task<StartExamViewResponse> Exam([FromBody] UpdateUserExamItemDto updateUserExamItemDto)
+        {
+            return await _userExamService.ExamAsync(updateUserExamItemDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 提交考试
+        /// </summary>
+        /// <param name="submitExamDto"></param>
+        /// <returns></returns>
+        [HttpPost(UserExamApiRoute.Submit)]
+        public async Task Submit([FromBody] SubmitExamDto submitExamDto)
+        {
+            await _userExamService.SubmitAsync(submitExamDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 阅卷
+        /// </summary>
+        /// <param name="gradingExtamItemDto"></param>
+        /// <returns></returns>
+        [HttpPost(UserExamApiRoute.Grading)]
+        public async Task<GradingExamQuestionDto> Grading([FromBody] GradingExtamItemDto gradingExtamItemDto)
+        {
+          return  await _userExamService.GradingAsync(gradingExtamItemDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 完成阅卷
+        /// </summary>
+        /// <param name="gradingExtamDto"></param>
+        /// <returns></returns>
+        [HttpPost(UserExamApiRoute.CompleteGrading)]
+        public async Task CompleteGrading([FromBody] GradingExamDto gradingExtamDto)
+        {
+            await _userExamService.CompleteGradingAsync(gradingExtamDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 获取阅卷试题
+        /// </summary>
+        /// <param name="gradingExamRequest"></param>
+        /// <returns></returns>
+        [HttpGet(UserExamApiRoute.GetGradingQuestions)]
+        public async Task<List<GradingExamQuestionDto>> GetGradingQuestions([FromQuery] GradingExamRequest gradingExamRequest)
+        {
+            return await _userExamService.GetGradingExamQuestion(gradingExamRequest);
+        }
+
+        /// <summary>
+        /// 获取阅卷试题
+        /// </summary>
+        /// <param name="examQuestionGroupRequest"></param>
+        /// <returns></returns>
+        [HttpGet(UserExamApiRoute.GetGradingQuestionViewResponces)]
+        public async Task<List<GradingQuestionViewResponce>> GetGradingQuestionViewResponces([FromQuery] ExamQuestionGroupRequest examQuestionGroupRequest)
+        {
+            return await _userExamService.GetGradingQuestionViewResponces(examQuestionGroupRequest);
+        }
+
+        /// <summary>
+        /// 获取阅卷试题
+        /// </summary>
+        /// <param name="viewGradingExamRequest"></param>
+        /// <returns></returns>
+        [HttpGet(UserExamApiRoute.ViewGradingExamQuestion)]
+        public async Task<GradingExamQuestionDto> ViewGradingExamQuestion([FromQuery] ViewGradingExamRequest viewGradingExamRequest)
+        {
+            return await _userExamService.ViewGradingExamQuestion(viewGradingExamRequest);
+        }
+
+        /// <summary>
+        /// 获取未考试的考生信息
+        /// </summary>
+        /// <param name="unExamUserReportPagedRequest"></param>
+        /// <returns></returns>
+        [HttpPost(UserExamApiRoute.GetUnExamUsers)]
+        public async Task<UnExamUserPageViewResponse> GetUnExamUsers([FromBody] UnExamUserReportPagedRequest unExamUserReportPagedRequest)
+        {
+            return await _userExamService.GetUnExamUsers(unExamUserReportPagedRequest);
+        }
+
+        /// <summary>
+        /// 成绩统计分析
+        /// </summary>
+        /// <param name="userExamResultReportPagedRequest"></param>
+        /// <returns></returns>
+
+        [HttpPost(UserExamApiRoute.GetUserExamResults)]
+        public async Task<UserExamResultPageViewResponse> GetUserExamResults([FromBody] UserExamResultReportPagedRequest userExamResultReportPagedRequest) {
+
+            return await _userExamService.GetUserExamResults(userExamResultReportPagedRequest); 
+        }
+    }
+}

+ 1 - 0
src/Hotline.Api/Controllers/HomeController.cs

@@ -224,6 +224,7 @@ public class HomeController : BaseController
             RecordDownLoadPrefix = _systemSettingCacheManager.GetSetting(SettingConstants.RecordDownLoadPrefix).SettingValue?.FirstOrDefault(),
             Luzhouhcp = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.LuZhouHaoChaPing).SettingValue[0]),
             LocationCenter = locationCenter,
+            IsEarly = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.IsEarly).SettingValue[0])
         };
         return rsp;
     }

+ 33 - 3
src/Hotline.Api/Controllers/JudicialManagementOrdersController.cs

@@ -14,6 +14,7 @@ using Hotline.Share.Dtos.Order;
 using Hotline.Share.Enums.JudicialManagement;
 using Hotline.Share.Requests;
 using Hotline.Tools;
+using JiebaNet.Segmenter.Common;
 using MapsterMapper;
 using Microsoft.AspNetCore.Mvc;
 using SqlSugar;
@@ -575,8 +576,8 @@ namespace Hotline.Api.Controllers
                 .GroupBy(d => d.Id)
                  .Select(d => new EventClassificationOrderCountDto
                  {
-                     TheClueIsTrue = SqlFunc.AggregateSum(SqlFunc.IIF(d.IsTheClueTrue.HasValue && d.IsTheClueTrue.Value == true, 1, 0)),
-                     TheClueIsNotTrue = SqlFunc.AggregateSum(SqlFunc.IIF(d.IsTheClueTrue.HasValue && d.IsTheClueTrue.Value == false, 1, 0)),
+                     TheClueIsTrue = SqlFunc.AggregateSum(SqlFunc.IIF(d.IsEnforcementOrder.HasValue && d.IsEnforcementOrder.Value == true, 1, 0)),
+                     TheClueIsNotTrue = SqlFunc.AggregateSum(SqlFunc.IIF(d.IsEnforcementOrder.HasValue && d.IsEnforcementOrder.Value == false, 1, 0)),
                      EnforcementOrder = SqlFunc.AggregateSum(SqlFunc.IIF(d.IsEnforcementOrder.HasValue && d.IsEnforcementOrder.Value == true, 1, 0)),
                      PassTheBuckOrder = SqlFunc.AggregateSum(SqlFunc.IIF(d.IsPassTheBuckOrder, 1, 0)),
                      ToBeVerified = SqlFunc.AggregateSum(SqlFunc.IIF(d.IsTheClueTrue == null, 1, 0)),
@@ -589,7 +590,8 @@ namespace Hotline.Api.Controllers
                 TheClueIsNotTrue = data.Sum(x => x.TheClueIsNotTrue),
                 EnforcementOrder = data.Sum(x => x.EnforcementOrder),
                 PassTheBuckOrder = data.Sum(x => x.PassTheBuckOrder),
-                ToBeVerified = data.Sum(x => x.ToBeVerified)
+                ToBeVerified = data.Sum(x => x.ToBeVerified),
+                TotalOrder = data.Sum(x => x.TheClueIsNotTrue) + data.Sum(x => x.EnforcementOrder) + data.Sum(x => x.PassTheBuckOrder)
             };
 
             return new { List = items, OrderCount = orderCount };
@@ -710,6 +712,34 @@ namespace Hotline.Api.Controllers
             return await _enforcementApplication.QueryIndustryOrderStatics(queryIndustryOrderStatisticsRequest);
         }
 
+        /// <summary>
+        /// 执法领域统计--导出
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost("enforcement_industry_statistics_order_export")]
+        public async Task<FileStreamResult> QueryIndustryOrderStaticsExportAsync([FromBody] ExportExcelDto<QueryIndustryOrderStatisticsRequest> dto)
+        {
+            var list = await _enforcementApplication.QueryIndustryOrderStatics(dto.QueryDto);
+
+            list.Add(new IndustryOrderStaticsDto()
+            {
+                IndustryName = "总计",
+                EnforcementOrderCount = list.Sum(x => x.EnforcementOrderCount),
+                NotEnforcementOrderCount = list.Sum(x => x.NotEnforcementOrderCount),
+                ToBeVerifiedOrderCount = list.Sum(x => x.ToBeVerifiedOrderCount),
+                TotalOrderCount = list.Sum(x => x.TotalOrderCount)
+            });
+            dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass<IndustryOrderStaticsDto>(dto.ColumnInfos);
+            var dtos = list
+                .Select(stu => _mapper.Map(stu, typeof(IndustryOrderStaticsDto), dynamicClass))
+                .Cast<object>()
+                .ToList();
+
+            var stream = ExcelHelper.CreateStream(dtos);
+
+            return ExcelStreamResult(stream, "执法领域统计");
+        }
+
         /// <summary>
         /// 执法部门办件统计---明细---导出
         /// </summary>

+ 114 - 2
src/Hotline.Api/Controllers/OrderController.cs

@@ -82,6 +82,9 @@ using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
 using OrderDto = Hotline.Share.Dtos.Order.OrderDto;
 using UserInfo = Hotline.Share.Dtos.FlowEngine.UserInfo;
+using Hotline.Caching.Services;
+using Hotline.Early;
+using MathNet.Numerics.Distributions;
 
 namespace Hotline.Api.Controllers;
 
@@ -168,6 +171,9 @@ public class OrderController : BaseController
     private readonly ICircularRecordDomainService _circularRecordDomainService;
     private readonly IRepository<Hotline.Special.SpecialNumber> _specialNumberRepository;
     private readonly IRepository<OrderVisitDetailCopy> _orderVisitDetailCopyRepository;
+    private readonly IRepository<OrderEarlyWarning> _orderEarlyWarningRepository;
+    private readonly IRepository<EarlyWarningSetting> _earlyWarningSettingRepository;
+    private readonly IRepository<OrderTsDetails> _orderTsDetailsRepository;
     private readonly IRedPackAuditRepository _redPackAuditRepository;
     private readonly IRepository<OrderPublishTemp> _orderPublishTempRepository;
 
@@ -249,6 +255,9 @@ public class OrderController : BaseController
         IRedPackAuditRepository redPackAuditRepository,
         IRepository<Hotline.Special.SpecialNumber> specialNumberRepository,
         IRepository<OrderVisitDetailCopy> orderVisitDetailCopyRepository,
+        IRepository<OrderEarlyWarning> orderEarlyWarningRepository,
+        IRepository<EarlyWarningSetting> earlyWarningSettingRepository,
+        IRepository<OrderTsDetails> orderTsDetailsRepository,
         ISystemLogRepository systemLogRepository,
         IRepository<OrderPublishTemp> orderPublishTempRepository)
     {
@@ -326,6 +335,9 @@ public class OrderController : BaseController
         _systemOrganizeRepository = systemOrganizeRepository;
         _orderComplementRepository = orderComplementRepository;
         _circularRecordDomainService = circularRecordDomainService;
+        _orderEarlyWarningRepository = orderEarlyWarningRepository;
+        _earlyWarningSettingRepository = earlyWarningSettingRepository;
+        _orderTsDetailsRepository = orderTsDetailsRepository;
         _redPackAuditRepository = redPackAuditRepository;
         _specialNumberRepository = specialNumberRepository;
         _orderVisitDetailCopyRepository = orderVisitDetailCopyRepository;
@@ -4681,6 +4693,7 @@ public class OrderController : BaseController
     [HttpGet("{id}")]
     public async Task<OrderDto> Get(string id)
     {
+
         var order = await _orderRepository.Queryable()
             .Includes(d => d.OrderExtension)
             .Includes(d => d.OrderDelays)
@@ -4972,8 +4985,37 @@ public class OrderController : BaseController
             orderTerminateList.Any(x => x.Status == ETerminateStatus.Refuse) ? "不同意" :
             orderTerminateList.Any(x => x.Status == ETerminateStatus.Approval || x.Status == ETerminateStatus.SendBack) ? "审批中" : null;
 
-        // 加载随手拍信息
-        await _orderSnapshotApplication.GetOrderDetailAsync(id, dto, HttpContext.RequestAborted);
+        if (_systemSettingCacheManager.Snapshot)
+        {
+            await _orderSnapshotRepository.Queryable()
+                .LeftJoin<IndustryCase>((snapshot, industryCase) => snapshot.IndustryCase == industryCase.Id)
+                .Where((snapshot, industryCase) => snapshot.Id == order.Id)
+                .Select((snapshot, industryCase) => new {
+                    snapshot.IndustryId,
+                    snapshot.IndustryName,
+                    industryCase.Name,
+                    snapshot.IsRectifyDepartment,
+                    snapshot.IsDangerDepartment,
+                    snapshot.IsSafetyDepartment,
+                    snapshot.SignRemark,
+                    snapshot.SignUserId,
+                    snapshot.SignUserName,
+                    snapshot.SignTime
+                })
+                .FirstAsync(HttpContext.RequestAborted)
+                .Then(async snapshot =>
+                {
+                    dto.IndustryName = snapshot.IndustryName + " " + snapshot.Name;
+                    dto.IndustryId = snapshot.IndustryId;
+                    dto.IsRectifyDepartment = snapshot.IsRectifyDepartment;
+                    dto.IsDangerDepartment = snapshot.IsDangerDepartment;
+                    dto.IsSafetyDepartment = snapshot.IsSafetyDepartment;
+                    dto.SignUserId = snapshot.SignUserId;
+                    dto.SignUserName = snapshot.SignUserName;
+                    dto.SignTime = snapshot.SignTime;
+                    dto.SignRemark = snapshot.SignRemark;
+                });
+        }
 
         //处理回访录音
         if (dto.OrderVisits != null && dto.OrderVisits.Count > 0)
@@ -5025,6 +5067,44 @@ public class OrderController : BaseController
         return _sessionContext.OrgIsCenter ? dto : dto.DataMask(_appOptions.Value.IsZiGong);
     }
 
+    /// <summary>
+    /// 查询工单预警信息
+    /// </summary>
+    /// <param name="id"></param>
+    /// <returns></returns>
+    [HttpGet("orderearly/{id}")]
+    public async Task<object> GetOrderEarly(string id)
+    {
+        var now = DateTime.Now;
+        var order = await _orderRepository.GetAsync(id, HttpContext.RequestAborted);
+
+        var query = _orderRepository.Queryable()
+         .Where(x => x.CreationTime <= now && x.CreationTime >= SqlFunc.DateAdd(now, -30) && x.Sensitive != null);
+        var exp = Expressionable.Create<Order>();
+        foreach (var item in order!.Sensitive!)
+        {
+            exp = exp.Or(x => SqlFunc.JsonArrayAny(x.Sensitive, item));
+        }
+        var orderList = await query.Where(exp.ToExpression()).ToListAsync();
+
+        var orderEarlyList = new List<OrderEarlyInfo>();
+        foreach (var item in order!.Sensitive!)
+        {
+            var model = new OrderEarlyInfo();
+            model.KeyWord = item;
+            model.WeekNum = orderList.Where(x => x.Sensitive!.Contains(item) && x.CreationTime <= now && x.CreationTime >= now.AddDays(-7)).Count();
+            model.MonthNum = orderList.Where(x => x.Sensitive!.Contains(item)).Count();
+            orderEarlyList.Add(model);
+        }
+        var orderEarly = await _orderEarlyWarningRepository.Queryable().FirstAsync(x => x.OrderId == id);
+
+        return new
+        {
+            OrderEarlyInfo = orderEarlyList,
+            OrderEarly = orderEarly,
+        };
+    }
+
     /// <summary>
     /// 新增工单
     /// </summary>
@@ -5714,6 +5794,23 @@ public class OrderController : BaseController
 
         var order = await _orderApplication.SaveOrderWorkflowInfo(dto, HttpContext.RequestAborted);
 
+        //预警模块
+        if (bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.IsEarly).SettingValue[0]))
+        {
+            if (dto.Data.DispatchEarlyWarning != null && dto.Data.DispatchEarlyWarning.Count > 0)
+            {
+                var model = await _orderEarlyWarningRepository.Queryable().Where(x => x.OrderId == order.Id).FirstAsync(HttpContext.RequestAborted);
+                if (model != null)
+                {
+                    model.DispatchEarlyWarning = _mapper.Map<List<EarlyWarningSetting>>(dto.Data.DispatchEarlyWarning);
+                    //model.DispatchEarlyWarning = model.CanChooseEarlyWarning?.Where(x => dto.Data.DispatchEarlyWarning.Select(d=>d.Id).Contains(x.Id)).ToList();
+                    await _orderEarlyWarningRepository.UpdateAsync(model, HttpContext.RequestAborted);
+                    await _publisher.PublishAsync(new OrderEarlyWarningNotify(order, model.DispatchEarlyWarning), PublishStrategy.ParallelWhenAll, HttpContext.RequestAborted);
+                }
+            }
+        }
+
+
         // 随手拍业务处理
         await _orderSnapshotApplication.SaveOrderWorkflowInfo(dto);
 
@@ -6239,6 +6336,21 @@ public class OrderController : BaseController
         return rsp;
     }
 
+    /// <summary>
+    /// 预警基础数据
+    /// </summary>
+    /// <returns></returns>
+    [HttpGet("early-base-data")]
+    public async Task<object> EarlyBaseData()
+    {
+        var rsp = new
+        {
+            EarlyWarningLevel = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.EarlyWarningLevel),
+            EarlyWarningType = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.EarlyWarningType),
+        };
+        return rsp;
+    }
+
     /// <summary>
     /// 新增页面基础数据
     /// </summary>

+ 1 - 0
src/Hotline.Api/Controllers/Snapshot/SnapshotOrderController.cs

@@ -2,6 +2,7 @@
 using Hotline.Application.FlowEngine;
 using Hotline.Application.Snapshot.Contracts;
 using Hotline.Application.Snapshot;
+using Hotline.Application.Snapshot.Contracts;
 using Hotline.Caching.Interfaces;
 using Hotline.Caching.Services;
 using Hotline.Configurations;

+ 4 - 0
src/Hotline.Api/Controllers/TestController.cs

@@ -34,6 +34,7 @@ using Hotline.Settings.Hotspots;
 using Hotline.Settings.TimeLimitDomain;
 using Hotline.Settings.TimeLimits;
 using Hotline.Share.Dtos;
+using Hotline.Share.Dtos.ExportWord;
 using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Share.Dtos.Home;
 using Hotline.Share.Dtos.Identity;
@@ -50,6 +51,8 @@ using Hotline.Share.Enums.Push;
 using Hotline.Share.Mq;
 using Hotline.ThirdAccountDomainServices.Interfaces;
 using Hotline.Users;
+using J2N.Text;
+using JiebaNet.Segmenter.Common;
 using Mapster;
 using MapsterMapper;
 using MediatR;
@@ -60,6 +63,7 @@ using MiniExcelLibs;
 using NETCore.Encrypt;
 using NETCore.Encrypt.Internal;
 using SqlSugar;
+using System.Text;
 using XC.RSAUtil;
 using XF.Domain.Authentications;
 using XF.Domain.Cache;

+ 14 - 16
src/Hotline.Api/config/appsettings.Development.json

@@ -36,9 +36,6 @@
     "LuZhou": {
       "AreaCode": "510500",
       "CallCenterType": "XingTang"
-    },
-    "FileUpload": {
-      "Url": "http://110.188.24.28:50120/"
     }
   },
   "CallCenterConfiguration": {
@@ -65,23 +62,23 @@
       "Ip": "222.213.23.229"
     },
     "XingTang": {
-      //"DbConnectionString": "server=123.56.10.71;Database=callcenter_db;Uid=root;Pwd=Lhw1981!(*!"
-      "DbConnectionString": "server=110.188.24.182;Database=callcenter_xingtang;Uid=dev;Pwd=fengwo11!!"
-      //"DbConnectionString": "PORT=50143;server=110.188.24.182;Database=callcenter_db;Uid=dev;Pwd=fengwo123!@#;"
+        //"DbConnectionString": "server=123.56.10.71;Database=callcenter_db;Uid=root;Pwd=Lhw1981!(*!"
+        "DbConnectionString": "server=110.188.24.182;Database=callcenter_xingtang;Uid=dev;Pwd=fengwo11!!"
+        //"DbConnectionString": "PORT=50143;server=110.188.24.182;Database=callcenter_db;Uid=dev;Pwd=fengwo123!@#;"
     }
   },
-  "ConnectionStrings": {
-    "Hotline": "PORT=5432;DATABASE=hotline;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;",
-    //"Hotline1": "PORT=5432;DATABASE=hotline_dev;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;"
-  },
+    "ConnectionStrings": {
+        "Hotline": "PORT=5432;DATABASE=hotline_dev;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;",
+        "CAP": "PORT=5432;DATABASE=fwmq;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;Search Path=cap"
+    },
   "Cache": {
     "Host": "110.188.24.182",
     "Port": 50179,
     "Password": "fengwo123!$!$",
-    "Database": 3 //hotline:3, dev:5, test:2, demo:4
+    "Database": 5 //hl:3, dev:5, test:2, demo:4
   },
   "Swagger": true,
-  "AccLog": false,
+  "AccLog":  false,
   "Cors": {
     "Origins": [ "http://localhost:8888", "http://admin.hotline.fw.com", "http://localhost:80", "http://localhost:8113" ]
   },
@@ -122,10 +119,11 @@
     "UseDashBoard": true,
     "FailedRetryCount": 5,
     "RabbitMq": {
-      "UserName": "dev",
-      "Password": "123456",
-      "HostName": "110.188.24.182",
-      "VirtualHost": "fwt-master"
+        "UserName": "dev",
+        "Password": "123456",
+        "HostName": "110.188.24.182",
+        "VirtualHost": "fwt-dev"
+        // "VirtualHost": "fwt-unittest"
     }
   },
   "FwClient": {

+ 47 - 47
src/Hotline.Application/CallCenter/Calls/CurrentWaitNumService.cs

@@ -1,53 +1,53 @@
-using Hotline.Caching.Interfaces;
-using Hotline.Realtimes;
-using Hotline.Settings;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Tr.Sdk;
-using XF.Domain.Dependency;
+//using Hotline.Caching.Interfaces;
+//using Hotline.Realtimes;
+//using Hotline.Settings;
+//using Microsoft.Extensions.DependencyInjection;
+//using Microsoft.Extensions.Hosting;
+//using System;
+//using System.Collections.Generic;
+//using System.Linq;
+//using System.Text;
+//using System.Threading.Tasks;
+//using Tr.Sdk;
+//using XF.Domain.Dependency;
 
-namespace Hotline.Application.CallCenter.Calls
-{
-    public class CurrentWaitNumService : BackgroundService, IIgnoreDependency
-    {
-        private readonly IServiceScopeFactory _serviceScopeFactory;
+//namespace Hotline.Application.CallCenter.Calls
+//{
+//    public class CurrentWaitNumService : BackgroundService, IIgnoreDependency
+//    {
+//        private readonly IServiceScopeFactory _serviceScopeFactory;
 
-        public CurrentWaitNumService(IServiceScopeFactory serviceScopeFactory)
-        {
-            _serviceScopeFactory = serviceScopeFactory;
-        }
+//        public CurrentWaitNumService(IServiceScopeFactory serviceScopeFactory)
+//        {
+//            _serviceScopeFactory = serviceScopeFactory;
+//        }
 
-        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
-        {
-            using var scope = _serviceScopeFactory.CreateScope();
-            var realtimeService = scope.ServiceProvider.GetRequiredService<IRealtimeService>();
-            var _systemSettingCacheManager = scope.ServiceProvider.GetRequiredService<ISystemSettingCacheManager>();
-            var _trClient = scope.ServiceProvider.GetRequiredService<ITrClient>();
-            int times = 3000;//5秒
-            var callinQueueIds = _systemSettingCacheManager.GetSetting(SettingConstants.CallInQueueId)?.SettingValue.ToList();
-            while (!stoppingToken.IsCancellationRequested)
-            {
-                try
-                {
-                    int count = 0;
-                    foreach (var id in callinQueueIds)
-                    {
-                        var rsp = await _trClient.QueryQueueWaitNumAsync(new Tr.Sdk.Tels.QueryQueueWaitNumRequest() { QueueId = id }, stoppingToken);
-                        count += Convert.ToInt32(rsp.Count);
-                    }
+//        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+//        {
+//            //using var scope = _serviceScopeFactory.CreateScope();
+//            //var realtimeService = scope.ServiceProvider.GetRequiredService<IRealtimeService>();
+//            //var _systemSettingCacheManager = scope.ServiceProvider.GetRequiredService<ISystemSettingCacheManager>();
+//            //var _trClient = scope.ServiceProvider.GetRequiredService<ITrClient>();
+//            //int times = 3000;//5秒
+//            //var callinQueueIds = _systemSettingCacheManager.GetSetting(SettingConstants.CallInQueueId)?.SettingValue.ToList();
+//            //while (!stoppingToken.IsCancellationRequested)
+//            //{
+//            //    try
+//            //    {
+//            //        int count = 0;
+//            //        foreach (var id in callinQueueIds)
+//            //        {
+//            //            var rsp = await _trClient.QueryQueueWaitNumAsync(new Tr.Sdk.Tels.QueryQueueWaitNumRequest() { QueueId = id }, stoppingToken);
+//            //            count += Convert.ToInt32(rsp.Count);
+//            //        }
 
-                    await realtimeService.CurrentWaitNumAsync(count, stoppingToken);
-                }
-                catch { }
-                await Task.Delay(times);
-            }
+//            //        await realtimeService.CurrentWaitNumAsync(count, stoppingToken);
+//            //    }
+//            //    catch { }
+//            //    await Task.Delay(times);
+//            //}
 
 
-        }
-    }
-}
+//        }
+//    }
+//}

+ 41 - 41
src/Hotline.Application/CallCenter/Calls/ToDayWaitNumService.cs

@@ -1,43 +1,43 @@
-using Hotline.CallCenter.Calls;
-using Hotline.Realtimes;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using XF.Domain.Repository;
+//using Hotline.CallCenter.Calls;
+//using Hotline.Realtimes;
+//using Microsoft.Extensions.DependencyInjection;
+//using Microsoft.Extensions.Hosting;
+//using System;
+//using System.Collections.Generic;
+//using System.Linq;
+//using System.Text;
+//using System.Threading.Tasks;
+//using XF.Domain.Repository;
 
-namespace Hotline.Application.CallCenter.Calls
-{
-    public class ToDayWaitNumService : BackgroundService
-    {
-        private readonly IServiceScopeFactory _serviceScopeFactory;
-        public ToDayWaitNumService(IServiceScopeFactory serviceScopeFactory)
-        {
-                _serviceScopeFactory = serviceScopeFactory;
-        }
+//namespace Hotline.Application.CallCenter.Calls
+//{
+//    public class ToDayWaitNumService : BackgroundService
+//    {
+//        private readonly IServiceScopeFactory _serviceScopeFactory;
+//        public ToDayWaitNumService(IServiceScopeFactory serviceScopeFactory)
+//        {
+//                _serviceScopeFactory = serviceScopeFactory;
+//        }
 
-        protected async override Task ExecuteAsync(CancellationToken stoppingToken)
-        {
-            using var scope = _serviceScopeFactory.CreateScope();
-            var realtimeService = scope.ServiceProvider.GetRequiredService<IRealtimeService>();
-            var _trcallrecordRepoository = scope.ServiceProvider.GetRequiredService<IRepository<TrCallRecord>>();
-            int times = 60000;//一分钟
-            while (!stoppingToken.IsCancellationRequested)
-            {
-                try
-                {
-                    int count = await _trcallrecordRepoository.Queryable()
-                        .Where(x => x.CreatedTime.Date == DateTime.Now.Date)
-                        .Where(x => x.QueueTims > 0 && x.Duration == 0)
-                        .CountAsync();
-                    await realtimeService.TodayWaitNumAsync(count, stoppingToken);
-                }
-                catch {}
-                await Task.Delay(times);
-            }
-        }
-    }
-}
+//        protected async override Task ExecuteAsync(CancellationToken stoppingToken)
+//        {
+//            //using var scope = _serviceScopeFactory.CreateScope();
+//            //var realtimeService = scope.ServiceProvider.GetRequiredService<IRealtimeService>();
+//            //var _trcallrecordRepoository = scope.ServiceProvider.GetRequiredService<IRepository<TrCallRecord>>();
+//            //int times = 60000;//一分钟
+//            //while (!stoppingToken.IsCancellationRequested)
+//            //{
+//            //    try
+//            //    {
+//            //        int count = await _trcallrecordRepoository.Queryable()
+//            //            .Where(x => x.CreatedTime.Date == DateTime.Now.Date)
+//            //            .Where(x => x.QueueTims > 0 && x.Duration == 0)
+//            //            .CountAsync();
+//            //        await realtimeService.TodayWaitNumAsync(count, stoppingToken);
+//            //    }
+//            //    catch {}
+//            //    await Task.Delay(times);
+//            //}
+//        }
+//    }
+//}

+ 407 - 0
src/Hotline.Application/Early/EarlyReportProvider.cs

@@ -0,0 +1,407 @@
+using Hotline.Application.ExportWord;
+using Hotline.Share.Dtos.Early;
+using Hotline.Share.Dtos.ExportWord;
+using XF.Domain.Dependency;
+
+namespace Hotline.Application.Early
+{
+    public class EarlyReportProvider : IEarlyReportProvder, IScopeDependency
+    {
+        public string PushReport(EarlyReportDto earlyReportDtoreportDto)
+        {
+            string summarize = CreateSummarize(earlyReportDtoreportDto);
+
+            var reportDto = new ReportDto
+            {
+                CssStyle = "padding-left: 10%;padding-right: 10%;padding-top: 10px;",
+                Title = new TitleDto
+                {
+                    Content = "宜宾市12345热线每日关注",
+                    CssStyle = "font-weight:bolder;color: red;font-size: 50px;font-family:宋体; width: 100 %; text-align: center; word-spacing: 10px;letter-spacing: 2px; margin-bottom: 10; line-height: 2; "
+                },
+                SubTitleDtos = new List<SubTitleDto>
+                {
+                    new SubTitleDto
+                    {
+                        CssStyle = "font-weight: bold; font-size: 24px;font-family:方正楷体_GBK;width: 100%;\r\n text-align: center;letter-spacing: 5px;line-height: 1.5;",
+                        Content = earlyReportDtoreportDto.ReportName
+                    },
+                    new SubTitleDto
+                    {
+                        CssStyle = "font-weight: bold; font-size: 24px;font-family:方正楷体_GBK;width: 100%;\r\n        text-align: center;letter-spacing: 5px;line-height: 1.5;",
+                        Content = "(截止"+ earlyReportDtoreportDto.ReportPushTime.ToString("MM月dd日hh时")+")"
+                    },
+                    new SubTitleDto
+                    {
+                        CssStyle = "font-weight: bold; font-size: 20px;font-family:楷体_GB2312;\r\n    width: 100%;letter-spacing: 5px;line-height: 1.5;\r\n    border-bottom:2px solid red;float: left;margin-bottom: 20px;",
+                        Children =  new List<SubTitleDto>
+                        {
+                             new SubTitleDto
+                             {
+                                 CssStyle = "float: left;",
+                                 Content = "市政务服务非公经济局"
+                             },
+                            new SubTitleDto
+                            {
+                                CssStyle="float: right;",
+                                Content = earlyReportDtoreportDto.ReportPushTime.ToString("yyyy年MM月dd日")
+                            }
+                        }
+                    }
+                },
+                ContentDto = new ContentDto
+                {
+                    CssStyle = "font-weight: normal; font-size: 18px;font-family:宋体;width: 100%;letter-spacing: 5px;line-height: 1.5;",
+                    TitleDto = new TitleDto
+                    {
+                        Content = "领导批示:",
+                        CssStyle = "font-weight: normal; font-size: 22px;font-family:黑体;width: 100%;letter-spacing: 5px;line-height: 1.5;"
+                    },
+                    ParagraphDtos = new List<ParagraphDto>
+                    {
+                        new ParagraphDto
+                        {
+                            CssStyle = "text-indent: 2rem;",
+                            Content = summarize
+                        }
+                    }
+                }
+            };
+            int index = FillParagraphes(earlyReportDtoreportDto, reportDto);
+
+            var olderOrderName = $"{earlyReportDtoreportDto.ReportPushTime.Month}月{earlyReportDtoreportDto.ReportPushTime.Day}日重点诉求处置情况";
+
+            reportDto.ContentDto.ParagraphDtos.Add(new ParagraphDto
+            {
+                Content = $"附件1:{olderOrderName}",
+                CssStyle = "text-indent: 2rem;",
+                SortIndex = ++index
+            });
+
+            var orderName = "前期重点问题办结情况";
+            reportDto.ContentDto.ParagraphDtos.Add(new ParagraphDto
+            {
+                Content = $"附件2:{orderName}",
+                CssStyle = "text-indent: 2rem;",
+                SortIndex = ++index
+            });
+
+            reportDto.AttachmentDtos = [];
+            AttachmentDto orderAttachmentDto = CreateOrderAttachment(earlyReportDtoreportDto, olderOrderName);
+            reportDto.AttachmentDtos.Add(orderAttachmentDto);
+            AttachmentDto fileOrderAttachmentDto = CreateFileOrderAttachment(earlyReportDtoreportDto, orderName);
+            reportDto.AttachmentDtos.Add(fileOrderAttachmentDto);
+
+            reportDto.BottomDtos = new List<BaseReportDto>
+            {
+                new BaseReportDto
+                {
+                    CssStyle = "font-weight: normal; font-size: 18px;font-family:楷体_GB2312;\r\n    width: 100%;letter-spacing: 2px;line-height: 1;\r\n    border-bottom:1px solid #000;float: left;margin-bottom: 20px;",
+                    Children = new List<BaseReportDto>
+                    {
+                        new BaseReportDto
+                        {
+                            CssStyle = "float: left;",
+                            Content = "信息公开属性:"
+                        },new BaseReportDto
+                        {
+                            CssStyle = "font-weight: bolder;float: left;",
+                            Content = "不予公开"
+                        }
+                    }
+                },
+                new BaseReportDto
+                {
+                    CssStyle = "font-weight: normal; font-size: 18px;font-family:楷体_GB2312;\r\n    width: 100%;letter-spacing: 2px;line-height: 1;\r\n    border-bottom:1px solid #000;float: left;margin-bottom: 20px;",
+                    Content = "主送:市长,常务副市长、分管副市长,联系副秘书长。"
+                },new BaseReportDto
+                {
+                    CssStyle="font-weight: normal; font-size: 18px;font-family:楷体_GB2312;\r\n    width: 100%;letter-spacing: 2px;line-height: 1;float: left;margin-bottom: 20px;",
+                    Children = new List<BaseReportDto>
+                    {
+                        new BaseReportDto
+                        {
+                            CssStyle = "float:left;",
+                            Content = "市政务服务非公经济发展局"
+                        },
+                        new BaseReportDto
+                        {
+                            CssStyle = "float:right;",
+                            Content = $"{earlyReportDtoreportDto.ReportPushTime.ToString()}印发"
+                        }
+                    }
+                }
+            };
+
+            return reportDto.RenderHtml();
+        }
+
+        private static AttachmentDto CreateOrderAttachment(EarlyReportDto earlyReportDtoreportDto, string olderOrderName)
+        {
+            TableDto orderTableDto = CreateOrderTable(earlyReportDtoreportDto);
+            var attachmentDto = new AttachmentDto
+            {
+                Title = new TitleDto
+                {
+                    CssStyle = "font-weight: bold; font-size: 18px;font-family:宋体;width: 100%; float:right;",
+                    Content = "附件1"
+                },
+                SubTitleDtos = new List<SubTitleDto>
+               {
+                   new SubTitleDto
+                   {
+                       CssStyle = "font-weight: bold; font-size: 24px;font-family:宋体;width: 100%; text-align: center;margin-bottom: 20px;",
+                       Content = olderOrderName
+                   }
+               },
+                TableDtos = new List<TableDto>
+                {
+                   orderTableDto
+                }
+            };
+            return attachmentDto;
+        }
+
+        private static AttachmentDto CreateFileOrderAttachment(EarlyReportDto earlyReportDtoreportDto, string olderOrderName)
+        {
+            TableDto orderTableDto = CreateFileOrderTable(earlyReportDtoreportDto);
+            var attachmentDto = new AttachmentDto
+            {
+                Title = new TitleDto
+                {
+                    CssStyle = "font-weight: bold; font-size: 18px;font-family:宋体;width: 100%; float:right;",
+                    Content = "附件2"
+                },
+                SubTitleDtos = new List<SubTitleDto>
+               {
+                   new SubTitleDto
+                   {
+                       CssStyle = "font-weight: bold; font-size: 24px;font-family:宋体;width: 100%; text-align: center;margin-bottom: 20px;",
+                       Content = olderOrderName
+                   }
+               },
+                TableDtos = new List<TableDto>
+                {
+                   orderTableDto
+                }
+            };
+            return attachmentDto;
+        }
+
+        private static TableDto CreateFileOrderTable(EarlyReportDto earlyReportDtoreportDto)
+        {
+            var tableDto = new TableDto
+            {
+                CssStyle = "border-collapse: collapse;width: 100%;"
+            };
+            tableDto.HeadDto.RowDtos.Add(new RowDto
+            {
+                ColumnDtos = new List<ColumnDto>
+                {
+                    new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;width: 10%;text-align: center;",
+                            Content = "序号",
+                        },
+                    new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;width: 10%;text-align: center;",
+                            Content = "期刊",
+                        },
+                        new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;width: 10%;text-align: center;",
+                            Content = "诉求来源",
+                        },
+                        new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;width: 20%;text-align: center;",
+                            Content = "问题类型",
+                        },
+                        new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;width: 25%;text-align: center;",
+                            Content = "诉求内容",
+                        },
+                        new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;width: 25%;text-align: center;",
+                            Content = "答复情况",
+                        }
+                }
+            });
+            var number = 1;
+            earlyReportDtoreportDto.ReportFiledOrderDetails.ForEach(item =>
+            {
+                tableDto.BodyDto.RowDtos.Add(new RowDto
+                {
+                    CssStyle = "border: solid 1px #000;",
+                    ColumnDtos = new List<ColumnDto>
+                    {
+                        new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;text-align: center;",
+                            Content = number.ToString(),
+                        },
+                        new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;text-align: center;",
+                            Content = item.ReportName,
+                        },
+                        new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;text-align: center;",
+                            Content = item.OneHotSportName,
+                        },
+                        new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;text-align: center;",
+                            Content = item.HotSportName,
+                        },
+                        new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;",
+                            Content = item.Content,
+                        },
+                        new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;",
+                            Content = item.ActualOpinion,
+                        }
+                    }
+                });
+                number++;
+            });
+            return tableDto;
+        }
+
+        private static TableDto CreateOrderTable(EarlyReportDto earlyReportDtoreportDto)
+        {
+            var tableDto = new TableDto
+            {
+                CssStyle = "border-collapse: collapse;width: 100%;"
+            };
+            tableDto.HeadDto.RowDtos.Add(new RowDto
+            {
+                ColumnDtos = new List<ColumnDto>
+                {
+                    new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;width: 10%;text-align: center;",
+                            Content = "序号",
+                        },
+                        new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;width: 20%;text-align: center;",
+                            Content = "诉求来源",
+                        },
+                        new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;width: 20%;text-align: center;",
+                            Content = "问题类型",
+                        },
+                        new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;width: 25%;text-align: center;",
+                            Content = "诉求内容",
+                        },
+                        new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;width: 25%;text-align: center;",
+                            Content = "办理情况",
+                        }
+                }
+            });
+            var number = 1;
+            earlyReportDtoreportDto.ReportOrderDetails.ForEach(item =>
+            {
+                tableDto.BodyDto.RowDtos.Add(new RowDto
+                {
+                    CssStyle = "border: solid 1px #000;",
+                    ColumnDtos = new List<ColumnDto>
+                    {
+                        new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;text-align: center;",
+                            Content = number.ToString(),
+                        },
+                        new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;text-align: center;",
+                            Content = item.OneHotSportName,
+                        },
+                        new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;text-align: center;",
+                            Content = item.HotSportName,
+                        },
+                        new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;",
+                            Content = item.Content,
+                        },
+                        new ColumnDto
+                        {
+                            CssStyle = "border: solid 1px #000;",
+                            Content = item.ActualOpinion,
+                        }
+                    }
+                });
+                number++;
+            });
+            return tableDto;
+        }
+
+        private static int FillParagraphes(EarlyReportDto earlyReportDtoreportDto, ReportDto reportDto)
+        {
+            var index = 1;
+            earlyReportDtoreportDto.ReportHotDetails.ForEach(item =>
+            {
+                reportDto.ContentDto.ParagraphDtos.Add(new ParagraphDto
+                {
+                    titleDto = new TitleDto
+                    {
+                        Content = $"{ToChineseNumber(index)}、{item.HotName}问题",
+                        CssStyle = "text-indent: 2rem;font-weight: bolder;",
+                    },
+                    Content = $"今日收到{item.HotName}问题{item.Count}件,主要涉及:{item.Content}",
+                    CssStyle = "text-indent: 2rem;",
+                    SortIndex = index
+                });
+                index++;
+            });
+            return index;
+        }
+
+        private static string CreateSummarize(EarlyReportDto earlyReportDtoreportDto)
+        {
+            var hotDetails = new List<string>();
+            earlyReportDtoreportDto.ReportHotDetails.ForEach(item =>
+            {
+                hotDetails.Add($"{item.HotName}{item.Count}件");
+            });
+            var areaDetails = new List<string>();
+            earlyReportDtoreportDto.ReportAreaDetails.ForEach(item =>
+            {
+                areaDetails.Add($"{item.AreaName}{item.Count}件");
+            });
+            var summarize = $"{earlyReportDtoreportDto.ReportPushTime.ToString("yyyy年MM月dd日 HH时mm分ss秒")},市12345热线共受理重点领域问题{earlyReportDtoreportDto.ReportHotDetails.Sum(x => x.Count)}件,其中,{string.Join("、", hotDetails)}。从区域分布来看,{string.Join(",", areaDetails)}。";
+            return summarize;
+        }
+
+        private static readonly string[] ChineseNumbers =
+             {
+        "零", "一", "二", "三", "四",
+        "五", "六", "七", "八", "九", "十"
+    };
+
+        public static string ToChineseNumber(int number)
+        {
+            if (number < 0 || number > 10)
+            {
+                throw new ArgumentOutOfRangeException(nameof(number), "输入数字必须在 0-10 范围内");
+            }
+            return ChineseNumbers[number];
+        }
+    }
+}

+ 14 - 0
src/Hotline.Application/Early/IEarlyReportProvder.cs

@@ -0,0 +1,14 @@
+using Hotline.Share.Dtos.Early;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Early
+{
+    public interface IEarlyReportProvder
+    {
+        public string PushReport(EarlyReportDto earlyReportDto);
+    }
+}

+ 9 - 0
src/Hotline.Application/Exam/Constants/ApiRoutes/ExamManageApiRoute.cs

@@ -0,0 +1,9 @@
+using Exam.Infrastructure.Web.Constants;
+
+namespace Hotline.Application.Exam.Constants.ApiRoutes
+{
+    public class ExamManageApiRoute:GenerateApiRoute
+    {
+        public const string ImportExcel = "ImportExcel";
+    }
+}

+ 9 - 0
src/Hotline.Application/Exam/Constants/ApiRoutes/ExamTagApiRoute.cs

@@ -0,0 +1,9 @@
+using Exam.Infrastructure.Web.Constants;
+
+namespace Hotline.Application.Exam.Constants.ApiRoutes
+{
+    public class ExamTagApiRoute:ApiRoute
+    {
+        public const string GetTreeList = "GetTreeList";
+    }
+}

+ 9 - 0
src/Hotline.Application/Exam/Constants/ApiRoutes/ExtractRuleApiRoute.cs

@@ -0,0 +1,9 @@
+using Exam.Infrastructure.Web.Constants;
+
+namespace Hotline.Application.Exam.Constants.ApiRoutes
+{
+    public class ExtractRuleApiRoute:ApiRoute
+    {
+        public const string GetTagQuestionCount = "GetTagQuestionCount";
+    }
+}

+ 9 - 0
src/Hotline.Application/Exam/Constants/ApiRoutes/GenerateApiRoute.cs

@@ -0,0 +1,9 @@
+using Exam.Infrastructure.Web.Constants;
+
+namespace Hotline.Application.Exam.Constants.ApiRoutes
+{
+    public class GenerateApiRoute:ApiRoute
+    {
+        public const string GenerateTestPaper = "GenerateTestPaper";
+    }
+}

+ 20 - 0
src/Hotline.Application/Exam/Constants/ApiRoutes/PracticeApiRoute.cs

@@ -0,0 +1,20 @@
+using Exam.Infrastructure.Web.Constants;
+using Microsoft.Identity.Client;
+
+namespace Hotline.Application.Exam.Constants.ApiRoutes
+{
+    public class PracticeApiRoute:ApiRoute
+    {
+        public const string Practice = "Practice";
+
+        public const string GetPracticeQuestionDto = "GetPracticeQuestionDto";
+
+        public const string GetPracticeQuestionViewResponses = "GetPracticeQuestionViewResponses";
+
+        public const string Complete = "Complete";
+
+        public const string GetViewPracticeQuestions = "GetViewPracticeQuestions";
+
+        public const string GetTagQuestionCount = "GetTagQuestionCount";
+    }
+}

+ 8 - 0
src/Hotline.Application/Exam/Constants/ApiRoutes/QuestionApiRoute.cs

@@ -0,0 +1,8 @@
+using Exam.Infrastructure.Web.Constants;
+
+namespace Hotline.Application.Exam.Constants.ApiRoutes
+{
+    public class QuestionApiRoute:ApiRoute
+    {
+    }
+}

+ 9 - 0
src/Hotline.Application/Exam/Constants/ApiRoutes/SourcewareApiRoute.cs

@@ -0,0 +1,9 @@
+using Exam.Infrastructure.Web.Constants;
+
+namespace Hotline.Application.Exam.Constants.ApiRoutes
+{
+    public class SourcewareApiRoute:ApiRoute
+    {
+        
+    }
+}

+ 9 - 0
src/Hotline.Application/Exam/Constants/ApiRoutes/SourcewareCategoryApiRoute.cs

@@ -0,0 +1,9 @@
+using Exam.Infrastructure.Web.Constants;
+
+namespace Hotline.Application.Exam.Constants.ApiRoutes
+{
+    public class SourcewareCategoryApiRoute:ApiRoute
+    {
+        public const string GetTreeList = "GetTreeList";
+    }
+}

+ 14 - 0
src/Hotline.Application/Exam/Constants/ApiRoutes/TestPaperApiRoute.cs

@@ -0,0 +1,14 @@
+using Exam.Infrastructure.Web.Constants;
+using Quartz.Impl.Triggers;
+
+namespace Hotline.Application.Exam.Constants.ApiRoutes
+{
+    public class TestPaperApiRoute:GenerateApiRoute
+    {
+        public const string Count = "GetTestPaperQuestionCount";
+
+        public const string GetQuestions = "GetQuestions";
+
+        public const string GetTagQuestionCount = "GetTagQuestionCount";
+    }
+}

+ 9 - 0
src/Hotline.Application/Exam/Constants/ApiRoutes/TrainPlanApiRoute.cs

@@ -0,0 +1,9 @@
+using Exam.Infrastructure.Web.Constants;
+
+namespace Hotline.Application.Exam.Constants.ApiRoutes
+{
+    public class TrainPlanApiRoute:ApiRoute
+    {
+        
+    }
+}

+ 22 - 0
src/Hotline.Application/Exam/Constants/ApiRoutes/TrainRecordApiRoute.cs

@@ -0,0 +1,22 @@
+using Exam.Infrastructure.Web.Constants;
+
+namespace Hotline.Application.Exam.Constants.ApiRoutes
+{
+    public class TrainRecordApiRoute:ApiRoute
+    {
+
+        public const string GetTrainQuestionDto = "GetTrainQuestionDto";
+
+        public const string Train = "Train";
+
+        public const string GetTrainPractice = "GetTrainPractice";
+
+        public const string CompleteTrainKnowladge = "CompleteTrainKnowladge";
+
+        public const string CompleteTrainRecord = "CompleteTrainRecord";
+
+        public const string AnalysisTrainResult = "AnalysisTrainResult";
+
+        public const string CalcuteAnalysisRate = "CalcuteAnalysisRate";
+    }
+}

+ 8 - 0
src/Hotline.Application/Exam/Constants/ApiRoutes/TrainTemplateApiRoute.cs

@@ -0,0 +1,8 @@
+using Exam.Infrastructure.Web.Constants;
+
+namespace Hotline.Application.Exam.Constants.ApiRoutes
+{
+    public class TrainTemplateApiRoute:ApiRoute
+    {
+    }
+}

+ 40 - 0
src/Hotline.Application/Exam/Constants/ApiRoutes/UserExamApiRoute.cs

@@ -0,0 +1,40 @@
+using Exam.Infrastructure.Web.Constants;
+using Org.BouncyCastle.Asn1.Mozilla;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Exam.Constants.ApiRoutes
+{
+
+    public class UserExamApiRoute:ApiRoute
+    {
+        public const string Start = "Start";
+
+        public const string GetExamQuestionGroup = "GetExamQuestionGroup";
+
+        public const string GetExamQuestion = "GetExamQuestion";
+
+        public const string Exam = "Exam";
+
+        public const string UpdateExam = "UpdateExam";
+
+        public const string Submit = "Submit";
+
+        public const string Grading = "Grading";
+
+        public const string CompleteGrading = "CompleteGrading";
+
+        public const string GetGradingQuestions = "GetGradingQuestions";
+
+        public const string GetGradingQuestionViewResponces = "GetGradingQuestionViewResponces";
+
+        public const string ViewGradingExamQuestion = "ViewGradingExamQuestion";
+
+        public const string GetUnExamUsers = "GetUnExamUsers";
+
+        public const string GetUserExamResults = "GetUserExamResults";
+    }
+}

+ 9 - 0
src/Hotline.Application/Exam/Constants/Messages/BusinessErrorMessage.cs

@@ -0,0 +1,9 @@
+using Exam.Infrastructure.Validation.Validation;
+
+namespace Hotline.Application.Exam.Constants.Messages
+{
+    public class BusinessErrorMessage: ErrorMessage
+    {
+        public const string HasChild = "{0}拥有下级,不可删除";
+    }
+}

+ 9 - 0
src/Hotline.Application/Exam/Constants/Messages/SystemConstants.cs

@@ -0,0 +1,9 @@
+namespace Hotline.Application.Exam.Constants.Messages
+{
+    public class SystemConstants
+    {
+        public static string[] Labels = ["A", "B", "C", "D", "E", "F", "G", "H","I","J"];
+
+        public static string[] ColumnNames = ["题型","试题标签","难度级别","正式可用","模拟可用", "题干", "选项A", "选项B", "选项C", "选项D", "选项E", "选项F", "选项G", "选项H", "选项I", "选项J", "答案"];
+    }
+}

+ 21 - 0
src/Hotline.Application/Exam/Core/Constants/ApiRoute.cs

@@ -0,0 +1,21 @@
+namespace Exam.Infrastructure.Web.Constants
+{
+    public class ApiRoute
+    {
+        public const string Add = "Add";
+
+        public const string Update = "Update";
+
+        public const string Delete = "Delete";
+
+        public const string GetPagedList = "GetPagedList";
+
+        public const string Get = "Get";
+
+        public const string GetList = "GetList";
+
+        public const string View = "View";
+
+        public const string UpdateStatus = "UpdateStatus";
+    }
+}

+ 17 - 0
src/Hotline.Application/Exam/Core/Constants/BusiConstants.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Exam.Core.Constants
+{
+    public  class BusiConstants
+    {
+        public const string TrainCode = "PX";
+
+        public const string TrainTemplateCode = "PXMB";
+
+        public const string PracticeCode = "LX";
+    }
+}

+ 19 - 0
src/Hotline.Application/Exam/Core/Extensions/ExpressionableExtensions.cs

@@ -0,0 +1,19 @@
+using Exam.Infrastructure.Data.Entity;
+using SqlSugar;
+using XF.Domain.Entities;
+
+namespace Exam.Infrastructure.Web.Extensions
+{
+    public static class ExpressionableExtensions
+    {
+        public static EntityQueryRequest ToEntityQueryRequest<T>(this Expressionable<T> expressionable) where T:class,IEntity<string>,new()
+        {
+            var entityQueryRequest = new EntityQueryRequest
+            {
+                Expression = expressionable.ToExpression()
+            };
+
+            return entityQueryRequest;
+        }
+    }
+}

+ 17 - 0
src/Hotline.Application/Exam/Core/Extensions/QuestionTypeExtensions.cs

@@ -0,0 +1,17 @@
+using Hotline.Share.Enums.Exams;
+
+namespace Hotline.Application.Exam.Core.Extensions
+{
+    public static class QuestionTypeExtensions
+    {
+        /// <summary>
+        /// 是否选择类型
+        /// </summary>
+        /// <param name="questionType"></param>
+        /// <returns></returns>
+        public static bool CheckSelectType(this EQuestionType questionType)
+        {
+            return questionType == EQuestionType.Single || questionType == EQuestionType.Multi || questionType == EQuestionType.Judge;
+        }
+    }
+}

+ 26 - 0
src/Hotline.Application/Exam/Core/Utilities/CodeUtility.cs

@@ -0,0 +1,26 @@
+using Exam.Infrastructure.Extensions;
+using Exam.Trains;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Exam.Core.Utilities
+{
+    public static class CodeUtility
+    {
+        public static string GenerateCode(string codePrefix, int length, string code)
+        {
+            var number = string.Empty;
+
+            number = code.IsNotNullOrEmpty() ? code.Substring(code.Length - length, length) : "0";
+
+            var value = int.Parse(number) + 1;
+
+            code = $"{codePrefix}{DateTime.Now.ToString("yyyyMMdd")}{value.ToString().PadLeft(length, '0')}";
+
+            return code;
+        }
+    }
+}

+ 15 - 0
src/Hotline.Application/Exam/Core/Utilities/ExpressionableUtility.cs

@@ -0,0 +1,15 @@
+using SqlSugar;
+using XF.Domain.Entities;
+
+namespace Exam.Infrastructure.Web.Utilities
+{
+    public static class ExpressionableUtility
+    {
+        public static Expressionable<T> CreateExpression<T>() where T :class,IEntity<string>,new()
+        {
+            Expressionable<T> expressionable = new Expressionable<T>();
+
+            return expressionable;
+        }
+    }
+}

+ 26 - 0
src/Hotline.Application/Exam/Core/Utilities/TypeAdapterConfigUtility.cs

@@ -0,0 +1,26 @@
+using Exam.Infrastructure.Data.Entity;
+using Mapster;
+using XF.Domain.Repository;
+
+namespace Exam.Infrastructure.Web.Utilities
+{
+    public class TypeAdapterConfigUtility
+    {
+        public static TypeAdapterConfig ForUpdate<TActionRequest, TEntity>()
+            where TActionRequest : ActionRequest
+            where TEntity : FullStateEntity
+        {
+            TypeAdapterConfig.GlobalSettings.ForType<TActionRequest, TEntity>()
+    .Ignore(x => x.CreationTime)
+                .Ignore(x => x.CreatorId)
+                .Ignore(x => x.CreatorName)
+                .Ignore(x => x.CreatorOrgId)
+                .Ignore(x => x.CreatorOrgLevel)
+                .Ignore(x => x.CreatorOrgName)
+                .Ignore(x => x.AreaId)
+                .Ignore(x => x.Id);
+
+            return TypeAdapterConfig.GlobalSettings;
+        }
+    }
+}

+ 26 - 0
src/Hotline.Application/Exam/Extensions/CollectionExtensions.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Exam.Extensions
+{
+    public static class CollectionExtensions
+    {
+        public static List<T> AddRangeExt<T>(this List<T> collection, List<T> extand)
+        {
+            if (extand != null)
+                collection.AddRange(extand);
+
+            return collection;
+        }
+
+        public static bool IsNotNullOrEmpty<T>(this List<T> collection)
+        {
+            if (collection == null) return false;
+
+            return collection.Any();
+        }
+    }
+}

+ 44 - 0
src/Hotline.Application/Exam/Extensions/OperationStatusExtensions.cs

@@ -0,0 +1,44 @@
+using Exam.Infrastructure.Data.Interface;
+using Exam.Infrastructure.Enums;
+using Hotline.Share.Exams.Interface;
+using XF.Domain.Entities;
+
+namespace Hotline.Application.Exam.Extensions
+{
+    public static class OperationStatusExtensions
+    {
+        public static void ResolveOperationStatus<T>(this List<T> actionRequests)
+             where T : IOperationStatus, IAddRequest, new()
+        {
+            actionRequests.Where(x=>x.OperationStatus == EEOperationStatus.Default).ToList().ForEach(actionRequest =>
+            {
+                actionRequest.OperationStatus = EEOperationStatus.Add;
+            });
+        }
+
+        public static void ResolveOperationStatus<T,TEntity>(this List<T> actionRequests,List<TEntity> currents) 
+            where T:IOperationStatus,IActionRequest,new()            
+            where TEntity:IEntity<string>
+        {
+            actionRequests.Where(x => x.OperationStatus == EEOperationStatus.Default).ToList().ForEach(actionRequest =>
+            {
+                if (currents.Any(x => x.Id == (actionRequest).Id))
+                {
+                    actionRequest.OperationStatus = EEOperationStatus.Update;
+                }
+            });
+
+            currents.ForEach(item =>
+            {
+                if(!actionRequests.Any(x=> x.Id == item.Id))
+                {
+                    actionRequests.Add(new T()
+                    {
+                        Id = item.Id,
+                        OperationStatus = EEOperationStatus.Delete
+                    });
+                }
+            });
+        }
+    }
+}

+ 25 - 0
src/Hotline.Application/Exam/Extensions/RepositoryExtensions.cs

@@ -0,0 +1,25 @@
+using Exam.Infrastructure.Data.Entity;
+using Hotline.Exams.Base;
+using System.Linq.Expressions;
+using XF.Domain.Entities;
+using XF.Domain.Repository;
+
+namespace Hotline.Application.Exam.Extensions
+{
+    public static class RepositoryExtensions
+    {
+        public static bool CheckHasChild<TEntity>(this IRepository<TEntity> repository, EntityQueryRequest entityQueryRequest) where TEntity : class, IEntity<string>, IParentEntity, new()
+        {
+            if (entityQueryRequest.Id != null)
+            {
+                return repository.Queryable().Any(x=>x.ParentId == entityQueryRequest.Id);
+            }
+            else if(entityQueryRequest.Ids!=null && entityQueryRequest.Ids.Any())
+            {
+                return repository.Queryable().Any(x => entityQueryRequest.Ids.Contains(x.ParentId));
+            }
+
+            return false;
+        }
+    }
+}

+ 15 - 0
src/Hotline.Application/Exam/Extensions/StartExamViewResponseExtensions.cs

@@ -0,0 +1,15 @@
+using Hotline.Share.ViewResponses.Exam;
+
+namespace Hotline.Application.Exam.Extensions
+{
+    public static class StartExamViewResponseExtensions
+    {
+        public static bool CheckValidate(this StartExamViewResponse startExamViewResponse)
+        {
+            if (startExamViewResponse.IsCompleted || !startExamViewResponse.IsJoin || !startExamViewResponse.IsStart)
+                return false;
+
+            return true;
+        }
+    }
+}

+ 22 - 0
src/Hotline.Application/Exam/Interface/ExamManages/IExamManageService.cs

@@ -0,0 +1,22 @@
+using Exam.ExamManages;
+
+using Exam.Share;
+using Exam.Share.ViewResponses.Exam;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Requests.Exam;
+using Microsoft.AspNetCore.Http;
+
+namespace Hotline.Application.Exam.Interface.ExamManages
+{
+    public interface IExamManageService:IQueryService<ExamManageViewResponse,ExamManageDto,ExamManagePagedRequest>,IApiService<AddExamManageDto,UpdateExamManageDto,ExamManage>
+    {
+        /// <summary>
+        /// 组卷
+        /// </summary>
+        /// <param name="generateExamTestPaperRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        public Task GenerateTestPaper(GenerateExamTestPaperRequest generateExamTestPaperRequest, CancellationToken cancellationToken);
+
+    }
+}

+ 15 - 0
src/Hotline.Application/Exam/Interface/ExamManages/IExamTagService.cs

@@ -0,0 +1,15 @@
+using Exam.ExamManages;
+using Exam.Share.ViewResponses.Sourceware;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Dtos.ExamManages;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.Requests.Sourceware;
+using Hotline.Share.ViewResponses.Exam;
+
+namespace Exam.Application.Interface.Exam
+{
+    public interface IExamTagService:IQueryService<ExamTagViewResponse,ExamTagDto,ExamTagRequest>,IApiService<AddExamTagDto,UpdateExamTagDto,ExamTag>
+    {
+        public Task<List<ExamTagViewResponse>> GetTreeAsync(ExamTagRequest examTagRequest);
+    }
+}

+ 15 - 0
src/Hotline.Application/Exam/Interface/ExamManages/IExtractRuleService.cs

@@ -0,0 +1,15 @@
+using Exam.ExamManages;
+
+using Exam.Share.ViewResponses.Exam;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Dtos.TestPapers;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.ViewResponses.Exam;
+
+namespace Exam.Application.Interface.Exam
+{
+    public interface IExtractRuleService:IQueryService<ExtractRuleViewResponse,ExtractRuleDto,ExtractRulePagedRequest>,IApiService<AddExtractRuleDto,UpdateExtractRuleDto,ExtractRule>
+    {
+        public Task<List<TagQuestionViewResponse>> GetTagQuestionCount(TagQuestionRequest tagQuestionRequest);
+    }
+}

+ 12 - 0
src/Hotline.Application/Exam/Interface/ExamManages/IUnExamUserService.cs

@@ -0,0 +1,12 @@
+
+using Exam.Share;
+using Exam.Share.ViewResponses.Exam;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Requests.Exam;
+
+namespace Exam.Application.Interface.Exam
+{
+    public interface IUnExamUserService:IQueryService<UnExamUserViewResponse,UserExamDto,UnExamUserReportPagedRequest>
+    {
+    }
+}

+ 12 - 0
src/Hotline.Application/Exam/Interface/ExamManages/IUserExamResultService.cs

@@ -0,0 +1,12 @@
+
+using Exam.Share;
+using Exam.Share.ViewResponses.Exam;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Requests.Exam;
+
+namespace Exam.Application.Interface.Exam
+{
+    public interface IUserExamResultService:IQueryService<UserExamResultViewResponse,UserExamDto,UserExamResultReportPagedRequest>
+    {
+    }
+}

+ 104 - 0
src/Hotline.Application/Exam/Interface/ExamManages/IUserExamService.cs

@@ -0,0 +1,104 @@
+using Exam.ExamManages;
+
+using Exam.Share;
+using Exam.Share.Dtos.ExamManage;
+using Exam.Share.ViewResponses.Exam;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Dtos.ExamManages;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.ViewResponses.Exam;
+
+namespace Exam.Application.Interface.Exam
+{
+    public interface IUserExamService:IQueryService<UserExamResultViewResponse,UserExamDto,UserExamPagedRequest>,IApiService<AddUserExamDto,UpdateUserExamDto,UserExam>
+    {
+        /// <summary>
+        /// 交卷
+        /// </summary>
+        /// <param name="submitExamDto"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        Task SubmitAsync(SubmitExamDto submitExamDto,CancellationToken cancellationToken);
+
+        
+        /// <summary>
+        /// 阅卷
+        /// </summary>
+        /// <param name="gradingExtamItemDto"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        Task<GradingExamQuestionDto> GradingAsync(GradingExtamItemDto gradingExtamItemDto, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 获取考试试题类型和Id
+        /// </summary>
+        /// <param name="examQuestionRequest"></param>
+        /// <returns></returns>
+        Task<List<ExamQuestionViewResponse>> GetExamQuestionViewResponses(ExamQuestionGroupRequest examQuestionGroupRequest);
+
+        /// <summary>
+        /// 获取已阅卷的考试试题类型和Id
+        /// </summary>
+        /// <param name="examQuestionGroupRequest"></param>
+        /// <returns></returns>
+        Task<List<GradingQuestionViewResponce>> GetGradingQuestionViewResponces(ExamQuestionGroupRequest examQuestionGroupRequest);
+
+        /// <summary>
+        /// 获取考试试题
+        /// </summary>
+        /// <param name="examQuestionRequest"></param>
+        /// <returns></returns>
+        Task<ExamQuestionDto> GetExamQuestionDto(ExamQuestionRequest examQuestionRequest);
+
+        /// <summary>
+        /// 初次考试
+        /// </summary>
+        /// <param name="addUserExamItemDto"></param>
+        /// <returns></returns>
+        Task<StartExamViewResponse> ExamAsync(UpdateUserExamItemDto addUserExamItemDto, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 开始考试
+        /// </summary>
+        /// <param name="startUserExamDto"></param>
+        /// <param name="CancellationToken"></param>
+        /// <returns></returns>
+        Task<StartExamViewResponse> StartUserExamAsync(StartUserExamDto startUserExamDto,CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 完成阅卷
+        /// </summary>
+        /// <param name="gradingExtamDto"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        Task CompleteGradingAsync(GradingExamDto gradingExtamDto,CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 获取阅卷试题
+        /// </summary>
+        /// <param name="gradingExamRequest"></param>
+        /// <returns></returns>
+        Task<List<GradingExamQuestionDto>> GetGradingExamQuestion(GradingExamRequest gradingExamRequest);
+
+        /// <summary>
+        /// 查看已阅卷考试试题
+        /// </summary>
+        /// <param name="viewGradingExamRequest"></param>
+        /// <returns></returns>
+        Task<GradingExamQuestionDto> ViewGradingExamQuestion(ViewGradingExamRequest viewGradingExamRequest);
+
+        /// <summary>
+        /// 获取缺考考生名单
+        /// </summary>
+        /// <param name="unExamUserReportPagedRequest"></param>
+        /// <returns></returns>
+        Task<UnExamUserPageViewResponse> GetUnExamUsers(UnExamUserReportPagedRequest unExamUserReportPagedRequest);
+
+        /// <summary>
+        /// 获取用户考试成绩
+        /// </summary>
+        /// <param name="userExamResultReportPagedRequest"></param>
+        /// <returns></returns>
+        Task<UserExamResultPageViewResponse> GetUserExamResults(UserExamResultReportPagedRequest userExamResultReportPagedRequest);
+    }
+}

+ 68 - 0
src/Hotline.Application/Exam/Interface/Practices/IPracticeService.cs

@@ -0,0 +1,68 @@
+
+using Exam.Practices;
+using Exam.Share;
+using Exam.Share.ViewResponses.Practices;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Dtos.Practices;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.Requests.TestPaper;
+using Hotline.Share.ViewResponses.Exam;
+using Hotline.Share.ViewResponses.Practices;
+
+namespace Exam.Application.Interface.Practices
+{
+    public interface IPracticeService:IQueryService<PracticeViewResponse, PracticeDto,PracticePagedRequest>,IApiService<AddPracticeDto,UpdatePracticeDto,Practice>
+    {
+        /// <summary>
+        /// 获取自我练习试题类型和Id
+        /// </summary>
+        /// <param name="examQuestionRequest"></param>
+        /// <returns></returns>
+        Task<List<PracticeQuestionViewResponse>> GetPracticeQuestionViewResponses(PracticeQuestionGroupRequest practiceQuestionGroupRequest);
+
+        /// <summary>
+        /// 查看自我练习试题类型和Id
+        /// </summary>
+        /// <param name="practiceQuestionGroupRequest"></param>
+        /// <returns></returns>
+        Task<List<PracticeQuestionViewResponse>> GetViewPracticeQuestions(PracticeQuestionGroupRequest practiceQuestionGroupRequest);
+
+        /// <summary>
+        /// 获取练习试题
+        /// </summary>
+        /// <param name="practiceQuestionRequest"></param>
+        /// <returns></returns>
+        Task<PracticeQuestionDto> GetPracticeQuestion(PracticeQuestionRequest practiceQuestionRequest);
+
+        /// <summary>
+        /// 查看练习习题
+        /// </summary>
+        /// <param name="practiceQuestionRequest"></param>
+        /// <returns></returns>
+        Task<ViewPracticeQuestionDto> ViewPracticeQuestion(PracticeQuestionRequest practiceQuestionRequest);
+
+        /// <summary>
+        /// 练习
+        /// </summary>
+        /// <param name="practiceAnswerDto"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        Task<PracticeQuestionDto> Practice(SubmitPracticeDto submitPracticeDto,CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 完成培训记录
+        /// </summary>
+        /// <param name="completePracticeRecordDto"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        Task Complete(CompletePracticeRecordDto completePracticeRecordDto, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 根据标签获取试题数
+        /// </summary>
+        /// <param name="tagQuestionCountForPracticeRequest"></param>
+        /// <returns></returns>
+        Task<TagQuestionCountViewResponse> GetTagQuestionCount(TagQuestionCountForPracticeRequest tagQuestionCountForPracticeRequest);
+
+    }
+}

+ 21 - 0
src/Hotline.Application/Exam/Interface/Questions/IQuestionAnswerService.cs

@@ -0,0 +1,21 @@
+
+
+using Exam.Questions;
+using Exam.Share;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Dtos.Questions;
+using Hotline.Share.Requests.Question;
+using System.ComponentModel;
+
+namespace Exam.Application
+{
+    /// <summary>
+    /// 试题参考答案服务接口
+    /// </summary>
+    [Description("试题参考答案服务接口")]
+    public interface IQuestionAnswerService:IQueryService<QuestionAnswerViewResponse,QuestionAnswerDto,QuestionAnswerPagedRequest>,IApiService<AddQuestionAnswerDto,UpdateQuestionAnswerDto,QuestionAnswer>
+    {
+    }
+}
+
+

+ 21 - 0
src/Hotline.Application/Exam/Interface/Questions/IQuestionKnowladgeService.cs

@@ -0,0 +1,21 @@
+
+
+using Exam.Questions;
+using Exam.Share;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Dtos.Questions;
+using Hotline.Share.Requests.Question;
+using System.ComponentModel;
+
+namespace Exam.Application
+{
+    /// <summary>
+    /// 关联知识服务接口
+    /// </summary>
+    [Description("关联知识服务接口")]
+    public interface IQuestionKnowladgeService:IQueryService<QuestionKnowladgeViewResponse,PracticeQuestionKnowladgeDto,QuestionKnowladgePagedRequest>,IApiService<AddQuestionKnowladgeDto,UpdateQuestionKnowladgeDto,TestPaperItemKnowladge>
+    {
+    }
+}
+
+

+ 21 - 0
src/Hotline.Application/Exam/Interface/Questions/IQuestionOptionsService.cs

@@ -0,0 +1,21 @@
+
+
+using Exam.Questions;
+using Exam.Share;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Dtos.Questions;
+using Hotline.Share.Requests.Question;
+using System.ComponentModel;
+
+namespace Exam.Application
+{
+    /// <summary>
+    /// 试题选项服务接口
+    /// </summary>
+    [Description("试题选项服务接口")]
+    public interface IQuestionOptionsService:IQueryService<QuestionOptionsViewResponse,QuestionOptionsDto,QuestionOptionsPagedRequest>,IApiService<AddQuestionOptionsDto,UpdateQuestionOptionsDto,QuestionOptions>
+    {
+    }
+}
+
+

+ 21 - 0
src/Hotline.Application/Exam/Interface/Questions/IQuestionService.cs

@@ -0,0 +1,21 @@
+
+using Exam.Questions;
+using Exam.Share.ViewResponses.Question;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Dtos.Questions;
+using Hotline.Share.Requests.Question;
+using Microsoft.AspNetCore.Http;
+
+namespace Exam.Application.Interface.Questions
+{
+    public interface IQuestionService:IQueryService<QuestionViewResponse,QuestionDto,QuestionPagedRequest>,IApiService<AddQuestionDto, UpdateQuestionDto, Question>
+    {
+
+        /// <summary>
+        /// 导入Excel
+        /// </summary>
+        /// <param name="files"></param>
+        /// <returns></returns>
+        public Task ImportExcel(IFormFile files, CancellationToken cancellationToken);
+    }
+}

+ 21 - 0
src/Hotline.Application/Exam/Interface/Questions/IQuestionSourcewareService.cs

@@ -0,0 +1,21 @@
+
+
+using Exam.Questions;
+using Exam.Share;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Dtos.Questions;
+using Hotline.Share.Requests.Question;
+using System.ComponentModel;
+
+namespace Exam.Application
+{
+    /// <summary>
+    /// 关联课件服务接口
+    /// </summary>
+    [Description("关联课件服务接口")]
+    public interface IQuestionSourcewareService:IQueryService<QuestionSourcewareViewResponse,PracticeQuestionSourcewareDto,QuestionSourcewarePagedRequest>,IApiService<AddQuestionSourcewareDto,UpdateQuestionSourcewareDto,PracticeQuestionSourceware>
+    {
+    }
+}
+
+

+ 21 - 0
src/Hotline.Application/Exam/Interface/Questions/IQuestionTagService.cs

@@ -0,0 +1,21 @@
+
+
+using Exam.Questions;
+using Exam.Share;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Dtos.Questions;
+using Hotline.Share.Requests.Question;
+using System.ComponentModel;
+
+namespace Exam.Application
+{
+    /// <summary>
+    /// 试题标签服务接口
+    /// </summary>
+    [Description("试题标签服务接口")]
+    public interface IQuestionTagService:IQueryService<QuestionTagViewResponse,QuestionTagDto,QuestionTagPagedRequest>,IApiService<AddQuestionTagDto,UpdateQuestionTagDto,QuestionTag>
+    {
+    }
+}
+
+

+ 20 - 0
src/Hotline.Application/Exam/Interface/Sourcewares/ISourcewareCategoryService.cs

@@ -0,0 +1,20 @@
+using Exam.Share.ViewResponses.Sourceware;
+using Hotline.Exams.Sourcewares;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Dtos.Sourcewares;
+using Hotline.Share.Requests.Sourceware;
+using System.ComponentModel;
+
+namespace Exam.Application
+{
+    /// <summary>
+    /// 课件类型服务接口
+    /// </summary>
+    [Description("课件类型服务接口")]
+    public interface ISourcewareCategoryService:IQueryService<SourcewareCategoryViewResponse,SourcewareCategoryDto,SourcewareCategoryRequest>,IApiService<AddSourcewareCategoryDto,UpdateSourcewareCategoryDto,SourcewareCategory>
+    {
+        public Task<List<SourcewareCategoryViewResponse>> GetTreeAsync(SourcewareCategoryRequest sourcewareCategoryRequest);
+    }
+}
+
+

+ 19 - 0
src/Hotline.Application/Exam/Interface/Sourcewares/ISourcewareService.cs

@@ -0,0 +1,19 @@
+using Hotline.Exams.Sourcewares;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Dtos.Sourcewares;
+using Hotline.Share.Requests.Sourceware;
+using Hotline.Share.ViewResponses.Sourcewares;
+using System.ComponentModel;
+
+namespace Exam.Application.Interface.Sourcewares
+{
+    /// <summary>
+    /// 课件服务接口
+    /// </summary>
+    [Description("课件服务接口")]
+    public interface ISourcewareService:IQueryService<SourcewareViewResponse,SourcewareDto,SourcewarePagedRequest>,IApiService<AddSourcewareDto, UpdateSourcewareDto, Sourceware>
+    {
+    }
+}
+
+

+ 21 - 0
src/Hotline.Application/Exam/Interface/Strategy/IExamStrategy.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Exam.Interface.Strategy
+{
+    public interface IExamStrategy
+    {
+
+        public bool Validate();
+
+        public void SetNext(IExamStrategy examStrategy);
+
+
+        public string ErroMessage { get; }
+
+        public Action CallBack { get; set; }
+    }
+}

+ 35 - 0
src/Hotline.Application/Exam/Interface/TestPapers/ITestPaperService.cs

@@ -0,0 +1,35 @@
+
+using Exam.Share.ViewResponses.Question;
+using Exam.Share.ViewResponses.TestPaper;
+using Exam.TestPapers;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Dtos.Questions;
+using Hotline.Share.Dtos.TestPapers;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.Requests.TestPaper;
+using Hotline.Share.ViewResponses.Exam;
+using Hotline.Share.ViewResponses.Questions;
+
+namespace Exam.Application.Interface.TestPapers
+{
+    public interface ITestPaperService:IQueryService<TestPaperViewResponse,TestPaperDto,TestPaperPagedRequest>,IApiService<AddTestPaperDto,UpdateTestPaperDto,TestPaper>
+    {
+        /// <summary>
+        /// 获取试卷试题数量
+        /// </summary>
+        /// <param name="testPaperQuestionCountRequest"></param>
+        /// <returns></returns>
+        public Task<List<TestPaperQuestionCountViewResponse>> GetTestPaperQuestionCount(TestPaperQuestionCountRequest testPaperQuestionCountRequest);
+        
+        public Task GenerateTestPaper(GenerateTestPaperRequest generateTestPaperRequest,CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 根据标签获取试题
+        /// </summary>
+        /// <param name="testPaperQuestionRequest"></param>
+        /// <returns></returns>
+        public Task<List<SimpleQuestionViewResponse>> GetQuestionDtos(TestPaperQuestionRequest testPaperQuestionRequest);
+
+        public Task<TagQuestionCountViewResponse> GetTagQuestionCount(TagQuestionCountRequest tagQuestionCountRequest);
+    }
+}

+ 13 - 0
src/Hotline.Application/Exam/Interface/Train/ITrainPlanService.cs

@@ -0,0 +1,13 @@
+
+using Exam.Share;
+using Exam.Share.ViewResponses.Train;
+using Exam.Trains;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Requests.Train;
+
+namespace Exam.Application.Interface.Train
+{
+    public interface ITrainPlanService:IQueryService<TrainPlanViewResponse,TrainPlanDto,TrainPlanPagedRequest>,IApiService<AddTrainPlanDto,UpdateTrainPlanDto,TrainPlan>
+    {
+    }
+}

+ 11 - 0
src/Hotline.Application/Exam/Interface/Train/ITrainRecordAnswerService.cs

@@ -0,0 +1,11 @@
+
+using Exam.Trains;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Dtos.Trains;
+
+namespace Exam.Application.Interface.Train
+{
+    public interface ITrainRecordAnswerService:IApiService<AddTrainRecordAnswerDto,UpdateTrainRecordAnswerDto,TrainRecordAnswer>
+    {
+    }
+}

+ 57 - 0
src/Hotline.Application/Exam/Interface/Train/ITrainRecordService.cs

@@ -0,0 +1,57 @@
+using Exam.Share.Dtos.Trains;
+using Exam.Share.ViewResponses.Train;
+using Exam.Trains;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Dtos.Trains;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.Requests.Train;
+using Hotline.Share.ViewResponses.Trains;
+
+namespace Train.Application.Interface.Train
+{
+    public interface ITrainRecordService:IQueryService<TrainRecordViewResponse,TrainRecordDto,TrainRecordPagedRequest>,IApiService<AddTrainRecordDto,UpdateTrainRecordDto,TrainRecord>
+    {
+        /// <summary>
+        /// 结束培训
+        /// </summary>
+        /// <param name="completeTrainRecordDto"></param>
+        /// <returns></returns>
+        Task CompleteTrainRecordAsync(CompleteTrainRecordDto completeTrainRecordDto,CancellationToken cancellationToken);
+
+
+        /// <summary>
+        /// 初次培训
+        /// </summary>
+        /// <param name="addUserTrainItemDto"></param>
+        /// <returns></returns>
+        Task<TrainPracticeDto> TrainAsync(AddTrainDto addTrainDto, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 获取培训习题
+        /// </summary>
+        /// <param name="trainPracticeRequest"></param>
+        /// <returns></returns>
+        Task<TrainPracticeDto> GetTrainPracticeAsync(TrainPracticeRequest trainPracticeRequest);
+
+        /// <summary>
+        /// 完成培训课件学习
+        /// </summary>
+        /// <param name="completeTrainPracticeDto"></param>
+        /// <returns></returns>
+        Task CompleteTrainKnowladgeAsync(CompleteTrainKnowladgeDto completeTrainPracticeDto, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 统计培训结果
+        /// </summary>
+        /// <param name="trainResultReportPagedRequest"></param>
+        /// <returns></returns>
+        Task<TrainResultPagedViewResponse> AnalysisTrainResult(TrainResultReportPagedRequest trainResultReportPagedRequest);
+
+        /// <summary>
+        /// 计算培训结果比例
+        /// </summary>
+        /// <param name="trainResultReportPagedRequest"></param>
+        /// <returns></returns>
+        Task<TrainResultRateViewResponse> CalcuteAnalysisRate(TrainResultReportPagedRequest trainResultReportPagedRequest);
+    }
+}

+ 13 - 0
src/Hotline.Application/Exam/Interface/Train/ITrainTemplateService.cs

@@ -0,0 +1,13 @@
+
+using Exam.Share.ViewResponses.Train;
+using Exam.Trains;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Dtos.Trains;
+using Hotline.Share.Requests.Train;
+
+namespace Exam.Application.Interface.Train
+{
+    public interface ITrainTemplateService:IQueryService<TrainTemplateViewResponse,TrainTemplateDto,TrainTemplatePagedRequest>,IApiService<AddTrainTemplateDto,UpdateTrainTemplateDto,TrainTemplate>
+    {
+    }
+}

+ 21 - 0
src/Hotline.Application/Exam/Mappers/MapperConfigs.cs

@@ -0,0 +1,21 @@
+using Exam.Infrastructure.Data.Interface;
+using Exam.Infrastructure.Web.Utilities;
+using Exam.Questions;
+using Hotline.Share.Dtos.Questions;
+using Hotline.Share.Notifications.NewRockCallCenter.Base;
+using Mapster;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using XF.Domain.Entities;
+using XF.Domain.Repository;
+
+namespace Exam.Application.Mappers
+{
+    internal class MapperConfigs : IRegister
+    {
+        public void Register(TypeAdapterConfig config)
+        {
+            config.ForType<QuestionDto, Question>().IgnoreNullValues(true);
+        }
+    }
+}

+ 236 - 0
src/Hotline.Application/Exam/Proxy/ExamManageProxy.cs

@@ -0,0 +1,236 @@
+using Exam.ExamManages;
+using Exam.Questions;
+using Exam.Repository.Sqlsugar.Repositories;
+using Hotline.Exams.ExamManages;
+using Hotline.Repository.SqlSugar;
+using Hotline.Repository.SqlSugar.DataPermissions;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.ExamManages;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.Exams;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.Questions;
+using Hotline.Repository.SqlSugar.Extensions;
+using Hotline.Repository.SqlSugar.Interface;
+using MapsterMapper;
+using SqlSugar;
+using XF.Domain.Authentications;
+
+namespace Hotline.Application.Exam.Proxy
+{
+    public class ExamManageProxy
+    {
+        private readonly IExamManageRepository _repository;
+        private readonly IExamQuestionRepository _examQuestionRepository;
+        private readonly IExamQuestionAnswerRepository _examQuestionAnswerRepository;
+        private readonly IExamQuestionOptionsRepository _examQuestionOptionsRepository;
+        private readonly IExamQuestionSourcewareRepository _examQuestionSourcewareRepository;
+        private readonly IExamQuestionKnowladgeRepository _examQuestionKnowladgeRepository;
+        private readonly IDataPermissionFilterBuilder _dataPermissionFilterBuilder;
+        private readonly IServiceProvider _serviceProvider;
+        private readonly IMapper _mapper;
+        private readonly ISessionContext _sessionContext;
+		private readonly ISugarUnitOfWork<HotlineDbContext> _uow;
+
+		public ExamManageProxy(IExamManageRepository repository,
+            IExamQuestionRepository examQuestionRepository,
+            IExamQuestionAnswerRepository examQuestionAnswerRepository,
+            IExamQuestionOptionsRepository examQuestionOptionsRepository,
+            IExamQuestionSourcewareRepository examQuestionSourcewareRepository,
+            IExamQuestionKnowladgeRepository examQuestionKnowladgeRepository,
+            IDataPermissionFilterBuilder dataPermissionFilterBuilder,
+            IServiceProvider serviceProvider,
+            IMapper mapper, ISessionContext sessionContext)
+        {
+            this._repository = repository;
+            this._examQuestionRepository = examQuestionRepository;
+            this._examQuestionAnswerRepository = examQuestionAnswerRepository;
+            this._examQuestionOptionsRepository = examQuestionOptionsRepository;
+            this._examQuestionSourcewareRepository = examQuestionSourcewareRepository;
+            this._examQuestionKnowladgeRepository = examQuestionKnowladgeRepository;
+            this._dataPermissionFilterBuilder = dataPermissionFilterBuilder;
+            this._serviceProvider = serviceProvider;
+            this._mapper = mapper;
+            this._sessionContext = sessionContext;
+            this._uow = repository.UOW;
+        }
+
+        #region public method
+        public async Task GenerateQuestion(List<Question> questions, string testPaperId, CancellationToken cancellationToken)
+        {
+            List<ExamQuestion> examQuestions = await SyncQuestion(questions, testPaperId, cancellationToken);
+
+            var examQuestionOptionses = await SyncQuestionOptions(examQuestions, cancellationToken);
+            var examQuestionSourcewares = await SyncQuestionSourcewares(examQuestions, cancellationToken);
+            var examQuestionKnowladges = await SyncQuestionKnowladge(examQuestions, cancellationToken);
+            var examQuestionAnswers = await SyncQuestionAnswer(examQuestions, cancellationToken);
+
+            examQuestions.ForEach(item =>
+            {
+                item.ExamQuestionOptionses = examQuestionOptionses.Where(x => item.QuestionId == x.QuestionId).ToList();
+                item.ExamQuestionOptionses.ForEach(x =>
+                {
+                    x.ExamQuestionId = item.Id;
+                });
+                item.ExamQuestionSourcewares = examQuestionSourcewares.Where(x => item.QuestionId == x.QuestionId).ToList();
+                item.ExamQuestionSourcewares.ForEach(x =>
+                {
+                    x.ExamQuestionId = item.Id;
+                });
+                item.ExamQuestionKnowladges = examQuestionKnowladges.Where(x => item.QuestionId == x.QuestionId).ToList();
+                item.ExamQuestionKnowladges.ForEach(x =>
+                {
+                    x.ExamQuestionId = item.Id;
+                });
+                
+                item.ExamQuestionAnswers = examQuestionAnswers.Where(x => item.QuestionId == x.QuestionId).ToList();
+                item.ExamQuestionAnswers.ForEach(x =>
+                {
+                    x.ExamQuestionId = item.Id;
+                });
+            });
+
+            await _examQuestionRepository.AddNav(examQuestions)
+                .Include(x => x.ExamQuestionAnswers)
+                .Include(x => x.ExamQuestionKnowladges)
+                .Include(x => x.ExamQuestionOptionses)
+                .Include(x => x.ExamQuestionSourcewares)
+                .ExecuteCommandAsync();
+        }
+
+
+        public async Task DeleteExamQuestions(string testPaperId, CancellationToken cancellationToken)
+        {
+            var examQuestionIds = await _examQuestionRepository.Queryable().Where(x => x.ExamId == testPaperId).Select(x => x.Id).ToListAsync();
+
+            await _examQuestionRepository.RemoveAsync(x => examQuestionIds.Contains(x.Id));
+
+            await _examQuestionAnswerRepository.RemoveAsync(x => examQuestionIds.Contains(x.ExamQuestionId));
+
+            await _examQuestionKnowladgeRepository.RemoveAsync(x => examQuestionIds.Contains(x.ExamQuestionId));
+
+            await _examQuestionOptionsRepository.RemoveAsync(x => examQuestionIds.Contains(x.ExamQuestionId));
+
+            await _examQuestionSourcewareRepository.RemoveAsync(x => examQuestionIds.Contains(x.ExamQuestionId));
+        }
+
+        #endregion
+
+        #region private method
+
+
+        private async Task<List<ExamQuestionAnswer>> SyncQuestionAnswer(List<ExamQuestion> examQuestions, CancellationToken cancellationToken)
+        {
+            var questionAnswersRepository = new ExamRepository<QuestionAnswer>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var questionIds = examQuestions.Select(x => x.QuestionId);
+            var questionAnswers = await questionAnswersRepository.Queryable().Where(x => questionIds.Contains(x.QuestionId)).ToListAsync();
+
+            var examQuestionAnswers = new List<ExamQuestionAnswer>();
+
+            questionAnswers.ForEach(item =>
+            {
+                var examQuestionAnswer = _mapper.Map<ExamQuestionAnswer>(item);
+                var examQuestion = examQuestions.FirstOrDefault(x => x.QuestionId == item.QuestionId);
+                examQuestionAnswer.QuestionAnswerId = item.Id;
+                examQuestionAnswer.ExamQuestionId = examQuestion?.Id ?? string.Empty;
+                examQuestionAnswers.Add(examQuestionAnswer);
+            });
+
+            examQuestionAnswers.ToInsert(_sessionContext);
+
+            await _examQuestionAnswerRepository.ValidateAddAsync(examQuestionAnswers, cancellationToken);
+
+            return examQuestionAnswers;
+        }
+
+        private async Task<List<ExamQuestionKnowladge>> SyncQuestionKnowladge(List<ExamQuestion> examQuestions, CancellationToken cancellationToken)
+        {
+            var questionKnowladgesRepository = new ExamRepository<QuestionKnowladge>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var questionIds = examQuestions.Select(x => x.QuestionId);
+            var questionKnowladges = await questionKnowladgesRepository.Queryable().Where(x => questionIds.Contains(x.QuestionId)).ToListAsync();
+
+            var examQuestionKnowladges = new List<ExamQuestionKnowladge>();
+
+            questionKnowladges.ForEach(item =>
+            {
+                var examQuestionKnowladge = _mapper.Map<ExamQuestionKnowladge>(item);
+                var examQuestion = examQuestions.FirstOrDefault(x => x.QuestionId == item.QuestionId);
+                examQuestionKnowladge.KnowladgeId = item.Id;
+                examQuestionKnowladge.ExamQuestionId = examQuestion?.Id ?? string.Empty;
+                examQuestionKnowladges.Add(examQuestionKnowladge);
+            });
+
+            examQuestionKnowladges.ToInsert(_sessionContext);
+
+            await _examQuestionKnowladgeRepository.ValidateAddAsync(examQuestionKnowladges, cancellationToken);
+
+            return examQuestionKnowladges;
+        }
+
+        private async Task<List<ExamQuestionSourceware>> SyncQuestionSourcewares(List<ExamQuestion> examQuestions, CancellationToken cancellationToken)
+        {
+            var questionSourcewaresRepository = new ExamRepository<QuestionSourceware>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var questionIds = examQuestions.Select(x => x.QuestionId);
+            var questionSourcewares = await questionSourcewaresRepository.Queryable().Where(x => questionIds.Contains(x.QuestionId)).ToListAsync();
+
+            var examQuestionSourcewares = new List<ExamQuestionSourceware>();
+
+            questionSourcewares.ForEach(item =>
+            {
+                var examQuestionSourceware = _mapper.Map<ExamQuestionSourceware>(item);
+                var examQuestion = examQuestions.FirstOrDefault(x => x.QuestionId == item.QuestionId);
+                examQuestionSourceware.SourcewareId = item.Id;
+                examQuestionSourceware.ExamQuestionId = examQuestion?.Id ?? string.Empty;
+                examQuestionSourcewares.Add(examQuestionSourceware);
+            });
+
+            examQuestionSourcewares.ToInsert(_sessionContext);
+
+            await _examQuestionSourcewareRepository.ValidateAddAsync(examQuestionSourcewares, cancellationToken);
+
+            return examQuestionSourcewares;
+        }
+
+        private async Task<List<ExamQuestionOptions>> SyncQuestionOptions(List<ExamQuestion> examQuestions, CancellationToken cancellationToken)
+        {
+            var questionOptionsRepository = new ExamRepository<QuestionOptions>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var questionIds = examQuestions.Select(x => x.QuestionId);
+            var questionOptions = await questionOptionsRepository.Queryable().Where(x => questionIds.Contains(x.QuestionId)).ToListAsync();
+
+            var examQuestionOptions = new List<ExamQuestionOptions>();
+
+            questionOptions.ForEach(item =>
+            {
+                var examQuestionOption = _mapper.Map<ExamQuestionOptions>(item);
+                var examQuestion = examQuestions.FirstOrDefault(x => x.QuestionId == item.QuestionId);
+                examQuestionOption.QuestionOptionId = item.Id;
+                examQuestionOption.ExamQuestionId = examQuestion?.Id ?? string.Empty;
+                examQuestionOptions.Add(examQuestionOption);
+            });
+
+            examQuestionOptions.ToInsert(_sessionContext);
+
+            await _examQuestionOptionsRepository.ValidateAddAsync(examQuestionOptions, cancellationToken);
+
+            return examQuestionOptions;
+        }
+
+        private async Task<List<ExamQuestion>> SyncQuestion(List<Question> questions, string testPaperId, CancellationToken cancellationToken)
+        {
+
+            var examQuestions = new List<ExamQuestion>();
+
+            questions.ForEach(item =>
+            {
+                var examQuestion = _mapper.Map<ExamQuestion>(item);
+                examQuestion.ExamId = testPaperId;
+                examQuestion.QuestionId = item.Id;
+                examQuestions.Add(examQuestion);
+            });
+
+            examQuestions.ToInsert(_sessionContext);
+
+            await _examQuestionRepository.ValidateAddAsync(examQuestions, cancellationToken);
+
+            return examQuestions;
+        }
+        #endregion
+    }
+}

+ 234 - 0
src/Hotline.Application/Exam/Proxy/TestPaperProxy.cs

@@ -0,0 +1,234 @@
+using Exam.Questions;
+using Exam.Repository.Sqlsugar.Repositories;
+using Exam.TestPapers;
+using Hotline.Exams.TestPapers;
+using Hotline.Repository.SqlSugar;
+using Hotline.Repository.SqlSugar.DataPermissions;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.Questions;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.TestPapers;
+using Hotline.Repository.SqlSugar.Extensions;
+using MapsterMapper;
+using SqlSugar;
+using System.Threading;
+using XF.Domain.Authentications;
+using XF.Domain.Repository;
+
+namespace Hotline.Application.Exam.Proxy
+{
+    public class TestPaperProxy
+    {
+        private readonly ITestPaperRepository _repository;
+        private readonly ITestPaperItemRepository _testPaperItemRepository;
+        private readonly ITestPaperItemAnswerRepository _testPaperItemAnswerRepository;
+        private readonly ITestPaperItemOptionsRepository _testPaperItemOptionsRepository;
+        private readonly ITestPaperItemSourcewareRepository _testPaperItemSourcewareRepository;
+        private readonly ITestPaperItemKnowladgeRepository _testPaperItemKnowladgeRepository;
+        private readonly IDataPermissionFilterBuilder _dataPermissionFilterBuilder;
+        private readonly IServiceProvider _serviceProvider;
+        private readonly IMapper _mapper;
+        private readonly ISessionContext _sessionContext;
+        private readonly ISugarUnitOfWork<HotlineDbContext> _uow;
+        public TestPaperProxy(ITestPaperRepository repository,
+            ITestPaperItemRepository testPaperItemRepository,
+            ITestPaperItemAnswerRepository testPaperItemAnswerRepository,
+            ITestPaperItemOptionsRepository testPaperItemOptionsRepository,
+            ITestPaperItemSourcewareRepository testPaperItemSourcewareRepository,
+            ITestPaperItemKnowladgeRepository testPaperItemKnowladgeRepository,
+            IDataPermissionFilterBuilder dataPermissionFilterBuilder,
+            IServiceProvider serviceProvider,
+            IMapper mapper,ISessionContext sessionContext)
+        {
+            this._repository = repository;
+            this._testPaperItemRepository = testPaperItemRepository;
+            this._testPaperItemAnswerRepository = testPaperItemAnswerRepository;
+            this._testPaperItemOptionsRepository = testPaperItemOptionsRepository;
+            this._testPaperItemSourcewareRepository = testPaperItemSourcewareRepository;
+            this._testPaperItemKnowladgeRepository = testPaperItemKnowladgeRepository;
+            this._dataPermissionFilterBuilder = dataPermissionFilterBuilder;
+            this._serviceProvider = serviceProvider;
+            this._mapper = mapper;
+            this._sessionContext = sessionContext;
+            this._uow = repository.UOW;
+        }
+
+        #region public method
+        public async Task GenerateQuestion(List<Question> questions,string testPaperId, CancellationToken cancellationToken)
+        {
+            List<TestPaperItem> testPaperItems = await SyncQuestion(questions, testPaperId, cancellationToken);
+
+            var testPaperItemOptionses = await SyncQuestionOptions(testPaperItems, cancellationToken);
+            var testPaperItemSourcewares = await SyncQuestionSourcewares(testPaperItems, cancellationToken);
+            var testPaperItemKnowladges = await SyncQuestionKnowladge(testPaperItems, cancellationToken);
+            var testPaperItemAnswers = await SyncQuestionAnswer(testPaperItems, cancellationToken);
+
+            testPaperItems.ForEach(item =>
+            {
+                item.TestPaperItemOptionses = testPaperItemOptionses.Where(x => item.QuestionId == x.QuestionId).ToList();
+                item.TestPaperItemOptionses.ForEach(x =>
+                {
+                    x.TestPaperItemId = item.Id;
+                });
+                item.TestPaperItemSourcewares = testPaperItemSourcewares.Where(x => item.QuestionId == x.QuestionId).ToList();
+                item.TestPaperItemSourcewares.ForEach(x =>
+                {
+                    x.TestPaperItemId = item.Id;
+                });
+                item.TestPaperItemKnowlagdes = testPaperItemKnowladges.Where(x => item.QuestionId == x.QuestionId).ToList();
+                item.TestPaperItemKnowlagdes.ForEach(x =>
+                {
+                    x.TestPaperItemId = item.Id;
+                });
+                item.TestPaperItemAnswers = testPaperItemAnswers.Where(x => item.QuestionId == x.QuestionId).ToList();
+                item.TestPaperItemAnswers.ForEach(x =>
+                {
+                    x.TestPaperItemId = item.Id;
+                });
+            });
+
+            await _testPaperItemRepository.AddNav(testPaperItems)
+                .Include(x => x.TestPaperItemAnswers)
+                .Include(x => x.TestPaperItemKnowlagdes)
+                .Include(x => x.TestPaperItemOptionses)
+                .Include(x => x.TestPaperItemSourcewares)
+                .ExecuteCommandAsync();
+        }
+       
+        
+        public async Task DeleteTestPaperItems(string testPaperId, CancellationToken cancellationToken)
+        {
+            var testPaperItemIds = await _testPaperItemRepository.Queryable().Where(x => x.TestPaperId == testPaperId).Select(x=>x.Id).ToListAsync();
+
+            await _testPaperItemRepository.RemoveAsync(x => testPaperItemIds.Contains(x.Id));
+
+            await _testPaperItemAnswerRepository.RemoveAsync(x => testPaperItemIds.Contains(x.TestPaperItemId));
+
+            await _testPaperItemKnowladgeRepository.RemoveAsync(x => testPaperItemIds.Contains(x.TestPaperItemId));
+
+            await _testPaperItemOptionsRepository.RemoveAsync(x => testPaperItemIds.Contains(x.TestPaperItemId));
+
+            await _testPaperItemSourcewareRepository.RemoveAsync(x => testPaperItemIds.Contains(x.TestPaperItemId));
+        }
+
+        #endregion
+
+        #region private method
+
+
+        private async Task<List<TestPaperItemAnswer>> SyncQuestionAnswer(List<TestPaperItem> testPaperItems, CancellationToken cancellationToken)
+        {
+            var questionAnswersRepository = new ExamRepository<QuestionAnswer>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var questionIds = testPaperItems.Select(x => x.QuestionId);
+            var questionAnswers = await questionAnswersRepository.Queryable().Where(x => questionIds.Contains(x.QuestionId)).ToListAsync();
+
+            var testPaperItemAnswers = new List<TestPaperItemAnswer>();
+
+            questionAnswers.ForEach(item =>
+            {
+                var testPaperItemAnswer = _mapper.Map<TestPaperItemAnswer>(item);
+                var testPaperItem = testPaperItems.FirstOrDefault(x => x.QuestionId == item.QuestionId);
+                testPaperItemAnswer.QuestionAnswerId = item.Id;
+                testPaperItemAnswer.TestPaperItemId = testPaperItem?.Id ?? string.Empty;
+                testPaperItemAnswers.Add(testPaperItemAnswer);
+            });
+
+            testPaperItemAnswers.ToInsert(_sessionContext);
+
+            await _testPaperItemAnswerRepository.ValidateAddAsync(testPaperItemAnswers, cancellationToken);
+
+            return testPaperItemAnswers;
+        }
+
+        private async Task<List<TestPaperItemKnowladge>> SyncQuestionKnowladge(List<TestPaperItem> testPaperItems, CancellationToken cancellationToken)
+        {
+            var questionKnowladgesRepository = new ExamRepository<QuestionKnowladge>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var questionIds = testPaperItems.Select(x => x.QuestionId);
+            var questionKnowladges = await questionKnowladgesRepository.Queryable().Where(x => questionIds.Contains(x.QuestionId)).ToListAsync();
+
+            var testPaperItemKnowladges = new List<TestPaperItemKnowladge>();
+
+            questionKnowladges.ForEach(item =>
+            {
+                var testPaperItemKnowladge = _mapper.Map<TestPaperItemKnowladge>(item);
+                var testPaperItem = testPaperItems.FirstOrDefault(x => x.QuestionId == item.QuestionId);
+                testPaperItemKnowladge.KnowladgeId = item.Id;
+                testPaperItemKnowladge.TestPaperItemId = testPaperItem?.Id ?? string.Empty;
+                testPaperItemKnowladges.Add(testPaperItemKnowladge);
+            });
+
+            testPaperItemKnowladges.ToInsert(_sessionContext);
+
+            await _testPaperItemKnowladgeRepository.ValidateAddAsync(testPaperItemKnowladges, cancellationToken);
+
+            return testPaperItemKnowladges;
+        }
+
+        private async Task<List<TestPaperItemSourceware>> SyncQuestionSourcewares(List<TestPaperItem> testPaperItems, CancellationToken cancellationToken)
+        {
+            var questionSourcewaresRepository = new ExamRepository<QuestionSourceware>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var questionIds = testPaperItems.Select(x => x.QuestionId);
+            var questionSourcewares = await questionSourcewaresRepository.Queryable().Where(x => questionIds.Contains(x.QuestionId)).ToListAsync();
+
+            var testPaperItemSourcewares = new List<TestPaperItemSourceware>();
+
+            questionSourcewares.ForEach(item =>
+            {
+                var testPaperItemSourceware = _mapper.Map<TestPaperItemSourceware>(item);
+                var testPaperItem = testPaperItems.FirstOrDefault(x => x.QuestionId == item.QuestionId);
+                testPaperItemSourceware.SourcewareId = item.Id;
+                testPaperItemSourceware.TestPaperItemId = testPaperItem?.Id ?? string.Empty;
+                testPaperItemSourcewares.Add(testPaperItemSourceware);
+            });
+
+            testPaperItemSourcewares.ToInsert(_sessionContext);
+
+            await _testPaperItemSourcewareRepository.ValidateAddAsync(testPaperItemSourcewares, cancellationToken);
+
+            return testPaperItemSourcewares;
+        }
+
+        private async Task<List<TestPaperItemOptions>> SyncQuestionOptions(List<TestPaperItem> testPaperItems, CancellationToken cancellationToken)
+        {
+            var questionOptionsRepository = new ExamRepository<QuestionOptions>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var questionIds = testPaperItems.Select(x => x.QuestionId);
+            var questionOptions = await questionOptionsRepository.Queryable().Where(x => questionIds.Contains(x.QuestionId)).ToListAsync();
+
+            var testPaperItemOptions = new List<TestPaperItemOptions>();
+
+            questionOptions.ForEach(item =>
+            {
+                var testPaperItemOption = _mapper.Map<TestPaperItemOptions>(item);
+                var testPaperItem = testPaperItems.FirstOrDefault(x => x.QuestionId == item.QuestionId);
+                testPaperItemOption.QuestionOptionId = item.Id;
+                testPaperItemOption.TestPaperItemId = testPaperItem?.Id ?? string.Empty;
+                testPaperItemOptions.Add(testPaperItemOption);
+            });
+
+            testPaperItemOptions.ToInsert(_sessionContext);
+
+            await _testPaperItemOptionsRepository.ValidateAddAsync(testPaperItemOptions, cancellationToken);
+
+            return testPaperItemOptions;
+        }
+
+        private async Task<List<TestPaperItem>> SyncQuestion(List<Question> questions, string testPaperId, CancellationToken cancellationToken)
+        {
+
+            var testPaperItems = new List<TestPaperItem>();
+
+            questions.ForEach(item =>
+            {
+                var testPaperItem = _mapper.Map<TestPaperItem>(item);
+                testPaperItem.TestPaperId = testPaperId;
+                testPaperItem.QuestionId = item.Id;
+                testPaperItems.Add(testPaperItem);
+            });
+
+            testPaperItems.ToInsert(_sessionContext);
+
+            await _testPaperItemRepository.ValidateAddAsync(testPaperItems, cancellationToken);
+
+            return testPaperItems;
+        }
+        #endregion
+    }
+}

+ 38 - 0
src/Hotline.Application/Exam/QueryExtensions/ExamManages/ExamManageQueryExtensions.cs

@@ -0,0 +1,38 @@
+using Exam.ExamManages;
+using Exam.Infrastructure.Extensions;
+using Exam.Infrastructure.Web.Utilities;
+using Hotline.Share.Requests.Exam;
+using JiebaNet.Segmenter.Common;
+using System.Linq.Expressions;
+
+namespace Hotline.Application.Exam.QueryExtensions.ExamManages
+{
+    public static class ExamManageQueryExtensions
+    {
+        public static Expression<Func<ExamManage,bool>> GetExpression(this ExamManagePagedRequest examManagePagedRequest)
+        {
+            Expression<Func<ExamManage, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<ExamManage>()
+                         .AndIF(examManagePagedRequest.Code.IsNotNullOrEmpty(), x => x.Code.Contains(examManagePagedRequest.Code))
+                         .AndIF(examManagePagedRequest.Name.IsNotNullOrEmpty(), x => x.Name.Contains(examManagePagedRequest.Name))
+                         .AndIF(examManagePagedRequest.ExamType.IsNotNull(), x => x.ExamType == examManagePagedRequest.ExamType)
+                         .AndIF(examManagePagedRequest.Mode.IsNotNull(), x => x.Mode == examManagePagedRequest.Mode).ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<ExamManage,bool>> GetExpression(this GenerateExamTestPaperRequest generateExamTestPaperRequest)
+        {
+            Expression<Func<ExamManage, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<ExamManage>()
+                         .AndIF(generateExamTestPaperRequest.ExamManageId.IsNotNullOrEmpty(), x => x.Id == generateExamTestPaperRequest.ExamManageId)
+                         .AndIF(generateExamTestPaperRequest.TestPaperId.IsNotNullOrEmpty(), x => x.TestPaperId == generateExamTestPaperRequest.TestPaperId)
+                         .AndIF(generateExamTestPaperRequest.ExtractRuleId.IsNotNullOrEmpty(),x=>x.ExtractRuleId == generateExamTestPaperRequest.ExtractRuleId)
+                         .ToExpression();
+
+            return expression;
+        }
+    }
+}

+ 41 - 0
src/Hotline.Application/Exam/QueryExtensions/ExamManages/ExamQuestionQueryExtensions.cs

@@ -0,0 +1,41 @@
+using Exam.ExamManages;
+using Exam.Infrastructure.Extensions;
+using Exam.Infrastructure.Web.Utilities;
+using Exam.Questions;
+using Exam.TestPapers;
+using Hotline.Share.Requests.Exam;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Exam.QueryExtensions.ExamManages
+{
+    public static class ExamQuestionQueryExtensions
+    {
+
+        public static Expression<Func<ExamManage,bool>> GetExpression(this ExamQuestionGroupRequest examQuestionGroupRequest)
+        {
+            Expression<Func<ExamManage, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<ExamManage>()
+                .AndIF(examQuestionGroupRequest.ExamId.IsNotNullOrEmpty(), x => x.Id == examQuestionGroupRequest.ExamId).ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<ExamQuestion, bool>> GetExpression(this ExamQuestionRequest examQuestionRequest)
+        {
+            Expression<Func<ExamQuestion, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<ExamQuestion>()
+                .AndIF(examQuestionRequest.QuestionId.IsNotNullOrEmpty(), x => x.QuestionId == examQuestionRequest.QuestionId)
+                .AndIF(examQuestionRequest.ExamId.IsNotNullOrEmpty(), x => x.ExamId == examQuestionRequest.ExamId)
+                .ToExpression();
+
+            return expression;
+        }
+    }
+}

+ 20 - 0
src/Hotline.Application/Exam/QueryExtensions/ExamManages/ExamTagQueryExtensions.cs

@@ -0,0 +1,20 @@
+using Exam.ExamManages;
+using Hotline.Share.Requests.Exam;
+using System.Linq.Expressions;
+
+namespace Hotline.Application.Exam.QueryExtensions.ExamManages
+{
+    public static class ExamTagQueryExtesions
+    {
+        public static Expression<Func<ExamTag,bool>> GetExpression(this ExamTagRequest examTagRequest)
+        {
+            Expression<Func<ExamTag, bool>> expression = m => m.Id != null;
+
+            return expression;
+        }
+
+    }
+}
+
+
+

+ 25 - 0
src/Hotline.Application/Exam/QueryExtensions/ExamManages/ExtractRuleQueryExtensions.cs

@@ -0,0 +1,25 @@
+using Exam.ExamManages;
+using Exam.Infrastructure.Extensions;
+using Exam.Infrastructure.Web.Utilities;
+using Hotline.Share.Requests.Exam;
+using JiebaNet.Segmenter.Common;
+using System.Linq.Expressions;
+
+namespace Hotline.Application.Exam.QueryExtensions.ExamManages
+{
+    public static class ExtractRuleQueryExtensions
+    {
+        public static Expression<Func<ExtractRule, bool>> GetExpression(this ExtractRulePagedRequest extractRulePagedRequest)
+        {
+            Expression<Func<ExtractRule, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<ExtractRule>()
+                         .AndIF(extractRulePagedRequest.Code.IsNotNullOrEmpty(), x => x.Code.Contains(extractRulePagedRequest.Code))
+                         .AndIF(extractRulePagedRequest.Name.IsNotNullOrEmpty(), x => x.Name.Contains(extractRulePagedRequest.Name))
+                         .AndIF(extractRulePagedRequest.ExamType.IsNotNull(), x => x.RuleType == extractRulePagedRequest.ExamType)
+                         .AndIF(extractRulePagedRequest.Status.IsNotNull(), x => x.Status == extractRulePagedRequest.Status).ToExpression();
+
+            return expression;
+        }
+    }
+}

+ 22 - 0
src/Hotline.Application/Exam/QueryExtensions/ExamManages/TagQuestionQueryExtensions.cs

@@ -0,0 +1,22 @@
+using Exam.Infrastructure.Web.Utilities;
+using Exam.Questions;
+using Hotline.Application.Exam.Extensions;
+using Hotline.Share.Requests.Exam;
+using JiebaNet.Segmenter.Common;
+using System.Linq.Expressions;
+
+namespace Hotline.Application.Exam.QueryExtensions.ExamManages
+{
+    public static class TagQuestionQueryExtensions
+    {
+        public static Expression<Func<QuestionTag, bool>> GetExpression(this TagQuestionRequest tagQuestionRequest)
+        {
+            Expression<Func<QuestionTag, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<QuestionTag>()
+                .AndIF(tagQuestionRequest.TagIds.IsNotNullOrEmpty(), x => tagQuestionRequest.TagIds.Contains(x.TagId)).ToExpression();
+
+            return expression;
+        }
+    }
+}

+ 113 - 0
src/Hotline.Application/Exam/QueryExtensions/ExamManages/UserExamQueryExtensions.cs

@@ -0,0 +1,113 @@
+using Exam.ExamManages;
+using Exam.Infrastructure.Extensions;
+using Exam.Infrastructure.Web.Utilities;
+using Exam.Share;
+using Exam.Trains;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.Requests.Train;
+using JiebaNet.Segmenter.Common;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Exam.QueryExtensions.ExamManages
+{
+    public static class UserExamQueryExtensions
+    {
+        public static Expression<Func<UserExam,bool>> GetExpression(this UserExamPagedRequest userExamPagedRequest)
+        {
+            Expression<Func<UserExam, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<UserExam>()
+                .AndIF(userExamPagedRequest.MinScore.IsNotNull(), x => x.Score >= userExamPagedRequest.MinScore)
+                .AndIF(userExamPagedRequest.MaxScore.IsNotNull(), x => x.Score <= userExamPagedRequest.MaxScore)
+                .And(x=>x.UserId == userExamPagedRequest.UserId)
+                .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<ExamManage,bool>> GetExamManageExpression(this UserExamPagedRequest userExamPagedRequest)
+        {
+            Expression<Func<ExamManage, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<ExamManage>()
+                .AndIF(userExamPagedRequest.Name.IsNotNullOrEmpty(), x => x.Name.Contains(userExamPagedRequest.Name))
+                .AndIF(userExamPagedRequest.ExamType.IsNotNull(), x => x.ExamType == userExamPagedRequest.ExamType)
+                .AndIF(userExamPagedRequest.MinStartTime.IsNotNull(),x=>x.StartTime >= userExamPagedRequest.MinStartTime)
+                .AndIF(userExamPagedRequest.MaxStartTime.IsNotNull(),x=>x.StartTime <= userExamPagedRequest.MaxStartTime)
+                .AndIF(userExamPagedRequest.MinEndTime.IsNotNull(), x => x.EndTime >= userExamPagedRequest.MinEndTime)
+                .AndIF(userExamPagedRequest.MaxEndTime.IsNotNull(),x=>x.EndTime <= userExamPagedRequest.MaxEndTime)
+                .AndIF(userExamPagedRequest.MinTotalScore.IsNotNull(),x=>x.TotalScore >= userExamPagedRequest.MinTotalScore)
+                .AndIF(userExamPagedRequest.MaxTotalScore.IsNotNull(),x=>x.TotalScore <= userExamPagedRequest.MaxTotalScore)
+                .ToExpression();
+
+            return expression;
+        }
+
+    
+        public static Expression<Func<UserExam,bool>> GetExpression(this GradingExamRequest gradingExamRequest)
+        {
+            Expression<Func<UserExam, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<UserExam>()
+                .AndIF(gradingExamRequest.ExamId.IsNotNullOrEmpty(), x => x.ExamId == gradingExamRequest.ExamId)
+                .AndIF(gradingExamRequest.UserId.IsNotNullOrEmpty(), x => x.UserId == gradingExamRequest.UserId).ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<ExamManage,bool>> GetExpression(this UnExamUserReportPagedRequest unExamUserReportPagedRequest)
+        {
+            Expression<Func<ExamManage, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<ExamManage>()
+            .AndIF(unExamUserReportPagedRequest.StartTime.IsNotNull(), x => x.StartTime >= unExamUserReportPagedRequest.StartTime)
+            .AndIF(unExamUserReportPagedRequest.EndTime.IsNotNull(), x => x.StartTime <= unExamUserReportPagedRequest.EndTime)
+            .AndIF(unExamUserReportPagedRequest.Keyword.IsNotNull(), x => x.Name.Contains(unExamUserReportPagedRequest.Keyword))
+            .And(x=>x.ExamStatus == Share.Enums.Exams.EExamStatus.Complete)
+            .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<UserExam, bool>> GetUserExamExpression(this UnExamUserReportPagedRequest unExamUserReportPagedRequest)
+        {
+            Expression<Func<UserExam, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<UserExam>()
+                .And(x=>x.ExamStatus == Share.Enums.Exams.EExamStatus.NoStart)
+            .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<ExamManage,bool>> GetExpression(this UserExamResultReportPagedRequest userExamResultReportPagedRequest)
+        {
+            Expression<Func<ExamManage, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<ExamManage>()
+            .AndIF(userExamResultReportPagedRequest.StartTime.IsNotNull(), x => x.StartTime >= userExamResultReportPagedRequest.StartTime)
+            .AndIF(userExamResultReportPagedRequest.EndTime.IsNotNull(), x => x.StartTime <= userExamResultReportPagedRequest.EndTime)
+            .AndIF(userExamResultReportPagedRequest.Keyword.IsNotNull(), x => x.Name.Contains(userExamResultReportPagedRequest.Keyword))
+            .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<UserExam, bool>> GetUserExamExpression(this UserExamResultReportPagedRequest userExamResultReportPagedRequest)
+        {
+            Expression<Func<UserExam, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<UserExam>()
+            .AndIF(userExamResultReportPagedRequest.MinScore.IsNotNull(), x => x.Score >= userExamResultReportPagedRequest.MinScore)
+            .AndIF(userExamResultReportPagedRequest.MaxScore.IsNotNull(), x => x.Score <= userExamResultReportPagedRequest.MaxScore)
+            .ToExpression();
+
+            return expression;
+        }
+    }
+}

+ 70 - 0
src/Hotline.Application/Exam/QueryExtensions/Practices/PracticeQueryExtensions.cs

@@ -0,0 +1,70 @@
+using DocumentFormat.OpenXml.Office2013.Excel;
+using Exam.Infrastructure.Web.Utilities;
+using Exam.Practices;
+using Exam.Questions;
+using Hotline.Application.Exam.Core.Extensions;
+using Hotline.Application.Exam.Extensions;
+using Hotline.Share.Enums.Exams;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.Requests.TestPaper;
+using JiebaNet.Segmenter.Common;
+using System.Linq.Expressions;
+
+namespace Hotline.Application.Exam.QueryExtensions.Practices
+{
+    public static class PracticeQueryExtensions
+    {
+        public static Expression<Func<Practice,bool>> GetExpression(this PracticePagedRequest practicePagedRequest)
+        {
+            Expression<Func<Practice, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<Practice>()
+                .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<PracticeQuestion,bool>> GetExpression(this PracticeQuestionGroupRequest practiceQuestionGroupRequest)
+        {
+            Expression<Func<PracticeQuestion, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<PracticeQuestion>()
+                .AndIF(practiceQuestionGroupRequest.PracticeId.IsNotNull(),x=>x.PracticeId == practiceQuestionGroupRequest.PracticeId)
+                .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<PracticeQuestion,bool>> GetExpression(this PracticeQuestionRequest practiceQuestionRequest)
+        {
+            Expression<Func<PracticeQuestion, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<PracticeQuestion>()
+                .AndIF(practiceQuestionRequest.PracticeQuestionId.IsNotNull(), x => x.Id == practiceQuestionRequest.PracticeQuestionId)
+                .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<QuestionTag, bool>> GetExpression(this TagQuestionCountForPracticeRequest tagQuestionCountForPracticeRequest)
+        {
+            Expression<Func<QuestionTag, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<QuestionTag>()
+               .AndIF(tagQuestionCountForPracticeRequest.TagIds.IsNotNullOrEmpty() && tagQuestionCountForPracticeRequest.TagIds.Any(), x => tagQuestionCountForPracticeRequest.TagIds.Contains(x.TagId))               
+               .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<Question, bool>> GetQuestionExpression(this TagQuestionCountForPracticeRequest tagQuestionCountForPracticeRequest)
+        {
+            Expression<Func<Question, bool>> expression = m => m.Id != null;
+            expression = ExpressionableUtility.CreateExpression<Question>()
+            .And(x=>x.QuestionType == EQuestionType.Single || x.QuestionType == EQuestionType.Multi || x.QuestionType == EQuestionType.Judge)
+            .ToExpression();
+
+            return expression;
+        }
+    }
+}

+ 20 - 0
src/Hotline.Application/Exam/QueryExtensions/Questions/QuestionAnswerQueryExtensions.cs

@@ -0,0 +1,20 @@
+using Exam.Questions;
+using Hotline.Share.Requests.Question;
+using System.Linq.Expressions;
+
+namespace Hotline.Application.Exam.QueryExtensions.Questions
+{
+    public static class QuestionAnswerQueryExtesions
+    {
+        public static Expression<Func<QuestionAnswer,bool>> GetExpression(this QuestionAnswerPagedRequest questionAnswerPagedRequest)
+        {
+            Expression<Func<QuestionAnswer, bool>> expression = m => m.Id != null;
+
+            return expression;
+        }
+
+    }
+}
+
+
+

+ 20 - 0
src/Hotline.Application/Exam/QueryExtensions/Questions/QuestionKnowladgeQueryExtensions.cs

@@ -0,0 +1,20 @@
+using Exam.Questions;
+using Hotline.Share.Requests.Question;
+using System.Linq.Expressions;
+
+namespace Hotline.Application.Exam.QueryExtensions.Questions
+{
+    public static class QuestionKnowladgeQueryExtesions
+    {
+        public static Expression<Func<TestPaperItemKnowladge,bool>> GetExpression(this QuestionKnowladgePagedRequest questionKnowladgePagedRequest)
+        {
+            Expression<Func<TestPaperItemKnowladge, bool>> expression = m => m.Id != null;
+
+            return expression;
+        }
+
+    }
+}
+
+
+

+ 20 - 0
src/Hotline.Application/Exam/QueryExtensions/Questions/QuestionOptionsQueryExtensions.cs

@@ -0,0 +1,20 @@
+using Exam.Questions;
+using Hotline.Share.Requests.Question;
+using System.Linq.Expressions;
+
+namespace Hotline.Application.Exam.QueryExtensions.Questions
+{
+    public static class QuestionOptionsQueryExtesions
+    {
+        public static Expression<Func<QuestionOptions,bool>> GetExpression(this QuestionOptionsPagedRequest questionOptionsPagedRequest)
+        {
+            Expression<Func<QuestionOptions, bool>> expression = m => m.Id != null;
+
+            return expression;
+        }
+
+    }
+}
+
+
+

+ 56 - 0
src/Hotline.Application/Exam/QueryExtensions/Questions/QuestionQueryExtesions.cs

@@ -0,0 +1,56 @@
+using Exam.ExamManages;
+using Exam.Infrastructure.Extensions;
+using Exam.Infrastructure.Web.Utilities;
+using Exam.Questions;
+using Hotline.Application.Exam.Extensions;
+using Hotline.Share.Requests.Question;
+using JiebaNet.Segmenter.Common;
+using SqlSugar;
+using System.Linq.Expressions;
+
+namespace Hotline.Application.Exam.QueryExtensions.Questions
+{
+    public static class QuestionQueryExtesions
+    {
+        public static Expression<Func<Question, bool>> GetExpression(this QuestionPagedRequest questionPagedRequest)
+        {
+            Expression<Func<Question, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<Question>().AndIF(questionPagedRequest.DifficultyLevel.IsNotNull(), x => questionPagedRequest.DifficultyLevel == x.DifficultyLevel)
+            .AndIF(questionPagedRequest.Title.IsNotNullOrEmpty(), x => x.Title.Contains(questionPagedRequest.Title))
+            .ToExpression();
+            return expression;
+        }
+
+        public static Expression<Func<QuestionTag, bool>> GetQuestionTagExpression(this QuestionPagedRequest questionPagedRequest)
+        {
+            Expression<Func<QuestionTag, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<QuestionTag>().AndIF(questionPagedRequest.TagIds.IsNotNullOrEmpty(), x => questionPagedRequest.TagIds.Contains(x.TagId))
+            .ToExpression();
+            return expression;
+        }
+
+        public static Expression<Func<ExamTag, bool>> GetExamTagExpression(this QuestionPagedRequest questionPagedRequest)
+        {
+            Expression<Func<ExamTag, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<ExamTag>().
+            AndIF(questionPagedRequest.TagIds.IsNotNullOrEmpty(), x => questionPagedRequest.TagIds.Contains(x.Id))
+            .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<QuestionKnowladge, bool>> GetQuestionKnowladgeExpression(this QuestionPagedRequest questionPagedRequest)
+        {
+            Expression<Func<QuestionKnowladge, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<QuestionKnowladge>().
+            AndIF(questionPagedRequest.KnowladgeIds.IsNotNullOrEmpty(), x => questionPagedRequest.KnowladgeIds.Contains(x.QuestionId))
+            .ToExpression();
+
+            return expression;
+        }
+    }
+}

+ 20 - 0
src/Hotline.Application/Exam/QueryExtensions/Questions/QuestionSourcewareQueryExtensions.cs

@@ -0,0 +1,20 @@
+using Exam.Questions;
+using Hotline.Share.Requests.Question;
+using System.Linq.Expressions;
+
+namespace Exam.Application
+{
+    public static class QuestionSourcewareQueryExtesions
+    {
+        public static Expression<Func<PracticeQuestionSourceware,bool>> GetExpression(this QuestionSourcewarePagedRequest questionSourcewarePagedRequest)
+        {
+            Expression<Func<PracticeQuestionSourceware, bool>> expression = m => m.Id != null;
+
+            return expression;
+        }
+
+    }
+}
+
+
+

+ 20 - 0
src/Hotline.Application/Exam/QueryExtensions/Questions/QuestionTagQueryExtensions.cs

@@ -0,0 +1,20 @@
+using Exam.Questions;
+using Hotline.Share.Requests.Question;
+using System.Linq.Expressions;
+
+namespace Exam.Application
+{
+    public static class QuestionTagQueryExtesions
+    {
+        public static Expression<Func<QuestionTag,bool>> GetExpression(this QuestionTagPagedRequest questionTagPagedRequest)
+        {
+            Expression<Func<QuestionTag, bool>> expression = m => m.Id != null;
+
+            return expression;
+        }
+
+    }
+}
+
+
+

+ 29 - 0
src/Hotline.Application/Exam/QueryExtensions/Sourcewares/SourcewareCategoryQueryExtensions.cs

@@ -0,0 +1,29 @@
+using Exam.Infrastructure.Extensions;
+using Hotline.Exams.Sourcewares;
+using Hotline.Share.Requests.Sourceware;
+using JiebaNet.Segmenter.Common;
+using SqlSugar;
+using System.Linq.Expressions;
+
+namespace Exam.Application.QueryExtensions.Sourcewares
+{
+    public static class SourcewareCategoryQueryExtesions
+    {
+        public static Expression<Func<SourcewareCategory,bool>> GetExpression(this SourcewareCategoryRequest sourcewareCategoryPagedRequest)
+        {
+            Expression<Func<SourcewareCategory, bool>> expression = m => m.Id != null;
+
+            Expressionable<SourcewareCategory> expressionable = new Expressionable<SourcewareCategory>();
+
+            expressionable.AndIF(sourcewareCategoryPagedRequest.Name.IsNotNullOrEmpty(), x => x.Name.Contains(sourcewareCategoryPagedRequest.Name));
+
+            expression = expressionable.ToExpression();
+
+            return expression;
+        }
+
+    }
+}
+
+
+

+ 29 - 0
src/Hotline.Application/Exam/QueryExtensions/Sourcewares/SourcewareQueryExtensions.cs

@@ -0,0 +1,29 @@
+using Exam.Infrastructure.Extensions;
+using Exam.Infrastructure.Web.Utilities;
+using Hotline.Exams.Sourcewares;
+using Hotline.Share.Requests.Sourceware;
+using SqlSugar;
+using System.Linq.Expressions;
+
+namespace Exam.Application.QueryExtensions.Sourcewares
+{
+    public static class SourcewareQueryExtesions
+    {
+        public static Expression<Func<Sourceware, bool>> GetExpression(this SourcewarePagedRequest sourcewarePagedRequest)
+        {
+            Expression<Func<Sourceware, bool>> expression = m => m.Id != null;
+
+
+            expression = ExpressionableUtility.CreateExpression<Sourceware>().AndIF(sourcewarePagedRequest.Name.IsNotNullOrEmpty(), m => m.Name.Contains(sourcewarePagedRequest.Name))
+           .AndIF(sourcewarePagedRequest.SourcewareType.IsNotNullOrEmpty(), m => m.SourcewareType == sourcewarePagedRequest.SourcewareType)
+           .AndIF(sourcewarePagedRequest.SourcewareCategoryId.IsNotNullOrEmpty(), m => m.CategoryId == sourcewarePagedRequest.SourcewareCategoryId)
+           .ToExpression();
+
+            return expression;
+        }
+
+    }
+}
+
+
+

+ 136 - 0
src/Hotline.Application/Exam/QueryExtensions/TestPapers/TestPaperQueryExtensions.cs

@@ -0,0 +1,136 @@
+using Exam.ExamManages;
+using Exam.Infrastructure.Data.Entity;
+using Exam.Infrastructure.Extensions;
+using Exam.Infrastructure.Web.Utilities;
+using Exam.Questions;
+using Exam.TestPapers;
+using Hotline.Application.Exam.Extensions;
+using Hotline.Share.Enums.Exams;
+using Hotline.Share.Requests.TestPaper;
+using JiebaNet.Segmenter.Common;
+using System.Linq.Expressions;
+
+namespace Hotline.Application.Exam.QueryExtensions.TestPapers
+{
+    public static class TestPaperQueryExtensions
+    {
+        public static Expression<Func<TestPaper,bool>> GetExpression(this TestPaperPagedRequest testPaperPagedRequest)
+        {
+            Expression<Func<TestPaper, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<TestPaper>()
+                .AndIF(testPaperPagedRequest.Name.IsNotNullOrEmpty(), x => x.Name.Contains(testPaperPagedRequest.Name))
+                .AndIF(testPaperPagedRequest.Code.IsNotNullOrEmpty(), x => x.Code.Contains(testPaperPagedRequest.Code))
+                .AndIF(testPaperPagedRequest.Status.IsNotNull(), x => x.Status == testPaperPagedRequest.Status)
+                .AndIF(testPaperPagedRequest.ExamMode.IsNotNull(), x => x.Mode == testPaperPagedRequest.ExamMode)
+                .AndIF(testPaperPagedRequest.ExamType.IsNotNull(), x => x.ExamType == testPaperPagedRequest.ExamType)
+                .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<TestPaperRule, bool>> GetTestPaperRuleExpression(this EntityQueryRequest entityQueryRequest)
+        {
+            Expression<Func<TestPaperRule, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<TestPaperRule>()
+                .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.TestPaperId == entityQueryRequest.Id)
+                .AndIF(entityQueryRequest.Ids.IsNotNullOrEmpty(), x => entityQueryRequest.Ids.Contains(x.TestPaperId))
+                .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<TestPaper,bool>> GetTestPaperCountExpression(this TestPaperQuestionCountRequest testPaperQuestionCountRequest)
+        {
+            Expression<Func<TestPaper, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<TestPaper>()
+               .AndIF(testPaperQuestionCountRequest.TestPaperId.IsNotNullOrEmpty(), x => x.Id == testPaperQuestionCountRequest.TestPaperId)
+               .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<ExtractRule, bool>> GetExpression(this TestPaperQuestionCountRequest testPaperQuestionCountRequest)
+        {
+            Expression<Func<ExtractRule, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<ExtractRule>()
+               .AndIF(testPaperQuestionCountRequest.ExtractRuleId.IsNotNullOrEmpty(), x => x.Id == testPaperQuestionCountRequest.ExtractRuleId)
+               .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<TestPaperRule,bool>> GetTestPaperRuleExpression(this TestPaperQuestionCountRequest testPaperQuestionCountRequest)
+        {
+            Expression<Func<TestPaperRule, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<TestPaperRule>()
+               .AndIF(testPaperQuestionCountRequest.TestPaperId.IsNotNullOrEmpty(), x => x.TestPaperId == testPaperQuestionCountRequest.TestPaperId)
+               .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<TestPaperItem,bool>> GetTestPaperItemsExpression(this TestPaperQuestionCountRequest testPaperQuestionCountRequest)
+        {
+            Expression<Func<TestPaperItem, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<TestPaperItem>()
+               .AndIF(testPaperQuestionCountRequest.TestPaperId.IsNotNullOrEmpty(), x => x.TestPaperId == testPaperQuestionCountRequest.TestPaperId)
+               .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<QuestionTag,bool>> GetExpression(this TagQuestionCountRequest tagQuestionCountRequest)
+        {
+            Expression<Func<QuestionTag, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<QuestionTag>()
+               .AndIF(tagQuestionCountRequest.TagIds.IsNotNullOrEmpty() && tagQuestionCountRequest.TagIds.Any(), x => tagQuestionCountRequest.TagIds.Contains(x.TagId))
+               .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<Question,bool>> GetQuestionExpression(this TagQuestionCountRequest tagQuestionCountRequest)
+        {
+            Expression<Func<Question, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<Question>()
+               .AndIF(tagQuestionCountRequest.QuestionType.IsNotNull(), x => tagQuestionCountRequest.QuestionType ==x.QuestionType)
+               .AndIF(tagQuestionCountRequest.DifficultyLevel.IsNotNull(),x=> tagQuestionCountRequest.DifficultyLevel == x.DifficultyLevel)
+               .AndIF(tagQuestionCountRequest.QuestionIds.IsNotNullOrEmpty(), x => !tagQuestionCountRequest.QuestionIds.Contains(x.Id))
+               .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<QuestionTag, bool>> GetExpression(this TestPaperQuestionRequest testPaperQuestionRequest)
+        {
+            Expression<Func<QuestionTag, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<QuestionTag>()
+               .AndIF(testPaperQuestionRequest.TagIds.IsNotNullOrEmpty(), x => testPaperQuestionRequest.TagIds.Contains(x.TagId))
+               .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<Question, bool>> GetQuestionExpression(this TestPaperQuestionRequest testPaperQuestionRequest)
+        {
+            Expression<Func<Question, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<Question>()
+               .AndIF(testPaperQuestionRequest.QuestionType.IsNotNull(), x => testPaperQuestionRequest.QuestionType == x.QuestionType)
+               .AndIF(testPaperQuestionRequest.DifficultyLevel.IsNotNull(), x => testPaperQuestionRequest.DifficultyLevel == x.DifficultyLevel)
+               .AndIF(testPaperQuestionRequest.QuestionIds.IsNotNullOrEmpty(), x => !testPaperQuestionRequest.QuestionIds.Contains(x.Id))
+               .ToExpression();
+
+            return expression;
+        }
+    }
+}

+ 63 - 0
src/Hotline.Application/Exam/QueryExtensions/Trains/TrainPlanQueryExtensions.cs

@@ -0,0 +1,63 @@
+using Exam.Infrastructure.Extensions;
+using Exam.Infrastructure.Web.Utilities;
+using Exam.Trains;
+using Hotline.Application.Exam.Extensions;
+using Hotline.Exams.Trains;
+using Hotline.Share.Requests.Train;
+using JiebaNet.Segmenter.Common;
+using System.Linq.Expressions;
+
+namespace Hotline.Application.Exam.QueryExtensions.Trains
+{
+    public static class TrainPlanQueryExtensions
+    {
+        public static Expression<Func<TrainPlan,bool>> GetExpression(this TrainPlanPagedRequest trainPlanPagedRequest)
+        {
+            Expression<Func<TrainPlan, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<TrainPlan>()
+                .AndIF(trainPlanPagedRequest.Code.IsNotNullOrEmpty(), x => x.Code.Contains(trainPlanPagedRequest.Code))
+                .AndIF(trainPlanPagedRequest.Name.IsNotNullOrEmpty(), x => x.Name.Contains(trainPlanPagedRequest.Name))
+                .AndIF(trainPlanPagedRequest.MaxStartTime.IsNotNull(), x => x.TrainStartTime <= trainPlanPagedRequest.MaxStartTime)
+                .AndIF(trainPlanPagedRequest.MinStartTime.IsNotNull(), x => x.TrainEndTime >= trainPlanPagedRequest.MinStartTime)
+                .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<TrainPlanTemplate,bool>> GetTemplateExpression(this TrainPlanPagedRequest trainPlanPagedRequest)
+        {
+            Expression<Func<TrainPlanTemplate, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<TrainPlanTemplate>()
+                .AndIF(trainPlanPagedRequest.TrainTemplateIds.IsNotNullOrEmpty(), x => trainPlanPagedRequest.TrainTemplateIds.Contains(x.TrainTemplateId))
+                .ToExpression();
+
+            return expression;
+        }
+
+
+        public static Expression<Func<TrainPlan, bool>> GetExpression(this TrainResultReportPagedRequest trainResultReportPagedRequest)
+        {
+            Expression<Func<TrainPlan, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<TrainPlan>()
+                .AndIF(trainResultReportPagedRequest.StartTime.IsNotNull(), x => x.TrainStartTime >= trainResultReportPagedRequest.StartTime)
+                .AndIF(trainResultReportPagedRequest.EndTime.IsNotNull(), x => x.TrainStartTime <= trainResultReportPagedRequest.EndTime)
+                .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<TrainTemplate, bool>> GetTemplateExpression(this TrainResultReportPagedRequest trainResultReportPagedRequest)
+        {
+            Expression<Func<TrainTemplate, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<TrainTemplate>()
+                .AndIF(trainResultReportPagedRequest.Keyword.IsNotNullOrEmpty(), x => x.Name.Contains(trainResultReportPagedRequest.Keyword))
+                .ToExpression();
+
+            return expression;
+        }
+    }
+}

+ 52 - 0
src/Hotline.Application/Exam/QueryExtensions/Trains/TrainRecordQueryExtensions.cs

@@ -0,0 +1,52 @@
+using Exam.Infrastructure.Extensions;
+using Exam.Infrastructure.Web.Utilities;
+using Exam.Questions;
+using Exam.TestPapers;
+using Exam.Trains;
+using Hotline.Exams.Trains;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.Requests.Train;
+using JiebaNet.Segmenter.Common;
+using System.Linq.Expressions;
+
+namespace Hotline.Application.Exam.QueryExtensions.Trains
+{
+    public static class TrainRecordQueryExtensions
+    {
+        public static Expression<Func<TrainPlan,bool>> GetExpression(this TrainRecordPagedRequest trainRecordPagedRequest)
+        {
+            Expression<Func<TrainPlan, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<TrainPlan>()
+                .AndIF(trainRecordPagedRequest.Code.IsNotNull(),x=>x.Code.Contains(trainRecordPagedRequest.Code))
+                .AndIF(trainRecordPagedRequest.Name.IsNotNull(),x=>x.Name.Contains(trainRecordPagedRequest.Name))
+                .AndIF(trainRecordPagedRequest.MinStartTime.IsNotNull(),x=>x.TrainStartTime>=trainRecordPagedRequest.MinStartTime)
+                .AndIF(trainRecordPagedRequest.MaxStartTime.IsNotNull(),x=>x.TrainStartTime<=trainRecordPagedRequest.MaxStartTime)
+                .AndIF(trainRecordPagedRequest.MinEndTime.IsNotNull(),x=>x.TrainEndTime>=trainRecordPagedRequest.MinEndTime)
+                .AndIF(trainRecordPagedRequest.MaxEndTime.IsNotNull(),x=>x.TrainEndTime<=trainRecordPagedRequest.MaxEndTime)
+                .ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<TrainPractice, bool>> GetExpression(this TrainQuestionRequest trainQuestionRequest)
+        {
+            Expression<Func<TrainPractice, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<TrainPractice>()
+                .AndIF(trainQuestionRequest.QuestionId.IsNotNullOrEmpty(), x => x.QuestionId == trainQuestionRequest.QuestionId).ToExpression();
+
+            return expression;
+        }
+
+        public static Expression<Func<TrainPractice,bool>> GetExpression(this TrainPracticeRequest trainPracticeRequest)
+        {
+            Expression<Func<TrainPractice, bool>> expression = m => m.Id != null;
+
+            expression = ExpressionableUtility.CreateExpression<TrainPractice>()
+                .AndIF(trainPracticeRequest.TrainPracticeId.IsNotNullOrEmpty(), x => x.Id == trainPracticeRequest.TrainPracticeId).ToExpression();
+
+            return expression;
+        }
+    }
+}

+ 30 - 0
src/Hotline.Application/Exam/QueryExtensions/Trains/TrainTemplateQueryExtensions.cs

@@ -0,0 +1,30 @@
+using DocumentFormat.OpenXml.Presentation;
+using Exam.Infrastructure.Extensions;
+using Exam.Infrastructure.Web.Utilities;
+using Exam.Trains;
+using Hotline.Share.Requests.Train;
+using JiebaNet.Segmenter.Common;
+using System.Linq.Expressions;
+
+namespace Hotline.Application.Exam.QueryExtensions.Trains
+{
+    public static class TrainTemplateQueryExtensions
+    {
+        public static Expression<Func<TrainTemplate,bool>> GetExpression(this TrainTemplatePagedRequest trainTemplatePagedRequest)
+        {
+            Expression<Func<TrainTemplate, bool>> expression = m => m.Id!=null;
+
+            expression = ExpressionableUtility.CreateExpression<TrainTemplate>()
+                .AndIF(trainTemplatePagedRequest.Code.IsNotNullOrEmpty(), x => x.Code.Contains(trainTemplatePagedRequest.Code))
+                .AndIF(trainTemplatePagedRequest.Name.IsNotNullOrEmpty(), x => x.Name.Contains(trainTemplatePagedRequest.Name))
+                .AndIF(trainTemplatePagedRequest.IsContainsPractice.IsNotNull(), x => x.IsContainsPractice == trainTemplatePagedRequest.IsContainsPractice)
+                .AndIF(trainTemplatePagedRequest.Status.IsNotNull(),x=>x.Status==trainTemplatePagedRequest.Status)
+                .AndIF(trainTemplatePagedRequest.MinCreationTime.IsNotNull(), x => x.CreationTime >= trainTemplatePagedRequest.MinCreationTime)
+                .AndIF(trainTemplatePagedRequest.MaxCreationTime.IsNotNull(),x=>x.CreationTime<= trainTemplatePagedRequest.MaxCreationTime)
+                .AndIF(trainTemplatePagedRequest.CreatorName.IsNotNullOrEmpty(), x => x.CreatorName == trainTemplatePagedRequest.CreatorName)
+                .ToExpression();
+
+            return expression;
+        }
+    }
+}

+ 694 - 0
src/Hotline.Application/Exam/Service/ExamManages/ExamManageService.cs

@@ -0,0 +1,694 @@
+using Exam.ExamManages;
+using Exam.Infrastructure.Data.Entity;
+using Exam.Infrastructure.Enums;
+using Exam.Infrastructure.Extensions;
+using Exam.Infrastructure.Web.Extensions;
+using Exam.Infrastructure.Web.Utilities;
+using Exam.Insfrastructure.Service.Service;
+using Exam.Questions;
+using Exam.Repository.Sqlsugar.Repositories;
+using Exam.Share;
+using Exam.Share.ViewResponses.Exam;
+using Hotline.Application.Exam.Interface.ExamManages;
+using Hotline.Repository.SqlSugar;
+using Hotline.Repository.SqlSugar.DataPermissions;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.ExamManages;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Share.Dtos.Questions;
+using Hotline.Share.Requests.Exam;
+using JiebaNet.Segmenter.Common;
+using MapsterMapper;
+using SqlSugar;
+using XF.Domain.Dependency;
+using XF.Domain.Entities;
+using Hotline.Repository.SqlSugar.Extensions;
+using Hotline.Application.Exam.QueryExtensions.ExamManages;
+using XF.Domain.Exceptions;
+using NetTaste;
+using Exam.Infrastructure.Validation.Validation;
+using Hotline.Application.Exam.Extensions;
+using XF.Domain.Authentications;
+using Hotline.Repository.SqlSugar.Exam.Core.Constants;
+using Exam.Repository.Sqlsugar.Repositories.ExamManages;
+using NPOI.SS.Formula.Functions;
+using Exam.TestPapers;
+using Hotline.Exams.TestPapers;
+using Hotline.Application.Exam.Proxy;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.TestPapers;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.Questions;
+using Microsoft.AspNetCore.Http;
+using Hotline.Tools;
+using Hotline.Users;
+using System.Buffers;
+using Exam.Repository.Sqlsugar.Repositories.TestPapers;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.Exams;
+
+namespace Hotline.Application.Exam.Service.ExamManages
+{
+    public class ExamManageService : ApiService<ExamManage, AddExamManageDto, UpdateExamManageDto, HotlineDbContext>, IExamManageService, IScopeDependency
+    {
+        private readonly IExamManageRepository _repository;
+        private readonly IExamQuestionScoreRepository _examQuestionScoreRepository;
+        private readonly IUserExamRepository _userExamRepository;
+        private readonly IDataPermissionFilterBuilder _dataPermissionFilterBuilder;
+        private readonly ITestPaperRepository _testPaperRepository;
+        private readonly ITestPaperItemRepository _testPaperItemRepository;
+        private readonly ITestPaperItemAnswerRepository _testPaperItemAnswerRepository;
+        private readonly ITestPaperItemOptionsRepository _testPaperItemOptionsRepository;
+        private readonly ITestPaperItemSourcewareRepository _testPaperItemSourcewareRepository;
+        private readonly ITestPaperItemKnowladgeRepository _testPaperItemKnowladgeRepository;
+        private readonly IExamQuestionRepository _examQuestionRepository;
+        private readonly IExamQuestionAnswerRepository _examQuestionAnswerRepository;
+        private readonly IExamQuestionOptionsRepository _examQuestionOptionsRepository;
+        private readonly IExamQuestionSourcewareRepository _examQuestionSourcewareRepository;
+        private readonly IExamQuestionKnowladgeRepository _examQuestionKnowladgeRepository;
+        private readonly IServiceProvider _serviceProvider;
+        private readonly IMapper _mapper;
+        private readonly ISessionContext _sessionContext;
+        private TestPaperProxy _testPaperProxy;
+        private ExamManageProxy _examManageProxy;
+        private AddExamManageDto _addExamManageDto;
+
+        public ExamManageService(IExamManageRepository repository,
+            IExamQuestionScoreRepository examQuestionScoreRepository,
+            IUserExamRepository userExamRepository,
+            ITestPaperRepository testPaperRepository,
+            ITestPaperItemRepository testPaperItemRepository,
+            ITestPaperItemAnswerRepository testPaperItemAnswerRepository,
+            ITestPaperItemOptionsRepository testPaperItemOptionsRepository,
+            ITestPaperItemSourcewareRepository testPaperItemSourcewareRepository,
+            ITestPaperItemKnowladgeRepository testPaperItemKnowladgeRepository,
+            IExamQuestionRepository examQuestionRepository,
+            IExamQuestionAnswerRepository examQuestionAnswerRepository,
+            IExamQuestionOptionsRepository examQuestionOptionsRepository,
+            IExamQuestionSourcewareRepository examQuestionSourcewareRepository,
+            IExamQuestionKnowladgeRepository examQuestionKnowladgeRepository,
+            IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider,
+            IMapper mapper, ISessionContext sessionContext) : base(repository, mapper, sessionContext)
+        {
+            this._repository = repository;
+            this._examQuestionScoreRepository = examQuestionScoreRepository;
+            this._userExamRepository = userExamRepository;
+            _dataPermissionFilterBuilder = dataPermissionFilterBuilder;
+            this._testPaperRepository = testPaperRepository;
+            this._testPaperItemRepository = testPaperItemRepository;
+            this._testPaperItemAnswerRepository = testPaperItemAnswerRepository;
+            this._testPaperItemOptionsRepository = testPaperItemOptionsRepository;
+            this._testPaperItemSourcewareRepository = testPaperItemSourcewareRepository;
+            this._testPaperItemKnowladgeRepository = testPaperItemKnowladgeRepository;
+            this._examQuestionRepository = examQuestionRepository;
+            this._examQuestionAnswerRepository = examQuestionAnswerRepository;
+            this._examQuestionOptionsRepository = examQuestionOptionsRepository;
+            this._examQuestionSourcewareRepository = examQuestionSourcewareRepository;
+            this._examQuestionKnowladgeRepository = examQuestionKnowladgeRepository;
+            _serviceProvider = serviceProvider;
+            this._mapper = mapper;
+            this._sessionContext = sessionContext;
+        }
+
+        #region public method
+        public async Task<ExamManageDto> GetAsync(EntityQueryRequest entityQueryRequest)
+        {
+            var entity = await _repository.GetAsync(entityQueryRequest.Id);
+
+            if (entity == null) return null;
+
+            var questionDto = _mapper.Map<ExamManageDto>(entity);
+
+          
+
+            if (questionDto != null)
+            {
+                questionDto.ExamQuestionScoreDtos = await GetExamQuestionScores(entityQueryRequest, entity);
+                if (entity.Mode == Share.Enums.Exams.EExamMode.Random)
+                {
+                    questionDto.TestPaperId = entity.ExtractRuleId;
+
+                }
+
+                questionDto.UserExamDtos = await GetUserExams(entityQueryRequest);
+            }
+
+            return questionDto;
+        }
+
+        public async Task<(int, List<ExamManageViewResponse>)> GetListAsync(ExamManagePagedRequest queryRequest)
+        {
+            ISugarQueryable<ExamManageViewResponse> queryable = Queryable(queryRequest);
+
+            var result = await queryable.ToListAsync();
+
+            var total = await queryable.CountAsync();
+
+            return (total, result);
+        }
+
+        public async Task<PageViewResponse<ExamManageViewResponse>> GetPagedListAsync(ExamManagePagedRequest queryRequest)
+        {
+            ISugarQueryable<ExamManageViewResponse> queryable = Queryable(queryRequest);
+
+            var list = await queryable.ToPageListAsync(queryRequest.PageIndex, queryRequest.PageSize);
+            var total = await queryable.CountAsync();
+
+            var result = new ExamManagePageViewResponse
+            {
+                Items = list,
+                Pagination = new Pagination(queryRequest.PageIndex, queryRequest.PageSize, total)
+            };
+
+            return result;
+        }
+
+        public override async Task<string> AddAsync(AddExamManageDto actionRequest, CancellationToken cancellationToken)
+        {
+            CalcuteTotalScore(actionRequest);
+
+            base.StartTran();
+
+            var id = await base.AddAsync(actionRequest, cancellationToken);
+
+            ResolveQuestionId(actionRequest, id);
+
+            ResolveRandomExtractRuleId(actionRequest);
+
+            base.Entity.ExamQuestionScores = await AddExamQuestionScores(actionRequest, cancellationToken);
+
+            base.Entity.UserExams = await AddUserExam(actionRequest, cancellationToken);
+
+            await base.Complete(base.Entity, OperationConstant.Create);
+
+            await GenerateExamQuestion(new GenerateExamTestPaperRequest
+            {
+                TestPaperId = actionRequest.Mode == Share.Enums.Exams.EExamMode.Manual ? actionRequest.TestPaperId : null,
+                ExtractRuleId = actionRequest.Mode == Share.Enums.Exams.EExamMode.Random ? actionRequest.TestPaperId : null,
+                ExamManageId = id
+            }, cancellationToken);
+
+            return id;
+        }
+
+        private void ResolveRandomExtractRuleId(AddExamManageDto actionRequest)
+        {
+            if (actionRequest.Mode == Share.Enums.Exams.EExamMode.Random)
+            {
+                base.Entity.ExtractRuleId = actionRequest.TestPaperId;
+                base.Entity.TestPaperId = string.Empty;
+            }
+        }
+
+        public override async Task UpdateAsync(UpdateExamManageDto actionRequest, CancellationToken cancellationToken)
+        {
+            CalcuteTotalScore(actionRequest);
+
+            base.StartTran();
+
+            await base.UpdateAsync(actionRequest, cancellationToken);
+
+            ResolveQuestionId(actionRequest, actionRequest.Id);
+
+            ResolveRandomExtractRuleId(actionRequest);
+
+            MapAddUpdateExamManage(actionRequest);
+
+            base.Entity.ExamQuestionScores = await ModifyExamQuestionScores(actionRequest, cancellationToken);
+
+            base.Entity.UserExams = await ModifyUserExam(actionRequest, cancellationToken);
+
+            await base.Complete(base.Entity, OperationConstant.Update);
+
+            await GenerateExamQuestion(new GenerateExamTestPaperRequest { 
+                TestPaperId = actionRequest.Mode == Share.Enums.Exams.EExamMode.Manual? actionRequest.TestPaperId:null,
+                ExtractRuleId = actionRequest.Mode == Share.Enums.Exams.EExamMode.Random ? actionRequest.TestPaperId : null,
+                ExamManageId = actionRequest.Id
+            }, cancellationToken);
+
+        }
+
+        private void MapAddUpdateExamManage(UpdateExamManageDto actionRequest)
+        {
+
+            _addExamManageDto = _mapper.Map<AddExamManageDto>(actionRequest);
+
+            _addExamManageDto.UserExamDtos = new List<AddUserExamDto>();
+
+            _addExamManageDto.ExamQuestionScoreDtos = new List<AddExamQuestionScoreDto>();
+
+			actionRequest.UserExamDtos.ForEach(x =>
+            {
+                x.OperationStatus = x.Id != null ? EEOperationStatus.Update : EEOperationStatus.Add;
+
+                if(x.OperationStatus== EEOperationStatus.Add)
+                {
+                    _addExamManageDto.UserExamDtos.Add(_mapper.Map<AddUserExamDto>(x));
+                }
+
+            });
+            actionRequest.ExamQuestionScoreDtos.ForEach(x =>
+            {
+                x.OperationStatus = x.Id != null ? EEOperationStatus.Update : EEOperationStatus.Add;
+
+                if (x.OperationStatus == EEOperationStatus.Add)
+                {
+                    _addExamManageDto.ExamQuestionScoreDtos.Add(_mapper.Map<AddExamQuestionScoreDto>(x));
+                }
+            });
+
+        }
+
+        public override async Task DeleteAsync(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
+        {
+            await base.DeleteAsync(entityQueryRequest, cancellationToken);
+
+            var tmpEntityQueryRequest = ExpressionableUtility.CreateExpression<ExamQuestionScore>()
+                .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.ExamManageId == entityQueryRequest.Id)
+                .AndIF(entityQueryRequest.Ids.IsNotNullOrEmpty(), x => entityQueryRequest.Ids.Contains(x.ExamManageId))
+                .ToEntityQueryRequest<ExamQuestionScore>();
+
+            await DeleteExamQuestionScores(tmpEntityQueryRequest, cancellationToken);
+
+            tmpEntityQueryRequest = ExpressionableUtility.CreateExpression<UserExam>()
+                .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.ExamId == entityQueryRequest.Id)
+                .AndIF(entityQueryRequest.Ids.IsNotNullOrEmpty(), x => entityQueryRequest.Ids.Contains(x.ExamId))
+                .ToEntityQueryRequest<UserExam>();
+
+            await DeleteUserExam(tmpEntityQueryRequest, cancellationToken);
+
+        }
+
+        public async Task GenerateTestPaper(GenerateExamTestPaperRequest generateExamTestPaperRequest, CancellationToken cancellationToken)
+        {
+            var expression = generateExamTestPaperRequest.GetExpression();
+            var examManage = await _repository.GetAsync(expression);
+            if (examManage != null)
+            {
+                var tagQuestionCounts = await GetTagQuestions(examManage);
+                var questions = await GetQuestions(tagQuestionCounts);
+
+                _testPaperProxy = new TestPaperProxy(_testPaperRepository,
+                    _testPaperItemRepository,
+                    _testPaperItemAnswerRepository,
+                    _testPaperItemOptionsRepository,
+                    _testPaperItemSourcewareRepository,
+                    _testPaperItemKnowladgeRepository,
+                    _dataPermissionFilterBuilder,
+                    _serviceProvider,
+                    _mapper,
+                    _sessionContext
+                    );
+
+                await _testPaperProxy.DeleteTestPaperItems(generateExamTestPaperRequest.TestPaperId, cancellationToken);
+
+                await _testPaperProxy.GenerateQuestion(questions, generateExamTestPaperRequest.TestPaperId, cancellationToken);
+            }
+        }
+
+        public async Task GenerateExamQuestion(GenerateExamTestPaperRequest generateExamTestPaperRequest, CancellationToken cancellationToken)
+        {
+            var expression = generateExamTestPaperRequest.GetExpression();
+            var examManages = await _repository.Queryable().Where(expression).ToListAsync();
+            var examManage = examManages.FirstOrDefault();
+            if (examManage != null)
+            {
+                var questions = new List<Question>();
+                if (examManage.Mode == Share.Enums.Exams.EExamMode.Random)
+                {
+                    var tagQuestionCounts = await GetTagQuestions(examManage);
+                    questions = await GetQuestions(tagQuestionCounts);
+                }
+                else
+                {
+                    questions = await GetQuestions(examManage);
+                }
+
+                _examManageProxy = new ExamManageProxy(_repository,
+                    _examQuestionRepository,
+                    _examQuestionAnswerRepository,
+                    _examQuestionOptionsRepository,
+                    _examQuestionSourcewareRepository,
+                    _examQuestionKnowladgeRepository,
+                    _dataPermissionFilterBuilder,
+                    _serviceProvider,
+                    _mapper,
+                    _sessionContext
+                    );
+
+                await _examManageProxy.DeleteExamQuestions(generateExamTestPaperRequest.ExamManageId, cancellationToken);
+
+                await _examManageProxy.GenerateQuestion(questions, generateExamTestPaperRequest.ExamManageId, cancellationToken);
+            }
+        }
+
+        private async Task<List<Question>> GetQuestions(ExamManage examManage)
+        {
+            var questionRepository = new ExamRepository<Question>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+
+            var questionTable = questionRepository.Queryable();
+            var testPaperItemTable = _testPaperItemRepository.Queryable().Where(x => x.TestPaperId == examManage.TestPaperId);
+
+            var questions = questionTable.InnerJoin(testPaperItemTable, (q, t) => q.Id == t.QuestionId)
+                .Select((q, t) => q);
+
+            return await questions.ToListAsync();
+        }
+
+        private async Task<List<TagQuestion>> GetTagQuestions(ExamManage examManage)
+        {
+            var extractRuleRepository = new ExamRepository<ExtractRule>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var ruleTagRepository = new ExamRepository<RuleTag>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var tagQuestionRepository = new ExamRepository<TagQuestion>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+
+          
+            var extractRuleTable = extractRuleRepository.Queryable().Where(x=>x.Id == examManage.ExtractRuleId && x.RuleType == examManage.ExamType);
+            var ruleTagTable = ruleTagRepository.Queryable();
+            var tagQuestionTable = tagQuestionRepository.Queryable();
+
+            var tagQuestions = await tagQuestionTable
+                .InnerJoin(ruleTagTable, (q, rt) => q.TagId == rt.TagId)
+                .InnerJoin(extractRuleTable, (q, rt, x) => rt.RuleId == x.Id)
+                .Select((q, rt, x) => q).ToListAsync();
+
+            return tagQuestions;
+
+        }
+        #endregion
+
+        #region private method
+
+        private void ResolveQuestionId(AddExamManageDto actionRequest, string id)
+        {
+
+            actionRequest.UserExamDtos.ForEach(x => x.ExamId = id);
+
+            actionRequest.ExamQuestionScoreDtos.ForEach(x => x.ExamManageId = id);
+        }
+
+        private void ResolveQuestionId(UpdateExamManageDto actionRequest, string id)
+        {
+
+            actionRequest.UserExamDtos.ForEach(x => x.ExamId = id);
+
+            actionRequest.ExamQuestionScoreDtos.ForEach(x => x.ExamManageId = id);
+        }
+
+        private async Task<List<ExamQuestionScore>> AddExamQuestionScores(AddExamManageDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.ExamQuestionScoreDtos == null) return null;
+
+            actionRequest.ExamQuestionScoreDtos.ResolveOperationStatus();
+
+            var examQuestionScoreDtos = actionRequest.ExamQuestionScoreDtos.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();
+
+            var examQuestionScores = _mapper.Map<List<ExamQuestionScore>>(examQuestionScoreDtos);
+
+            examQuestionScores.ToInsert(_sessionContext);
+
+            await _examQuestionScoreRepository.ValidateAddAsync(examQuestionScores, cancellationToken);
+
+            return examQuestionScores;
+        }
+
+        private async Task<List<UserExam>> AddUserExam(AddExamManageDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.UserExamDtos == null) return null;
+
+            actionRequest.UserExamDtos.ResolveOperationStatus();
+
+            var userExamDtos = actionRequest.UserExamDtos.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();
+
+            var userExams = new List<UserExam>();
+            //_mapper.Map<List<UserExam>>(userExamDtos);
+
+            userExamDtos.ForEach(x =>
+            {
+                userExams.Add(_mapper.Map<UserExam>(x));
+            });
+
+            userExams.ToInsert(_sessionContext);
+
+            await _userExamRepository.ValidateAddAsync(userExams, cancellationToken);
+
+            return userExams;
+
+        }
+        private async Task<List<UserExam>> UpdateUserExam(UpdateExamManageDto actionRequest, List<UserExam> all, CancellationToken cancellationToken)
+        {
+            if (actionRequest.UserExamDtos == null) return null;
+
+            var userExamDtos = actionRequest.UserExamDtos.Where(x => x.OperationStatus == EEOperationStatus.Update).ToList();
+
+            var ids = userExamDtos.Select(x => x.Id);
+
+            var userExams = all.Where(x => ids.Contains(x.Id)).ToList();
+
+            //userExams = _mapper.Map<List<UpdateUserExamDto>, List<UserExam>>(userExamDtos, userExams);
+            userExams.ForEach(item =>
+            {
+                var userExamDto = userExamDtos.FirstOrDefault(x => x.Id == item.Id);
+
+                if (userExamDto != null)
+                    _mapper.Map<UpdateUserExamDto, UserExam>(userExamDto, item);
+            });
+
+            userExams.ToUpdate(_sessionContext);
+
+            await _userExamRepository.ValidateUpdateAsync(userExams, cancellationToken);
+
+            return userExams;
+        }
+
+        private async Task<List<ExamQuestionScore>> UpdateExamQuestionScores(UpdateExamManageDto actionRequest, List<ExamQuestionScore> all, CancellationToken cancellationToken)
+        {
+            if (actionRequest.ExamQuestionScoreDtos == null) return null;
+
+            var examQuestionScoreDtos = actionRequest.ExamQuestionScoreDtos.Where(x => x.OperationStatus == EEOperationStatus.Update).ToList();
+
+            var ids = examQuestionScoreDtos.Select(x => x.Id);
+
+            var examQuestionScores = all.Where(x => ids.Contains(x.Id)).ToList();
+
+            //examQuestionScores = _mapper.Map<List<UpdateExamQuestionScoreDto>, List<ExamQuestionScore>>(examQuestionScoreDtos, examQuestionScores);
+
+            examQuestionScores.ForEach(item =>
+            {
+                var examQuestionScoreDto = examQuestionScoreDtos.FirstOrDefault(x => x.Id == item.Id);
+                if (examQuestionScoreDto != null)
+                    item = _mapper.Map<UpdateExamQuestionScoreDto, ExamQuestionScore>(examQuestionScoreDto, item);
+            });
+
+            examQuestionScores.ToUpdate(_sessionContext);
+
+            await _examQuestionScoreRepository.ValidateUpdateAsync(examQuestionScores, cancellationToken);
+
+            return examQuestionScores;
+        }
+
+        private async Task DeleteUserExam(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
+        {
+            await _userExamRepository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
+        }
+
+        private async Task DeleteExamQuestionScores(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
+        {
+            await _examQuestionScoreRepository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
+        }
+
+        private async Task<List<UserExam>> ModifyUserExam(UpdateExamManageDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.UserExamDtos == null) return null;
+
+            var all = await _userExamRepository.Queryable().Where(x => x.ExamId == actionRequest.Id).ToListAsync();
+
+            actionRequest.UserExamDtos.ResolveOperationStatus(all);
+
+            var userExams = new List<UserExam>();
+
+            userExams.AddRangeExt(await AddUserExam(_addExamManageDto, cancellationToken));
+
+            userExams.AddRangeExt(await UpdateUserExam(actionRequest, all, cancellationToken));
+
+            var questionOptionsDtos = actionRequest.UserExamDtos.Where(x => x.OperationStatus == EEOperationStatus.Delete);
+            var ids = questionOptionsDtos.Select(m => m.Id);
+            EntityQueryRequest entityQueryRequest = ResovleDelete<UserExam>(ids);
+
+            await DeleteUserExam(entityQueryRequest, cancellationToken);
+
+            return userExams;
+
+        }
+
+        private async Task<List<ExamQuestionScore>> ModifyExamQuestionScores(UpdateExamManageDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.ExamQuestionScoreDtos == null) return null;
+
+            var all = await _examQuestionScoreRepository.Queryable().Where(x => x.ExamManageId == actionRequest.Id).ToListAsync();
+
+            var examQuestionScores = new List<ExamQuestionScore>();
+
+            examQuestionScores.AddRangeExt(await AddExamQuestionScores(_addExamManageDto, cancellationToken));
+
+            examQuestionScores.AddRangeExt(await UpdateExamQuestionScores(actionRequest, all, cancellationToken));
+
+            var examQuestionScoreDtos = actionRequest.ExamQuestionScoreDtos.Where(x => x.OperationStatus == EEOperationStatus.Delete);
+            var ids = examQuestionScoreDtos.Select(m => m.Id);
+            EntityQueryRequest entityQueryRequest = ResovleDelete<ExamQuestionScore>(ids);
+
+            await DeleteExamQuestionScores(entityQueryRequest, cancellationToken);
+
+            return examQuestionScores;
+        }
+
+        private EntityQueryRequest ResovleDelete<T>(IEnumerable<string> ids) where T : class, IEntity<string>, new()
+        {
+            Expressionable<T> expressionable = ExpressionableUtility.CreateExpression<T>();
+            expressionable.AndIF(ids.Any(), x => ids.Contains(x.Id));
+
+            var entityQueryRequest = new EntityQueryRequest
+            {
+                Expression = ids.Any() ? expressionable.ToExpression() : null
+            };
+            return entityQueryRequest;
+        }
+
+        private async Task<List<UserExamDto>> GetUserExams(EntityQueryRequest entityQueryRequest)
+        {
+            var userRepository = new ExamRepository<User>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var userExams = _userExamRepository.Queryable().Where(x => x.ExamId == entityQueryRequest.Id);
+            var userTable = userRepository.Queryable();
+
+
+            var userExamDtos = userExams.InnerJoin(userTable,(e,u)=>e.UserId == u.Id).Select((e, u) => new UserExamDto
+            {
+                Id = e.Id,
+                Name = u.Name,
+                ExamId = e.ExamId,
+                UserId = e.UserId,
+                FullOrgName = u.FullOrgName
+            });
+
+            return await userExamDtos.ToListAsync();
+        }
+
+        private async Task<List<ExamQuestionScoreDto>> GetExamQuestionScores(EntityQueryRequest entityQueryRequest,ExamManage examManage)
+        {
+            var examQuestionScores = _examQuestionScoreRepository.Queryable().Where(x => x.ExamManageId == entityQueryRequest.Id);
+
+            var examQuestionScoreDtos = examQuestionScores.Select(q => new ExamQuestionScoreDto
+            {
+                Id = q.Id,
+                QuestionType = q.QuestionType,
+                ExamManageId = q.ExamManageId,
+                Score = q.Score
+            });
+
+            if(examManage.Mode == Share.Enums.Exams.EExamMode.Random)
+            {
+                var tagQuestionRepository = new ExamRepository<TagQuestion>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+                var tagQuestionTable = tagQuestionRepository.Queryable().Where(x => x.RuleId == examManage.ExtractRuleId);
+
+                var result = examQuestionScoreDtos.InnerJoin(tagQuestionTable, (e, t) => e.QuestionType == t.QuestionType).Select((e, t) => new ExamQuestionScoreDto
+                {
+                    Id = e.Id,
+                    QuestionType = e.QuestionType,
+                    ExamManageId = e.ExamManageId,
+                    Score = e.Score,
+                    Count = t.Count
+                }).Distinct();
+                return await result.ToListAsync();
+            }
+            else
+            {
+                var testPaperItemRepository = new ExamRepository<TestPaperItem>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+                var testPaperItemTable = testPaperItemRepository.Queryable().Where(x => x.TestPaperId == examManage.TestPaperId);
+
+                var result = examQuestionScoreDtos.InnerJoin(testPaperItemTable, (e, t) => e.QuestionType == t.QuestionType)
+                    .GroupBy((e, t) => new
+                    {
+                        Id = e.Id,
+                        QuestionType = e.QuestionType,
+                        ExamManageId = e.ExamManageId,
+                        Score = e.Score,
+                    })
+                    .Select((e, t) => new ExamQuestionScoreDto
+                {
+                    Id = e.Id,
+                    QuestionType = e.QuestionType,
+                    ExamManageId = e.ExamManageId,
+                    Score = e.Score,
+                    Count = SqlFunc.AggregateCount(t.Id)
+                });
+                return await result.ToListAsync();
+            }           
+        }
+        private void CalcuteTotalScore(AddExamManageDto actionRequest)
+        {
+            if (actionRequest.ExamQuestionScoreDtos == null) return;
+
+            var examQuestionScoreDtos = actionRequest.ExamQuestionScoreDtos.Where(x => x.OperationStatus != EEOperationStatus.Delete);
+
+            var totalScore = examQuestionScoreDtos.Sum(x => x.Count * x.Score);
+            if (totalScore != actionRequest.TotalScore)
+            {
+                throw new UserFriendlyException(ErrorMessage.ServiceError, string.Format(ErrorMessage.IsNotEqual, "试题分数总和", typeof(AddExamManageDto).GetDescription(nameof(AddExamManageDto.TotalScore))));
+            }
+        }
+
+
+
+        private ISugarQueryable<ExamManageViewResponse> Queryable(ExamManagePagedRequest queryRequest)
+        {
+            var expression = queryRequest.GetExpression();
+            var questionTable = _repository.Queryable().Where(expression);
+
+            var queryable = questionTable.Select(e => new ExamManageViewResponse
+            {
+                Id = e.Id,
+                Code = e.Code,
+                Count = e.Count,
+                CutoffScore = e.CutoffScore,
+                EndTime = e.EndTime,
+                ExamStatus = e.ExamStatus,
+                ExamType = e.ExamType,
+                Mode = e.Mode,
+                Name = e.Name,
+                Remark = e.Name,
+                SortIndex = e.SortIndex,
+                StartTime = e.StartTime,
+                TimeSpan = e.TimeSpan,
+                TotalScore = e.TotalScore,
+            });
+            return queryable;
+        }
+
+
+        private async Task<List<Question>> GetQuestions(List<TagQuestion> tagQuestionCounts)
+        {
+            var questionRepository = new ExamRepository<Question>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var questionTagRepository = new ExamRepository<QuestionTag>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+
+            var questionTable = questionRepository.Queryable();
+            var questionTagTable = questionTagRepository.Queryable();
+
+            if (tagQuestionCounts != null && tagQuestionCounts.Any())
+            {
+                var questionQuerables = new List<ISugarQueryable<Question>>();
+                tagQuestionCounts.ForEach(item =>
+                {
+                    ISugarQueryable<Question> queryable = questionTable.InnerJoin(questionTagTable, (q, t) => t.Id == t.QuestionId)
+                        .Where((q, t) => q.QuestionType == item.QuestionType && t.TagId == item.TagId).Take(item.Count).Select((q, t) => q);
+
+                    questionQuerables.Add(queryable);
+                });
+
+                var queryResult = questionRepository.UnionAll(questionQuerables.ToArray());
+
+                return await queryResult.ToListAsync();
+            }
+            else
+            {
+                return null;
+            }
+
+        }
+        #endregion
+    }
+}

+ 113 - 0
src/Hotline.Application/Exam/Service/ExamManages/ExamTagService.cs

@@ -0,0 +1,113 @@
+using Exam.Application.Interface.Exam;
+using Exam.ExamManages;
+using Exam.Infrastructure.Data.Entity;
+using Exam.Infrastructure.Extensions;
+using Exam.Insfrastructure.Service.Service;
+using Exam.Share.ViewResponses.Sourceware;
+using Hotline.Application.Exam.Constants.Messages;
+using Hotline.Application.Exam.Extensions;
+using Hotline.Application.Exam.QueryExtensions.ExamManages;
+using Hotline.Exams.Sourcewares;
+using Hotline.Repository.SqlSugar;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.ExamManages;
+using Hotline.Share.Dtos.ExamManages;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.Requests.Sourceware;
+using Hotline.Share.ViewResponses.Exam;
+using Mapster;
+using MapsterMapper;
+using System.ComponentModel;
+using XF.Domain.Dependency;
+using XF.Domain.Exceptions;
+
+namespace Hotline.Application.Exam.Service.ExamManages
+{
+    /// <summary>
+    /// 考试标签服务
+    /// </summary>
+    [Description("考试标签服务")]
+    public class ExamTagService : ApiService<ExamTag, AddExamTagDto,UpdateExamTagDto,HotlineDbContext>,IExamTagService, IScopeDependency
+    {
+        private readonly IExamTagRepository _repository;
+        private readonly IMapper _mapper;
+        public ExamTagService(IExamTagRepository repository,IMapper mapper) : base(repository, mapper)
+        {
+            _repository = repository;
+            _mapper = mapper;
+        }
+
+        public async Task<ExamTagDto> GetAsync(EntityQueryRequest entityQueryRequest)
+        {
+            var entity = await _repository.GetAsync(entityQueryRequest.Id);
+
+            return _mapper.Map<ExamTagDto>(entity);
+        }
+
+        public async Task<(int, List<ExamTagViewResponse>)> GetListAsync(ExamTagRequest queryRequest)
+        {
+            var query = _repository.Queryable();
+
+            var result = await query.Select(m => new ExamTagViewResponse { 
+                				Name = m.Name,
+								ParentId = m.ParentId,
+								Id = m.Id,
+                                SortIndex = m.SortIndex,
+                                Status = m.Status
+				            }).ToListAsync();
+
+            var total = await query.CountAsync();
+
+            return (total,result);
+        }
+
+        public async Task<PageViewResponse<ExamTagViewResponse>> GetPagedListAsync(ExamTagRequest queryRequest)
+        {
+            throw new NotImplementedException();
+        }
+
+        /// <summary>
+        /// 获取树状
+        /// </summary>
+        /// <param name="sourcewareCategoryDto"></param>
+        /// <returns></returns>
+        /// <exception cref="NotImplementedException"></exception>
+        public async Task<List<ExamTagViewResponse>> GetTreeAsync(ExamTagRequest examTagRequest)
+        {
+            var expression = examTagRequest.GetExpression();
+
+            var sourcewareCategorys = await _repository.GetTreeAsync(expression);
+
+            var sourcewareCategoryViewResponses = sourcewareCategorys.Select(m => new ExamTagViewResponse
+            {
+                Id = m.Id,
+                Name = m.Name,
+                ParentId = m.ParentId,
+                SortIndex = m.SortIndex,
+                Status = m.Status,
+                Children = _mapper.Map<List<ExamTagViewResponse>>(m.Children)
+            }).ToList();
+
+
+            return sourcewareCategoryViewResponses;
+        }
+
+        /// <summary>
+        /// 删除考试标签
+        /// </summary>
+        /// <param name="entityQueryRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        /// <exception cref="UserFriendlyException"></exception>
+        public override Task DeleteAsync(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
+        {
+            if (_repository.CheckHasChild(entityQueryRequest))
+            {
+                throw new UserFriendlyException(BusinessErrorMessage.ServiceError,string.Format(BusinessErrorMessage.HasChild, typeof(ExamTag).GetDescription()));
+            }
+
+            return base.DeleteAsync(entityQueryRequest, cancellationToken);
+        }
+    }
+}
+
+

+ 536 - 0
src/Hotline.Application/Exam/Service/ExamManages/ExtractRuleService.cs

@@ -0,0 +1,536 @@
+using DocumentFormat.OpenXml.Office2010.Excel;
+using Exam.Application.Interface.Exam;
+using Exam.ExamManages;
+using Exam.Infrastructure.Data.Entity;
+using Exam.Infrastructure.Enums;
+using Exam.Infrastructure.Extensions;
+using Exam.Infrastructure.Web.Extensions;
+using Exam.Infrastructure.Web.Utilities;
+using Exam.Insfrastructure.Service.Service;
+using Exam.Questions;
+using Exam.Repository.Sqlsugar.Repositories;
+using Exam.Share.ViewResponses.Exam;
+using Hotline.Application.Exam.Extensions;
+using Hotline.Application.Exam.QueryExtensions.ExamManages;
+using Hotline.Repository.SqlSugar;
+using Hotline.Repository.SqlSugar.DataPermissions;
+using Hotline.Repository.SqlSugar.Exam.Core.Constants;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.ExamManages;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.Questions;
+using Hotline.Repository.SqlSugar.Extensions;
+using Hotline.Share.Dtos.TestPapers;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.Tools;
+using Hotline.Share.ViewResponses.Exam;
+using JiebaNet.Segmenter.Common;
+using MapsterMapper;
+using SqlSugar;
+using XF.Domain.Authentications;
+using XF.Domain.Dependency;
+
+namespace Hotline.Application.Exam.Service.ExamManages
+{
+    public class ExtractRuleService : ApiService<ExtractRule, AddExtractRuleDto, UpdateExtractRuleDto, HotlineDbContext>, IExtractRuleService, IScopeDependency
+    {
+        private readonly IExtractRuleRepository _repository;
+        private readonly ITagQuestionRepository _tagQuestionRepository;
+        private readonly IRuleTagRepository _ruleTagRepository;
+        private readonly ISessionContext _sessionContext;
+        private readonly IDataPermissionFilterBuilder _dataPermissionFilterBuilder;
+        private readonly IServiceProvider _serviceProvider;
+        private readonly IMapper _mapper;
+        private AddExtractRuleDto _addExtractRuleDto;
+        public ExtractRuleService(IExtractRuleRepository repository,
+            ITagQuestionRepository tagQuestionRepository,
+            IRuleTagRepository ruleTagRepository,
+             ISessionContext sessionContext,
+            IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider,
+            IMapper mapper) : base(repository, mapper, sessionContext)
+        {
+            _repository = repository;
+            _tagQuestionRepository = tagQuestionRepository;
+            _ruleTagRepository = ruleTagRepository;
+            this._sessionContext = sessionContext;
+            _dataPermissionFilterBuilder = dataPermissionFilterBuilder;
+            _serviceProvider = serviceProvider;
+            _mapper = mapper;
+        }
+
+        #region public method
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="entityQueryRequest"></param>
+        /// <returns></returns>
+        public async Task<ExtractRuleDto> GetAsync(EntityQueryRequest entityQueryRequest)
+        {
+            var entity = await _repository.GetAsync(entityQueryRequest.Id);
+
+            var extractRuleDto = _mapper.Map<ExtractRuleDto>(entity);
+
+            if (extractRuleDto != null)
+            {
+                extractRuleDto.RuleTagDtos = await GetRuleTagDtos(entityQueryRequest);
+
+                extractRuleDto.TagQuestionDtos = await GetTagQuestions(entityQueryRequest);
+            }
+
+
+            return extractRuleDto;
+        }
+
+        public async Task<(int, List<ExtractRuleViewResponse>)> GetListAsync(ExtractRulePagedRequest queryRequest)
+        {
+            queryRequest.Status = queryRequest.Status ?? Share.Enums.Exams.EPublicStatus.Valid;
+
+            ISugarQueryable<ExtractRuleViewResponse> queryable = QueryResult(queryRequest);
+
+            var result = await queryable.ToListAsync();
+
+            var total = await queryable.CountAsync();
+
+            return (total, result);
+        }
+
+        public async Task<PageViewResponse<ExtractRuleViewResponse>> GetPagedListAsync(ExtractRulePagedRequest queryRequest)
+        {
+            ISugarQueryable<ExtractRuleViewResponse> queryable = QueryResult(queryRequest);
+
+            var list = await queryable.ToPageListAsync(queryRequest.PageIndex, queryRequest.PageSize);
+            var total = await queryable.CountAsync();
+
+            var result = new ExtractRulePageViewResponse
+            {
+                Items = list,
+                Pagination = new Pagination(queryRequest.PageIndex, queryRequest.PageSize, total)
+            };
+
+            return result;
+        }
+
+        /// <summary>
+        /// 新增抽题规则
+        /// </summary>
+        /// <param name="actionRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        public override async Task<string> AddAsync(AddExtractRuleDto actionRequest, CancellationToken cancellationToken)
+        {
+            base.StartTran();
+
+            var id = await base.AddAsync(actionRequest, cancellationToken);
+
+            ResolveExtractRuleId(actionRequest, id);
+
+            base.Entity.RuleTags = await AddRuleTags(actionRequest, cancellationToken);
+
+            base.Entity.TagQuestions = await AddTagQuestions(actionRequest, cancellationToken);
+
+            await base.Complete(base.Entity, OperationConstant.Create);
+
+            return id;
+        }
+
+        /// <summary>
+        /// 修改抽题规则
+        /// </summary>
+        /// <param name="actionRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        public override async Task UpdateAsync(UpdateExtractRuleDto actionRequest, CancellationToken cancellationToken)
+        {
+            base.StartTran();
+
+            await base.UpdateAsync(actionRequest, cancellationToken);
+
+            ResolveExtractRuleId(actionRequest, actionRequest.Id);
+
+            ResolveAddExtractRuleDto(actionRequest);
+
+            base.Entity.RuleTags = await ModifyRuleTags(actionRequest, cancellationToken);
+
+            base.Entity.TagQuestions = await ModifyTagQuestions(actionRequest, cancellationToken);
+
+            await base.Complete(base.Entity, OperationConstant.Update);
+        }
+
+        private void ResolveAddExtractRuleDto(UpdateExtractRuleDto actionRequest)
+        {
+            _addExtractRuleDto = _mapper.Map<AddExtractRuleDto>(actionRequest);
+
+            //_addExtractRuleDto.RuleTagDtos = new List<AddRuleTagDto>();
+
+            //actionRequest.RuleTagDtos.ToList().ForEach(item =>
+            //{
+            //    _addExtractRuleDto.RuleTagDtos.Add(_mapper.Map<AddRuleTagDto>(item));
+            //});
+
+            //_addExtractRuleDto.TagQuestionDtos = new List<AddTagQuestionDto>();
+
+            //actionRequest.TagQuestionDtos.ToList().ForEach(item =>
+            //{
+            //    _addExtractRuleDto.TagQuestionDtos.Add(_mapper.Map<AddTagQuestionDto>(item));
+            //});
+        }
+
+        /// <summary>
+        /// 删除抽题规则
+        /// </summary>
+        /// <param name="entityQueryRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        public override async Task DeleteAsync(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
+        {
+            await base.DeleteAsync(entityQueryRequest, cancellationToken);
+
+            var tempEntityQueryRequest = ExpressionableUtility.CreateExpression<TagQuestion>()
+                .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.RuleId == entityQueryRequest.Id)
+                .AndIF(entityQueryRequest.Ids.IsNotNullOrEmpty(), x => entityQueryRequest.Ids.Contains(x.RuleId)).ToEntityQueryRequest<TagQuestion>();
+
+            await DeleteTagQuestions(tempEntityQueryRequest, cancellationToken);
+
+            tempEntityQueryRequest = ExpressionableUtility.CreateExpression<RuleTag>()
+                .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.RuleId == entityQueryRequest.Id)
+                .AndIF(entityQueryRequest.Ids.IsNotNullOrEmpty(), x => entityQueryRequest.Ids.Contains(x.RuleId)).ToEntityQueryRequest<RuleTag>();
+
+
+            await DeleteRuleTags(tempEntityQueryRequest, cancellationToken);
+        }
+
+        /// <summary>
+        /// 获取标题试题数
+        /// </summary>
+        /// <param name="tagQuestionRequest"></param>
+        /// <returns></returns>
+        public async Task<List<TagQuestionViewResponse>> GetTagQuestionCount(TagQuestionRequest tagQuestionRequest)
+        {
+            if (tagQuestionRequest.TagIds.IsNullOrEmpty()) return new List<TagQuestionViewResponse>();
+
+            var expression = tagQuestionRequest.GetExpression();
+            var questionTagTable = new ExamRepository<QuestionTag>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(expression);
+            var questionTable = new ExamRepository<Question>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
+
+            var queryable = questionTagTable.LeftJoin(questionTable, (t, q) => t.QuestionId == q.Id)
+                .GroupBy((t, q) => new { t.TagId, q.QuestionType })
+                .OrderBy((t, q) => t.TagId)
+                .Select((t, q) => new TagQuestionViewResponse
+                {
+                    TagId = t.TagId,
+                    QuestionType = q.QuestionType,
+                    TotalCount = SqlFunc.AggregateCount(new
+                    {
+                        TagId = t.TagId,
+                        QuestionType = q.QuestionType,
+                    })
+                });
+
+            return await queryable.ToListAsync();
+        }
+        #endregion
+
+        #region private method
+        /// <summary>
+        /// 获取查询结果
+        /// </summary>
+        /// <param name="queryRequest"></param>
+        /// <returns></returns>
+        private ISugarQueryable<ExtractRuleViewResponse> QueryResult(ExtractRulePagedRequest queryRequest)
+        {
+            var expression = queryRequest.GetExpression();
+            var query = _repository.Queryable().Where(expression);
+            var querable = query.OrderBy(o => o.SortIndex).Select(m => new ExtractRuleViewResponse
+            {
+                Name = m.Name,
+                Id = m.Id,
+                Status = m.Status,
+                Code = m.Code,
+                SortIndex = m.SortIndex,
+                Remark = m.Remark,
+                ExamType = m.RuleType
+            });
+            return querable;
+        }
+
+        /// <summary>
+        /// 新增标签试题
+        /// </summary>
+        /// <param name="actionRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        private async Task<List<TagQuestion>> AddTagQuestions(AddExtractRuleDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.TagQuestionDtos == null) return null;
+
+            actionRequest.TagQuestionDtos.ResolveOperationStatus();
+
+            var tagQuestionDtos = actionRequest.TagQuestionDtos.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();
+
+            var tagQuestions = _mapper.Map<List<TagQuestion>>(tagQuestionDtos);
+
+            tagQuestions.ToInsert(_sessionContext);
+
+            await _tagQuestionRepository.ValidateAddAsync(tagQuestions, cancellationToken);
+
+            return tagQuestions;
+        }
+
+        /// <summary>
+        /// 新增规则标签
+        /// </summary>
+        /// <param name="actionRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        private async Task<List<RuleTag>> AddRuleTags(AddExtractRuleDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.RuleTagDtos == null) return null;
+
+            actionRequest.RuleTagDtos.ResolveOperationStatus();
+
+            var ruleTagDtos = actionRequest.RuleTagDtos.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();
+
+            var ruleTags = _mapper.Map<List<RuleTag>>(ruleTagDtos);
+
+            ruleTags.ToInsert(_sessionContext);
+
+            await _ruleTagRepository.ValidateAddAsync(ruleTags, cancellationToken);
+
+            return ruleTags;
+
+        }
+
+        /// <summary>
+        /// 修改标签试题数
+        /// </summary>
+        /// <param name="actionRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        /// <exception cref="NotImplementedException"></exception>
+        private async Task<List<TagQuestion>> ModifyTagQuestions(UpdateExtractRuleDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.TagQuestionDtos == null) return null;
+
+            //var all = await _tagQuestionRepository.Queryable().Where(m => m.RuleId == actionRequest.Id).ToListAsync();
+
+            //actionRequest.TagQuestionDtos.ResolveOperationStatus(all);
+
+            var entityQueyRequest = ExpressionableUtility.CreateExpression<TagQuestion>()
+               .AndIF(actionRequest.Id.IsNotNullOrEmpty(), x => x.RuleId == actionRequest.Id).ToEntityQueryRequest<TagQuestion>();
+
+            await DeleteTagQuestions(entityQueyRequest, cancellationToken);
+
+            var tagQuestions = new List<TagQuestion>();
+
+            tagQuestions.AddRangeExt(await AddTagQuestions(_addExtractRuleDto, cancellationToken));
+
+            //tagQuestions.AddRangeExt(await UpdateTagQuestions(actionRequest, all, cancellationToken));
+
+            //var ruleTagDtos = actionRequest.TagQuestionDtos.Where(x => x.OperationStatus == EEOperationStatus.Delete).ToList();
+            //var ids = ruleTagDtos.Select(x => x.Id).ToList();
+            //var entityQueyRequest = ExpressionableUtility.CreateExpression<TagQuestion>()
+            //    .AndIF(ids.IsNotNull(), x => ids.Contains(x.Id)).ToEntityQueryRequest<TagQuestion>();
+
+            await DeleteTagQuestions(entityQueyRequest, cancellationToken);
+
+            return tagQuestions;
+        }
+
+        /// <summary>
+        /// 修改标签试题数
+        /// </summary>
+        /// <param name="actionRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        /// <exception cref="NotImplementedException"></exception>
+        private async Task<List<TagQuestion>> UpdateTagQuestions(UpdateExtractRuleDto actionRequest, List<TagQuestion> all, CancellationToken cancellationToken)
+        {
+            if (actionRequest.TagQuestionDtos == null) return null;
+
+            var tagQuestionDtos = actionRequest.TagQuestionDtos.Where(x => x.OperationStatus == EEOperationStatus.Update).ToList();
+            var ids = tagQuestionDtos.Select(x => x.Id).ToList();
+            var tagQuestions = all.Where(x => ids.Contains(x.Id)).ToList();
+
+            var entitys = new List<TagQuestion>();
+            tagQuestionDtos.ForEach(x =>
+            {
+                var entity = tagQuestions.FirstOrDefault(x => x.Id == x.Id);
+                entity = _mapper.Map(x, entity);
+                if (entity != null)
+                {
+                    entity.RuleId = actionRequest.Id;
+                    entitys.Add(entity);
+                }
+                
+            });
+
+            entitys.ToUpdate(_sessionContext);
+
+            await _tagQuestionRepository.ValidateUpdateAsync(entitys, cancellationToken);
+
+            return entitys;
+        }
+
+        /// <summary>
+        /// 修改规则标签
+        /// </summary>
+        /// <param name="actionRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        /// <exception cref="NotImplementedException"></exception>
+        private async Task<List<RuleTag>> ModifyRuleTags(UpdateExtractRuleDto actionRequest, CancellationToken cancellationToken)
+        {
+            var ruleId = actionRequest.Id;
+
+            if (actionRequest.RuleTagDtos == null) return null;
+
+            //var all = await _ruleTagRepository.Queryable().Where(x => x.RuleId == actionRequest.Id).ToListAsync();
+
+            //actionRequest.RuleTagDtos.ResolveOperationStatus(all);
+
+            var ruleTags = new List<RuleTag>();
+
+            var entityQueyRequest = ExpressionableUtility.CreateExpression<RuleTag>()
+                .AndIF(ruleId.IsNotNullOrEmpty(), x => x.RuleId == ruleId).ToEntityQueryRequest<RuleTag>();
+
+            await DeleteRuleTags(entityQueyRequest, cancellationToken);
+
+            ruleTags.AddRangeExt(await AddRuleTags(_addExtractRuleDto, cancellationToken));
+
+            //ruleTags.AddRangeExt(await UpdateRuleTags(actionRequest, cancellationToken));
+
+            //var ruleTagDtos = actionRequest.RuleTagDtos.Where(x => x.OperationStatus == EEOperationStatus.Delete).ToList();
+            //var ids = ruleTagDtos.Select(x => x.Id).ToList();
+            //var entityQueyRequest = ExpressionableUtility.CreateExpression<RuleTag>()
+            //    .AndIF(ids.IsNotNull(), x => ids.Contains(x.Id)).ToEntityQueryRequest<RuleTag>();
+
+            return ruleTags;
+        }
+
+        private async Task<List<RuleTag>> UpdateRuleTags(UpdateExtractRuleDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.RuleTagDtos == null) return null;
+
+            var ruleTagDtos = actionRequest.RuleTagDtos.Where(x => x.OperationStatus == EEOperationStatus.Update).ToList();
+            var ids = ruleTagDtos.Select(x => x.Id).ToList();
+            var ruleTags = await _ruleTagRepository.Queryable().Where(x => ids.Contains(x.Id)).ToListAsync();
+
+            var entitys = new List<RuleTag>();
+            ruleTagDtos.ForEach(x =>
+            {
+                var entity = ruleTags.FirstOrDefault(x => x.Id == x.Id);
+                entity = _mapper.Map(x, entity);
+                if (entity != null)
+                {
+                    entity.RuleId = actionRequest.Id;
+                    entitys.Add(entity);
+                }
+               
+            });
+
+            entitys.ToUpdate(_sessionContext);
+
+            await _ruleTagRepository.ValidateUpdateAsync(entitys, cancellationToken);
+
+            return entitys;
+
+        }
+
+
+        /// <summary>
+        /// 删除规则标签
+        /// </summary>
+        /// <param name="tempEntityQueryRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        private async Task DeleteRuleTags(EntityQueryRequest tempEntityQueryRequest, CancellationToken cancellationToken)
+        {
+            await _ruleTagRepository.DeleteWithValidateAsync(tempEntityQueryRequest, cancellationToken);
+        }
+
+        /// <summary>
+        /// 删除标签试题数
+        /// </summary>
+        /// <param name="tempEntityQueryRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        private async Task DeleteTagQuestions(EntityQueryRequest tempEntityQueryRequest, CancellationToken cancellationToken)
+        {
+            await _tagQuestionRepository.DeleteWithValidateAsync(tempEntityQueryRequest, cancellationToken);
+        }
+
+        /// <summary>
+        /// 获取规则标签
+        /// </summary>
+        /// <param name="entityQueryRequest"></param>
+        /// <returns></returns>
+        /// <exception cref="NotImplementedException"></exception>
+        private async Task<List<RuleTagDto>> GetRuleTagDtos(EntityQueryRequest entityQueryRequest)
+        {
+            var ruleTagTable = _ruleTagRepository.Queryable().Where(x => x.RuleId == entityQueryRequest.Id);
+
+            var examTagTable = new ExamRepository<ExamTag>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
+
+
+            var queryable = ruleTagTable.LeftJoin(examTagTable, (r, e) => r.TagId == e.Id).Select((r, e) => new RuleTagDto
+            {
+                Id = r.Id,
+                TagId = r.TagId,
+                Tag = e.Name
+            });
+
+            return await queryable.ToListAsync();
+
+        }
+
+        /// <summary>
+        /// 获取标签试题数
+        /// </summary>
+        /// <param name="entityQueryRequest"></param>
+        /// <returns></returns>
+        /// <exception cref="NotImplementedException"></exception>
+        private async Task<List<TagQuestionDto>> GetTagQuestions(EntityQueryRequest entityQueryRequest)
+        {
+            var tagQuestionTable = _tagQuestionRepository.Queryable().Where(x => x.RuleId == entityQueryRequest.Id);
+
+            var examTagTable = new ExamRepository<ExamTag>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
+
+
+            var tagQuestionDtos = tagQuestionTable.LeftJoin(examTagTable, (t, e) => t.TagId == e.Id).Select((t, e) => new TagQuestionDto
+            {
+                Id = t.Id,
+                TagId = t.TagId,
+                QuestionType = t.QuestionType,
+                Count = t.Count,
+                Tag = e.Name
+            });
+
+            return await tagQuestionDtos.ToListAsync();
+        }
+
+        /// <summary>
+        /// 处理抽题规则Id
+        /// </summary>
+        /// <param name="actionRequest"></param>
+        /// <param name="id"></param>
+        /// <exception cref="NotImplementedException"></exception>
+        private void ResolveExtractRuleId(AddExtractRuleDto actionRequest, string id)
+        {
+            actionRequest.TagQuestionDtos.ForEach(x => x.RuleId = id);
+
+            actionRequest.RuleTagDtos.ForEach(x => x.RuleId = id);
+        }
+
+        /// <summary>
+        /// 处理抽题规则Id
+        /// </summary>
+        /// <param name="actionRequest"></param>
+        /// <param name="id"></param>
+        /// <exception cref="NotImplementedException"></exception>
+        private void ResolveExtractRuleId(UpdateExtractRuleDto actionRequest, string id)
+        {
+            actionRequest.TagQuestionDtos.ForEach(x => x.RuleId = id);
+
+            actionRequest.RuleTagDtos.ForEach(x => x.RuleId = id);
+        }
+        #endregion
+    }
+}

+ 1022 - 0
src/Hotline.Application/Exam/Service/ExamManages/UserExamService.cs

@@ -0,0 +1,1022 @@
+using DocumentFormat.OpenXml.Drawing;
+using DocumentFormat.OpenXml.Drawing.Charts;
+using DocumentFormat.OpenXml.Office2010.Excel;
+using Exam.Application.Interface.Exam;
+using Exam.ExamManages;
+using Exam.Infrastructure.Data.Entity;
+using Exam.Infrastructure.Data.Interface;
+using Exam.Infrastructure.Enums;
+using Exam.Infrastructure.Extensions;
+using Exam.Infrastructure.Validation.Validation;
+using Exam.Infrastructure.Web.Utilities;
+using Exam.Insfrastructure.Service.Service;
+using Exam.Questions;
+using Exam.Repository.Sqlsugar.Repositories;
+using Exam.Share;
+using Exam.Share.Dtos.ExamManage;
+using Exam.Share.ViewResponses.Exam;
+using Exam.TestPapers;
+using Hotline.Application.Exam.Core.Extensions;
+using Hotline.Application.Exam.Extensions;
+using Hotline.Application.Exam.Interface.Strategy;
+using Hotline.Application.Exam.QueryExtensions.ExamManages;
+using Hotline.Application.Exam.Strategy;
+using Hotline.Exams.ExamManages;
+using Hotline.Exams.TestPapers;
+using Hotline.Repository.SqlSugar;
+using Hotline.Repository.SqlSugar.DataPermissions;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.ExamManages;
+using Hotline.Repository.SqlSugar.Extensions;
+using Hotline.Repository.SqlSugar.Interface;
+using Hotline.Settings;
+using Hotline.Share.Dtos.ExamManages;
+using Hotline.Share.Dtos.Org;
+using Hotline.Share.Exams.Extensions;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.ViewResponses;
+using Hotline.Share.ViewResponses.Exam;
+using Hotline.Users;
+using JiebaNet.Segmenter.Common;
+using MapsterMapper;
+using NPOI.SS.Formula.Functions;
+using SqlSugar;
+using System.Linq;
+using System.Threading.Tasks;
+using XF.Domain.Authentications;
+using XF.Domain.Dependency;
+using XF.Domain.Exceptions;
+using XF.Domain.Repository;
+
+namespace Hotline.Application.Exam.Service.ExamManages
+{
+    public class UserExamService : ApiService<UserExam, AddUserExamDto, UpdateUserExamDto, HotlineDbContext>, IUserExamService, IScopeDependency
+    {
+        private readonly IUserExamRepository _repository;
+        private readonly IUserExamItemRepository _userExamItemRepository;
+        private readonly IUserExamItemOptionRepository _userExamItemOptionRepository;
+        private readonly IExamAnswerRepository _examAnswerRepository;
+        private readonly IDataPermissionFilterBuilder _dataPermissionFilterBuilder;
+        private readonly IServiceProvider _serviceProvider;
+        private readonly IMapper _mapper;
+        private readonly ISessionContext _sessionContext
+            ;
+
+        public UserExamService(IUserExamRepository repository,
+            IUserExamItemRepository userExamItemRepository,
+            IUserExamItemOptionRepository userExamItemOptionRepository,
+            IExamAnswerRepository examAnswerRepository,
+            IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider,
+            IMapper mapper, ISessionContext sessionContext) : base(repository, mapper, sessionContext)
+        {
+            this._repository = repository;
+            this._userExamItemRepository = userExamItemRepository;
+            this._userExamItemOptionRepository = userExamItemOptionRepository;
+            this._examAnswerRepository = examAnswerRepository;
+            this._dataPermissionFilterBuilder = dataPermissionFilterBuilder;
+            this._serviceProvider = serviceProvider;
+            this._mapper = mapper;
+            this._sessionContext = sessionContext;
+        }
+
+        #region public method
+        public Task<UserExamDto> GetAsync(EntityQueryRequest entityQueryRequest)
+        {
+            throw new NotImplementedException();
+        }
+        public Task<(int, List<UserExamResultViewResponse>)> GetListAsync(UserExamPagedRequest queryRequest)
+        {
+            throw new NotImplementedException();
+        }
+
+        public async Task<ExamQuestionDto> GetExamQuestionDto(ExamQuestionRequest examQuestionRequest)
+        {
+            var expression = examQuestionRequest.GetExpression();
+            var quesetion = await new ExamRepository<ExamQuestion>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(expression).FirstAsync();
+
+            if (quesetion != null)
+            {
+                var examQuestionDto = _mapper.Map<ExamQuestionDto>(quesetion);
+
+                if (examQuestionDto.QuestionType.CheckSelectType())
+                {
+                    var questionOptions = await new ExamRepository<ExamQuestionOptions>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(x => x.ExamQuestionId == quesetion.Id).ToListAsync();
+
+                    List<UserExamItemOptions> userItemItemOptions = await GetUserExteamItemOptios(quesetion);
+
+                    if (questionOptions != null)
+                    {
+                        examQuestionDto.QuestionOptions = new List<ExamQuestionOptionsDto>();
+
+                        questionOptions.ForEach(item =>
+                        {
+                            var examQuestionOptionsDto = _mapper.Map<ExamQuestionOptionsDto>(item);
+                            if (userItemItemOptions != null)
+                            {
+                                examQuestionOptionsDto.IsSelected = userItemItemOptions.Any(m => m.QuestionOptionId == item.Id);
+                            }
+
+                            examQuestionDto.QuestionOptions.Add(examQuestionOptionsDto);
+                        });
+                    }
+
+                }
+                else
+                {
+                    List<ExamAnswer> examAnswers = await GetExamAnswers(examQuestionRequest);
+
+                    examQuestionDto.Answer = examAnswers != null ? examAnswers.FirstOrDefault()?.Answer : null;
+
+                }
+
+
+                return examQuestionDto;
+            }
+            else
+            {
+                throw new UserFriendlyException(ErrorMessage.ServiceError, string.Format(ErrorMessage.IsNotExists, string.Concat(typeof(Question).GetDescription(), ":", examQuestionRequest.QuestionId)));
+            }
+
+        }
+
+        private async Task<List<ExamAnswer>> GetExamAnswers(ExamQuestionRequest examQuestionRequest)
+        {
+            var examAnswerRepository = new ExamRepository<ExamAnswer>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var userExamItemRepository = new ExamRepository<UserExamItem>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+
+            var userExamItemTable = userExamItemRepository.Queryable().Where(i=> i.QuestionId == examQuestionRequest.QuestionId);
+            var userExamTable = _repository.Queryable().Where(u=> u.UserId == _sessionContext.UserId);
+            var examAnswerTable = examAnswerRepository.Queryable();
+
+            var examAnswers = await examAnswerTable.InnerJoin(userExamItemTable, (e, i) => e.UserExamItemId == i.Id)
+                .InnerJoin(userExamTable, (e, i, u) => i.UserExamId == u.Id)
+                .Select((e, i, u) => e).ToListAsync();
+            return examAnswers;
+        }
+
+        private async Task<List<UserExamItemOptions>> GetUserExteamItemOptios(ExamQuestion quesetion)
+        {
+            var userExamItemOptionsRepository = new ExamRepository<UserExamItemOptions>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+
+            var userExamItemOptionsTable = userExamItemOptionsRepository.Queryable();
+            var userExamItemTable = _userExamItemRepository.Queryable();
+            var userExamTable = _repository.Queryable();
+
+            var userItemItemOptions = await userExamItemOptionsTable
+                .InnerJoin(userExamItemTable, (o, u) => o.UserExamItemId == u.Id)
+                .InnerJoin(userExamTable, (o, u, e) => u.UserExamId == e.Id)
+                .Where((o, u, e) => u.QuestionId == quesetion.QuestionId && e.UserId == _sessionContext.UserId).
+                Select((o, u, e) => o).ToListAsync();
+            return userItemItemOptions;
+        }
+
+        public async Task<List<ExamQuestionViewResponse>> GetExamQuestionViewResponses(ExamQuestionGroupRequest examQuestionGroupRequest)
+        {
+            if (examQuestionGroupRequest.ExamId.IsNull() && examQuestionGroupRequest.UserId.IsNull())
+                throw UserFriendlyException.SameMessage("查询参数不能为空");
+
+            var expression = examQuestionGroupRequest.GetExpression();
+            var examManageTable = new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(expression);
+            var testPaperItemTable = new ExamRepository<ExamQuestion>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
+
+            var queryable = await examManageTable.InnerJoin(testPaperItemTable, (e, i) => e.Id == i.ExamId)
+                .Select((e, i) => i).ToListAsync();
+
+            var result = queryable.GroupBy(x => x.QuestionType).Select(m => new ExamQuestionViewResponse
+            {
+                QuestionType = m.Key,
+                Questions = m.Select(n => new SimpleViewResponse
+                {
+                    Id = n.QuestionId
+                }).ToList()
+            }).ToList();
+
+            return result;
+
+        }
+
+
+        public async Task<List<GradingQuestionViewResponce>> GetGradingQuestionViewResponces(ExamQuestionGroupRequest examQuestionGroupRequest)
+        {
+            var expression = examQuestionGroupRequest.GetExpression();
+            var examManageTable = new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(expression);
+            var testPaperItemTable = new ExamRepository<ExamQuestion>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
+
+            var queryable = await examManageTable.InnerJoin(testPaperItemTable, (e, i) => e.Id == i.ExamId)
+                .Select((e, i) => i).ToListAsync();
+
+            var examQuestionScores = await new ExamRepository<ExamQuestionScore>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(x => x.ExamManageId == examQuestionGroupRequest.ExamId).ToListAsync();
+
+            var userExamItemTable = _userExamItemRepository.Queryable();
+            var userExamTable = _repository.Queryable().Where(x => x.ExamId == examQuestionGroupRequest.ExamId && x.UserId == examQuestionGroupRequest.UserId);
+
+            var userExamItems = await userExamItemTable.InnerJoin(userExamTable, (i, u) => i.UserExamId == u.Id)
+                .Select((i, u) => i).ToListAsync();
+
+            var result = queryable.GroupBy(x => x.QuestionType).Select(m => new GradingQuestionViewResponce
+            {
+                QuestionType = m.Key,
+                Questions = m.Select(n => new GradingExamViewResponse
+                {
+                    IsCorrect = CheckCorrect(n, examQuestionScores, userExamItems),
+                    Id = userExamItems.FirstOrDefault(x => x.QuestionId == n.QuestionId)?.Id ?? n.Id
+                }).ToList()
+            }).ToList();
+
+            return result;
+        }
+
+        private bool CheckCorrect(ExamQuestion n, List<ExamQuestionScore> examQuestionScores, List<UserExamItem> userExamItems)
+        {
+            var examQuestionScore = examQuestionScores.FirstOrDefault(x => x.QuestionType == n.QuestionType);
+            var userItem = userExamItems.FirstOrDefault(x => x.QuestionId == n.QuestionId);
+
+            if (userItem != null && examQuestionScore != null)
+            {
+                return userItem.Score == examQuestionScore.Score;
+            }
+
+            return false;
+        }
+
+        public async Task<PageViewResponse<UserExamResultViewResponse>> GetPagedListAsync(UserExamPagedRequest queryRequest)
+        {
+            SqlSugar.ISugarQueryable<UserExamResultViewResponse> queryable = GetQueryable(queryRequest);
+
+            var list = await queryable.ToPageListAsync(queryRequest.PageIndex, queryRequest.PageSize);
+            var total = await queryable.CountAsync();
+
+            var result = new UserExamResultPageViewResponse
+            {
+                Items = list,
+                Pagination = new Pagination(queryRequest.PageIndex, queryRequest.PageSize, total)
+            };
+
+            return result;
+        }
+        public async Task<GradingExamQuestionDto> GradingAsync(GradingExtamItemDto gradingExtamItemDto, CancellationToken cancellationToken)
+        {
+            var userExamItem = await _userExamItemRepository.GetAsync(m => m.Id == gradingExtamItemDto.UserExamItemId);
+
+            if (userExamItem != null)
+            {
+                userExamItem = _mapper.Map<GradingExtamItemDto, UserExamItem>(gradingExtamItemDto, userExamItem);
+
+                await _userExamItemRepository.ValidateUpdateAsync(userExamItem, cancellationToken);
+            }
+
+            return await GetNextExamQuestion(gradingExtamItemDto);
+        }
+
+        public async Task SubmitAsync(SubmitExamDto submitExamDto, CancellationToken cancellationToken)
+        {
+            var userExam = await _repository.GetAsync(x => x.Id == submitExamDto.Id);
+
+            if (userExam != null)
+            {
+                userExam = _mapper.Map<SubmitExamDto, UserExam>(submitExamDto, userExam);
+
+                await _repository.ValidateUpdateAsync(userExam, cancellationToken);
+            }
+        }
+
+        public async Task<StartExamViewResponse> ExamAsync(UpdateUserExamItemDto addUserExamItemDto, CancellationToken cancellationToken)
+        {
+            var userExam = await new ExamRepository<UserExam>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(x => x.Id == addUserExamItemDto.UserExamId).FirstAsync();
+
+            var startExamViewResponse = await CheckExamValid(userExam);
+
+            if (!startExamViewResponse.CheckValidate())
+            {
+                return startExamViewResponse;
+            }
+
+            var hasUserExamItem = _userExamItemRepository.Queryable().Where(x => x.UserExamId == addUserExamItemDto.UserExamId && x.QuestionId == addUserExamItemDto.QuestionId).Any();
+
+            if (!hasUserExamItem)
+            {
+                await AddExamAsync(_userExamItemRepository, addUserExamItemDto, cancellationToken);
+            }
+            else
+            {
+                await UpdateExamAsync(_userExamItemRepository, addUserExamItemDto, cancellationToken);
+            }
+
+            await CalcuteExamItemScore(_userExamItemRepository, addUserExamItemDto, cancellationToken);
+
+            return startExamViewResponse;
+        }
+        public async Task<StartExamViewResponse> StartUserExamAsync(StartUserExamDto startUserExamDto, CancellationToken cancellationToken)
+        {
+            var userExam = await _repository.GetAsync(x => x.Id == startUserExamDto.Id);
+
+            if (userExam == null) return new StartExamViewResponse
+            {
+                IsJoin = false
+            };
+
+            if (userExam.StartTime == null)
+                userExam.StartTime = DateTime.Now;
+
+            var startExamViewResponse = await CheckExamValid(userExam);
+
+            if (!startExamViewResponse.CheckValidate())
+            {
+                return startExamViewResponse;
+            }
+
+            userExam.ExamStatus = Share.Enums.Exams.EExamStatus.Executing;
+
+            userExam.ToUpdate(_sessionContext);
+
+            await _repository.UpdateWithValidateAsync(userExam, cancellationToken);
+
+            var examManage = await new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider).GetAsync(x => x.Id == userExam.ExamId);
+
+            return new StartExamViewResponse
+            {
+                StartTime = userExam.StartTime,
+                TimeSpan = examManage?.TimeSpan,
+                IsCompleted = false
+            };
+        }
+
+        private async Task<StartExamViewResponse> CheckExamValid(UserExam userExam)
+        {
+            var examManageRepository = new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+
+            var examManage = await examManageRepository.Queryable().Where(x => x.Id == userExam.ExamId).FirstAsync();
+
+            var startExamViewResponse = new StartExamViewResponse
+            {
+                IsJoin = true,
+                IsCompleted = false
+            };
+
+            if (examManage != null)
+            {
+                var examStrategyProxy = new ExamStrategyProxy();
+
+                var examStrategys = new List<IExamStrategy>();
+
+                startExamViewResponse = AddCheckStartTimeStrategy(userExam, examManage, startExamViewResponse, examStrategys);
+                startExamViewResponse = AddCheckEndTimeStrategy(userExam, examManage, startExamViewResponse, examStrategys);
+                startExamViewResponse = AddCheckValidateTimeStrategy(userExam, examManage, startExamViewResponse, examStrategys);
+                startExamViewResponse = AddCheckValidateCountStrategy(userExam, examManage, startExamViewResponse, examStrategys);
+
+                if (!examStrategyProxy.Validate())
+                {
+                    return startExamViewResponse;
+                }
+            }
+
+            startExamViewResponse.IsStart = true;
+
+            return startExamViewResponse;
+        }
+
+        private StartExamViewResponse AddCheckValidateCountStrategy(UserExam userExam, ExamManage examManage, StartExamViewResponse startExamViewResponse, List<IExamStrategy> examStrategys)
+        {
+            if (examManage.ExamType == Share.Enums.Exams.EExamType.Simulate)
+            {
+                var checkStartTime = new CheckValidateCountStrategy(1, 1)
+                {
+                    CallBack = () =>
+                    {
+                        startExamViewResponse = new StartExamViewResponse
+                        {
+                            IsJoin = false,
+                            IsStart = false,
+                            IsCompleted = true
+                        };
+                    }
+                };
+                examStrategys.Add(checkStartTime);
+                return startExamViewResponse;
+            }
+            else
+            {
+                var count = new ExamRepository<UserExam>(_uow, _dataPermissionFilterBuilder, _serviceProvider).CountAsync(x => x.ExamId == examManage.Id && x.UserId == userExam.UserId).Result;
+
+                var checkStartTime = new CheckValidateCountStrategy(examManage.Count, count)
+                {
+                    CallBack = () =>
+                    {
+                        startExamViewResponse = new StartExamViewResponse
+                        {
+                            IsJoin = false,
+                            IsStart = false,
+                            IsCompleted = true
+                        };
+                    }
+                };
+                examStrategys.Add(checkStartTime);
+                return startExamViewResponse;
+            }
+
+        }
+
+        private StartExamViewResponse AddCheckValidateTimeStrategy(UserExam userExam, ExamManage examManage, StartExamViewResponse startExamViewResponse, List<IExamStrategy> examStrategys)
+        {
+            if (examManage.ExamType == Share.Enums.Exams.EExamType.Simulate) return startExamViewResponse;
+
+            var checkStartTime = new CheckValidateTimeStrategy(examManage.TimeSpan, userExam.StartTime)
+            {
+                CallBack = () =>
+                {
+                    startExamViewResponse = new StartExamViewResponse
+                    {
+                        IsJoin = false,
+                        IsStart = false,
+                        IsCompleted = true
+                    };
+                }
+            };
+            examStrategys.Add(checkStartTime);
+            return startExamViewResponse;
+        }
+
+        private StartExamViewResponse AddCheckEndTimeStrategy(UserExam userExam, ExamManage examManage, StartExamViewResponse startExamViewResponse, List<IExamStrategy> examStrategys)
+        {
+            if (examManage.ExamType == Share.Enums.Exams.EExamType.Simulate) return startExamViewResponse;
+
+            var checkStartTime = new CheckEndTimeStrategy(examManage.EndTime, DateTime.Now)
+            {
+                CallBack = () =>
+                {
+                    startExamViewResponse = new StartExamViewResponse
+                    {
+                        IsJoin = false,
+                        IsStart = false,
+                        IsCompleted = true
+                    };
+                }
+            };
+            examStrategys.Add(checkStartTime);
+            return startExamViewResponse;
+        }
+
+        private static StartExamViewResponse AddCheckStartTimeStrategy(UserExam userExam, ExamManage examManage, StartExamViewResponse startExamViewResponse, List<IExamStrategy> examStrategys)
+        {
+            if (examManage.ExamType == Share.Enums.Exams.EExamType.Simulate) return startExamViewResponse;
+
+            var checkStartTime = new CheckStartTimeStrategy(examManage.StartTime, userExam.StartTime)
+            {
+                CallBack = () =>
+                {
+                    startExamViewResponse = new StartExamViewResponse
+                    {
+                        IsJoin = false,
+                        IsStart = false,
+                        IsCompleted = false
+                    };
+                }
+            };
+            examStrategys.Add(checkStartTime);
+            return startExamViewResponse;
+        }
+
+        public async Task CompleteGradingAsync(GradingExamDto gradingExtamDto, CancellationToken cancellationToken)
+        {
+            var userExam = await _repository.GetAsync(x => x.Id == gradingExtamDto.Id);
+
+            if (userExam == null) return;
+
+            var userExamItems = await _userExamItemRepository.Queryable().Where(x => x.UserExamId == gradingExtamDto.Id).ToListAsync();
+
+            if (userExamItems != null)
+            {
+                var totalScore = userExamItems.Sum(x => x.Score);
+                userExam.Score = totalScore;
+
+                var examManage = await new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider).GetAsync(x => x.Id == userExam.ExamId);
+                userExam.IsSuccess = totalScore >= examManage.CutoffScore;
+
+                userExam.ExamStatus = Share.Enums.Exams.EExamStatus.Complete;
+
+                userExam.ToUpdate(_sessionContext);
+
+                await _repository.ValidateUpdateAsync(userExam, cancellationToken);
+            }
+        }
+
+        public async Task<List<GradingExamQuestionDto>> GetGradingExamQuestion(GradingExamRequest gradingExamRequest)
+        {
+            var expression = gradingExamRequest.GetExpression();
+            var userExamTable = _repository.Queryable().Where(expression);
+
+            var questionScoreRepository = new ExamRepository<ExamQuestionScore>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+
+            var userExamItemTable = _userExamItemRepository.Queryable();
+            var userExamItemOptionTable = _userExamItemOptionRepository.Queryable();
+            var examAnswerTable = new ExamRepository<ExamAnswer>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
+            var questionTable = new ExamRepository<ExamQuestion>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
+            var quesitonOptionTable = new ExamRepository<ExamQuestionOptions>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
+            var testPaperItemAnswerTable = new ExamRepository<ExamQuestionAnswer>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
+            var questionScoreTable = questionScoreRepository.Queryable();
+
+            var queryResult = await userExamTable.InnerJoin(userExamItemTable, (e, i) => e.Id == i.UserExamId)
+                .InnerJoin(questionTable, (e, i, q) => i.QuestionId == q.Id)
+                .LeftJoin(userExamItemOptionTable, (e, i, q, o) => i.Id == o.UserExamItemId)
+                .LeftJoin(quesitonOptionTable, (e, i, q, o, qo) => o.QuestionOptionId == qo.Id)
+                .LeftJoin(examAnswerTable, (e, i, q, o, qo, a) => i.Id == a.UserExamItemId)
+                .LeftJoin(testPaperItemAnswerTable, (e, i, q, o, qo, a, ta) => ta.QuestionId == qo.QuestionId)
+                .InnerJoin(questionScoreTable, (e, i, q, o, qo, a, ta, s) => q.QuestionType == s.QuestionType && e.ExamId == s.ExamManageId)
+                .Where((e, i, q, o, qo, a, ta, s) => !q.QuestionType.CheckSelectType())
+                .Select(
+                (e, i, q, o, qo, a, ta, s) => new GradingExamQuestionTempDto
+                {
+                    Id = i.Id,
+                    QuestionType = q.QuestionType,
+                    CorrectAnswer = ta != null ? ta.Answer : string.Empty,
+                    Answer = a != null ? a.Answer : string.Empty,
+                    Title = q.Title,
+                    QuestionOptionId = o.Id,
+                    UserExamItemId = i.Id,
+                    Content = qo.Content,
+                    Label = qo.Label,
+                    IsAnswer = qo.IsAnswer,
+                    IsSelected = o != null,
+                    Score = s.Score
+                }
+                ).ToListAsync();
+
+            var gradingExamQuestionDtos = queryResult.GroupBy(x => new
+            {
+                Id = x.Id,
+                QuestionType = x.QuestionType
+            }).Select(g => new GradingExamQuestionDto
+            {
+                Answer = g.FirstOrDefault().Answer,
+                QuestionType = g.Key.QuestionType,
+                Id = g.Key.Id,
+                Score = g.FirstOrDefault().Score,
+                Title = g.FirstOrDefault().Title,
+                CorrectAnswer = g.Key.QuestionType.CheckSelectType() ? string.Join(",", g.Where(i => i.IsAnswer).Select(n => n.Label)) : g.FirstOrDefault()?.CorrectAnswer
+            }).ToList();
+
+            return gradingExamQuestionDtos;
+        }
+
+
+        public async Task<GradingExamQuestionDto> ViewGradingExamQuestion(ViewGradingExamRequest viewGradingExamRequest)
+        {
+            var gradingExtamItemDto = _mapper.Map<ViewGradingExamRequest, GradingExtamItemDto>(viewGradingExamRequest);
+
+            return await GetNextExamQuestion(gradingExtamItemDto);
+        }
+
+
+        public async Task<UnExamUserPageViewResponse> GetUnExamUsers(UnExamUserReportPagedRequest unExamUserReportPagedRequest)
+        {
+            unExamUserReportPagedRequest.ResoleEndTime();
+
+            var examManageRepository = new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var userRepository = new ExamRepository<User>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+
+            var expression = unExamUserReportPagedRequest.GetExpression();
+            var userExamExpression = unExamUserReportPagedRequest.GetUserExamExpression();
+            var examManageTable = examManageRepository.Queryable().Where(expression);
+            var userTable = userRepository.Queryable();
+            var userExamTable = _repository.Queryable().Where(userExamExpression);
+
+            var queryResult = userExamTable.InnerJoin(examManageTable, (ue, e) => ue.ExamId == e.Id)
+                .InnerJoin(userTable, (ue, e, u) => ue.UserId == u.Id)
+                .Select((ue, e, u) => new UnExamUserViewResponse
+                {
+                    ExamName = e.Name,
+                    OrgName = u.Organization.Name,
+                    UserName = u.Name
+                });
+
+            var total = await queryResult.CountAsync();
+            var items = await queryResult.ToPageListAsync(unExamUserReportPagedRequest.PageIndex, unExamUserReportPagedRequest.PageSize);
+
+            return new UnExamUserPageViewResponse
+            {
+                Items = items,
+                Pagination = new Pagination(unExamUserReportPagedRequest.PageIndex, unExamUserReportPagedRequest.PageSize, total)
+            };
+
+        }
+
+        public async Task<UserExamResultPageViewResponse> GetUserExamResults(UserExamResultReportPagedRequest userExamResultReportPagedRequest)
+        {
+            userExamResultReportPagedRequest.ResoleEndTime();
+
+            var examManageRepository = new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var userRepository = new ExamRepository<User>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+
+            var expression = userExamResultReportPagedRequest.GetExpression();
+            var userExamExpression = userExamResultReportPagedRequest.GetUserExamExpression();
+            var examManageTable = examManageRepository.Queryable().Where(expression);
+            var userTable = userRepository.Queryable();
+            var userExamTable = _repository.Queryable().Where(userExamExpression);
+
+            var queryResult = userExamTable.InnerJoin(examManageTable, (ue, e) => ue.ExamId == e.Id)
+                .InnerJoin(userTable, (ue, e, u) => ue.UserId == u.Id)
+                .Select((ue, e, u) => new UserExamResultViewResponse
+                {
+                    ExamName = e.Name,
+                    OrgName = u.Organization.Name,
+                    UserName = u.Name,
+                    TotalScore = e.TotalScore,
+                    CutoffScore = e.CutoffScore,
+                    Score = ue.Score ?? 0,
+
+                });
+
+            var total = await queryResult.CountAsync();
+            var items = await queryResult.ToPageListAsync(userExamResultReportPagedRequest.PageIndex, userExamResultReportPagedRequest.PageSize);
+
+            return new UserExamResultPageViewResponse
+            {
+                Items = items,
+                Pagination = new Pagination(userExamResultReportPagedRequest.PageIndex, userExamResultReportPagedRequest.PageSize, total)
+            };
+        }
+        #endregion
+
+        #region private method
+
+
+
+        private async Task CalcuteExamItemScore(IUserExamItemRepository userExamRepository, UpdateUserExamItemDto addUserExamItemDto, CancellationToken cancellationToken)
+        {
+            if (!addUserExamItemDto.QuestionType.CheckSelectType()) return;
+
+            var testPaperItemOptionsRepository = new ExamRepository<ExamQuestionOptions>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var examManageRepository = new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var testPaperItemRepository = new ExamRepository<ExamQuestion>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var examQuestionScoreRepository = new ExamRepository<ExamQuestionScore>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var testPaperOptionsTable = testPaperItemOptionsRepository.Queryable().Where(x => x.QuestionId == addUserExamItemDto.QuestionId && x.IsAnswer);
+            var testPaperItemTable = testPaperItemRepository.Queryable();
+            var userExamTable = _repository.Queryable().Where(x => x.Id == addUserExamItemDto.UserExamId);
+            var examManageTable = examManageRepository.Queryable();
+            var testPaperOptionIds = await testPaperOptionsTable.InnerJoin(testPaperItemTable, (t, i) => t.ExamQuestionId == i.Id)
+                .InnerJoin(examManageTable, (t, i, e) => i.ExamId == e.Id)
+                .InnerJoin(userExamTable, (t, i, e, u) => e.Id == u.ExamId)
+                .Select((t, i, e, u) => t.Id).ToListAsync();
+
+            var isCorrect = addUserExamItemDto.UserExamItemOptionDtos.Select(x => x.QuestionOptionId).OrderBy(x => x).SequenceEqual(testPaperOptionIds.OrderBy(x => x));
+
+            var userExamItem = await userExamRepository.GetAsync(x => x.UserExamId == addUserExamItemDto.UserExamId && x.QuestionId == addUserExamItemDto.QuestionId);
+            var examQuesiontScores = await examQuestionScoreRepository.Queryable().Where(x => x.QuestionType == addUserExamItemDto.QuestionType)
+                .InnerJoin(userExamTable, (e, u) => e.Id == u.ExamId)
+                .Select((e, u) => e).ToListAsync();
+            userExamItem.IsCheck = true;
+            userExamItem.Score = isCorrect ? examQuesiontScores.FirstOrDefault()?.Score : 0;
+            userExamItem.ToUpdate(_sessionContext);
+            await userExamRepository.UpdateWithValidateAsync(userExamItem, cancellationToken);
+        }
+
+        private async Task AddExamAsync(IRepository<UserExamItem> userExamItemRepository, AddUserExamItemDto addUserExamItemDto, CancellationToken cancellationToken)
+        {
+            var userExamItem = await AddUserExamItem(addUserExamItemDto, cancellationToken);
+
+            userExamItem.UserExamItemOptionses = await AddUserExamItemOptions(addUserExamItemDto, cancellationToken);
+
+            userExamItem.ExamAnswers = await AddExamAnswer(addUserExamItemDto, cancellationToken);
+
+            await userExamItemRepository.AddNav(userExamItem)
+                .Include(x => x.UserExamItemOptionses)
+                .Include(x => x.ExamAnswers)
+                .ExecuteCommandAsync();
+        }
+
+        private async Task UpdateExamAsync(IRepository<UserExamItem> userExamItemRepository, UpdateUserExamItemDto updateUserExamItemDto, CancellationToken cancellationToken)
+        {
+            var userExamItem = await UpdateUserExamItem(updateUserExamItemDto, cancellationToken);
+
+            userExamItem.UserExamItemOptionses = await ModifyUserItemOptions(updateUserExamItemDto, cancellationToken);
+
+            userExamItem.ExamAnswers = await UpdateExamAnswer(updateUserExamItemDto, cancellationToken);
+
+            await userExamItemRepository.UpdateNav(userExamItem)
+              .Include(x => x.UserExamItemOptionses)
+              .Include(x => x.ExamAnswers)
+              .ExecuteCommandAsync();
+        }
+        private async Task<GradingExamQuestionDto> GetNextExamQuestion(GradingExtamItemDto gradingExtamItemDto)
+        {
+            // TODO: 获取未阅卷的第一道题
+            var current = _userExamItemRepository.Queryable().Where(x => x.Id == gradingExtamItemDto.UserExamItemId);
+            var userExamItemTable = _userExamItemRepository.Queryable().Where(x => !x.IsCheck);
+
+            var userExamItem = current.InnerJoin(userExamItemTable, (c, u) => c.UserExamId == u.UserExamId).OrderBy((c, u) => c.SortIndex).First();
+
+            if (userExamItem != null)
+            {
+                var testPaperItem = new ExamRepository<ExamQuestion>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(x => x.Id == userExamItem.Id && !x.QuestionType.CheckSelectType()).First();
+
+                if (testPaperItem == null) return null;
+
+                var gradingExamQuestionDto = new GradingExamQuestionDto();
+
+                gradingExamQuestionDto = _mapper.Map<ExamQuestion, GradingExamQuestionDto>(testPaperItem, gradingExamQuestionDto);
+
+                var examAnswer = new ExamRepository<ExamAnswer>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(x => x.UserExamItemId == userExamItem.Id).First();
+
+                var testPaperItemAnswer = await new ExamRepository<ExamQuestionAnswer>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().FirstAsync(x => x.QuestionId == testPaperItem.QuestionId && x.ExamQuestionId == testPaperItem.Id);
+
+                gradingExamQuestionDto.Answer = examAnswer.Answer ?? string.Empty;
+
+                gradingExamQuestionDto.CorrectAnswer = testPaperItemAnswer != null ? testPaperItemAnswer.Answer ?? string.Empty : string.Empty;
+
+
+                return gradingExamQuestionDto;
+
+            }
+
+            return null;
+        }
+
+        private async Task<List<ExamAnswer>> AddExamAnswer(AddUserExamItemDto addUserExamItemDto, CancellationToken cancellationToken)
+        {
+            if (addUserExamItemDto.QuestionType.CheckSelectType()) return null;
+
+            var examAnswers = new List<ExamAnswer>();
+
+            var examAnswer = new ExamAnswer
+            {
+                UserId = _sessionContext.UserId,
+                Answer = addUserExamItemDto.Answer
+            };
+
+            examAnswer.ToInsert(_sessionContext);
+
+            await _examAnswerRepository.ValidateAddAsync(examAnswer, cancellationToken);
+
+            return examAnswers;
+        }
+
+
+        private async Task<UserExamQuestionDto> GetNextExamQuestion(AddUserExamItemDto addUserExamItemDto)
+        {
+            // TODO: 获取未阅卷的第一道题
+            var testPaperItemRepository = new ExamRepository<ExamQuestion>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var examRepository = new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var userExamTable = _repository.Queryable().Where(x => x.Id == addUserExamItemDto.UserExamId);
+            var userExamItemTable = _userExamItemRepository.Queryable().WhereIF(addUserExamItemDto.QuestionId.IsNotNullOrEmpty(), x => x.QuestionId == addUserExamItemDto.QuestionId);
+            var examTable = examRepository.Queryable();
+            var testPaperItemTable = testPaperItemRepository.Queryable();
+            var current = examTable.InnerJoin(testPaperItemTable, (e, t) => e.Id == t.ExamId)
+                .InnerJoin(userExamTable, (e, t, u) => e.Id == u.ExamId)
+                .InnerJoin(userExamItemTable, (e, t, u, i) => t.QuestionId == i.QuestionId).Select((e, t, u, i) => t);
+            var nextTable = testPaperItemTable.InnerJoin(current, (t, c) => t.SortIndex > c.SortIndex).OrderBy((t, c) => t.SortIndex).Select((t, c) => t);
+
+            var userExamItem = userExamItemTable.InnerJoin(nextTable, (u, n) => u.QuestionId == n.QuestionId).OrderBy((u, n) => u.SortIndex).Select((u, n) => u).First();
+
+            if (userExamItem != null)
+            {
+                var question = testPaperItemRepository.Queryable().Where(x => x.Id == userExamItem.Id).First();
+
+                if (question == null) return null;
+
+                var userExamQuestionDto = new UserExamQuestionDto();
+
+                userExamQuestionDto = _mapper.Map<ExamQuestion, UserExamQuestionDto>(question, userExamQuestionDto);
+
+                if (question.QuestionType.CheckSelectType())
+                {
+                    var userExamItemOptionTable = _userExamItemOptionRepository.Queryable().Where(x => x.UserExamItemId == userExamItem.Id);
+                    var quesitonOptionTable = new ExamRepository<ExamQuestionOptions>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
+
+                    var queryResult = userExamItemOptionTable.InnerJoin(quesitonOptionTable, (u, q) => u.QuestionOptionId == q.Id)
+                        .Select((u, q) => new UserExamItemOptionDto
+                        {
+                            Content = q.Content,
+                            QuestionOptionId = u.QuestionOptionId,
+                            UserExamItemId = u.UserExamItemId
+                        });
+
+                    userExamQuestionDto.UserExamItemOptionDtos = queryResult.ToList();
+                }
+                else
+                {
+                    var examAnswer = new ExamRepository<ExamAnswer>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(x => x.UserExamItemId == userExamItem.Id).First();
+
+                    userExamQuestionDto.Answer = examAnswer.Answer ?? string.Empty;
+                }
+
+                return userExamQuestionDto;
+            }
+            else
+            {
+                return null;
+            }
+
+
+        }
+
+        private async Task<List<ExamAnswer>> UpdateExamAnswer(UpdateUserExamItemDto updateUserExamItemDto, CancellationToken cancellationToken)
+        {
+            var examAnswerTable = _examAnswerRepository.Queryable();
+
+            var userExamItemTable = _userExamItemRepository.Queryable().Where(x => x.Id == updateUserExamItemDto.Id);
+
+            var examAnswer = await examAnswerTable.InnerJoin(userExamItemTable, (e, u) => e.UserExamItemId == u.Id).Select((e, u) => e).FirstAsync();
+
+            if (!updateUserExamItemDto.QuestionType.CheckSelectType())
+            {
+                var examAnswers = new List<ExamAnswer>();
+
+                if (examAnswer != null)
+                {
+                    examAnswer.Answer = updateUserExamItemDto.Answer;
+                    examAnswer.UserId = _sessionContext.UserId;
+                    examAnswer.UserExamItemId = updateUserExamItemDto.Id;
+
+                    examAnswer.ToUpdate(_sessionContext);
+
+                    await _examAnswerRepository.ValidateUpdateAsync(examAnswer, cancellationToken);
+
+                    examAnswers.Add(examAnswer);
+                }
+                else
+                {
+                    examAnswer = new ExamAnswer
+                    {
+                        Answer = updateUserExamItemDto.Answer,
+                        UserId = _sessionContext.UserId,
+                        UserExamItemId = updateUserExamItemDto.Id
+                    };
+                    examAnswer.ToInsert(_sessionContext);
+
+                    await _examAnswerRepository.ValidateAddAsync(examAnswer, cancellationToken);
+
+                    examAnswers.Add(examAnswer);
+                }                
+
+                return examAnswers;
+            }
+
+            return null;
+        }
+
+
+        private async Task<List<UserExamItemOptions>> ModifyUserItemOptions(UpdateUserExamItemDto updateUserExamItemDto, CancellationToken cancellationToken)
+        {
+            if (updateUserExamItemDto.QuestionType.CheckSelectType())
+            {
+
+                var entityQuestionRequest = new EntityQueryRequest
+                {
+                    Expression = ExpressionableUtility.CreateExpression<UserExamItemOptions>()
+              .AndIF(updateUserExamItemDto.Id.IsNotEmpty(), x => x.UserExamItemId == updateUserExamItemDto.Id).ToExpression()
+                };
+
+                await DeleteUserExamItemOptions(entityQuestionRequest, cancellationToken);
+
+                //await UpdateUserItemOptions(updateUserExamItemDto, cancellationToken);
+
+                var addUserExamItemDto = _mapper.Map<AddUserExamItemDto>(updateUserExamItemDto);
+
+                addUserExamItemDto.UserExamItemOptionDtos = new List<AddUserExamItemOptionDto>();
+
+                updateUserExamItemDto.UserExamItemOptionDtos.ForEach(item =>
+                {
+                    addUserExamItemDto.UserExamItemOptionDtos.Add(_mapper.Map<AddUserExamItemOptionDto>(item));
+                });
+
+                return await AddUserExamItemOptions(addUserExamItemDto, cancellationToken);
+            }
+
+            return null;
+        }
+
+        private async Task UpdateUserItemOptions(UpdateUserExamItemDto updateUserExamItemDto, CancellationToken cancellationToken)
+        {
+
+            if (updateUserExamItemDto.QuestionType.CheckSelectType())
+            {
+
+                var userExamItemOptions = await _userExamItemOptionRepository.Queryable().Where(x => x.UserExamItemId == updateUserExamItemDto.Id).ToListAsync();
+
+                var entities = new List<UserExamItemOptions>();
+
+                if (updateUserExamItemDto.UserExamItemOptionDtos != null)
+                {
+                    updateUserExamItemDto.UserExamItemOptionDtos.Where(m => m.OperationStatus == EEOperationStatus.Update).ToList().ForEach(x =>
+                    {
+                        var entity = userExamItemOptions.FirstOrDefault(m => m.Id == x.Id);
+                        if (entity != null)
+                        {
+                            entities.Add(_mapper.Map<UpdateUserExamItemOptionDto, UserExamItemOptions>(x, entity));
+                        }
+
+                    });
+                }
+
+                entities.ToUpdate(_sessionContext);
+
+                await _userExamItemOptionRepository.ValidateUpdateAsync(entities, cancellationToken);
+            }
+
+
+        }
+
+        private async Task<UserExamItem> UpdateUserExamItem(UpdateUserExamItemDto updateUserExamItemDto, CancellationToken cancellationToken)
+        {
+            var userExamItem = await _userExamItemRepository.GetAsync(x => x.UserExamId == updateUserExamItemDto.UserExamId && x.QuestionId == updateUserExamItemDto.QuestionId);
+
+            userExamItem = _mapper.Map<UpdateUserExamItemDto, UserExamItem>(updateUserExamItemDto, userExamItem);
+
+            updateUserExamItemDto.Id = userExamItem.Id;
+
+            userExamItem.ToUpdate(_sessionContext);
+
+            await _userExamItemRepository.ValidateUpdateAsync(userExamItem, cancellationToken);
+
+            if (updateUserExamItemDto.QuestionType.CheckSelectType())
+            {
+                if (updateUserExamItemDto.UserExamItemOptionDtos != null)
+                {
+                    updateUserExamItemDto.UserExamItemOptionDtos.ForEach(x => x.UserExamItemId = updateUserExamItemDto.Id);
+                }
+            }
+
+            return userExamItem;
+        }
+
+
+        private async Task<List<UserExamItemOptions>> AddUserExamItemOptions(AddUserExamItemDto addUserExamItemDto, CancellationToken cancellationToken)
+        {
+            var userExamItemOptions = new List<UserExamItemOptions>();
+
+            if (addUserExamItemDto.QuestionType.CheckSelectType())
+            {
+                if (addUserExamItemDto.UserExamItemOptionDtos != null)
+                {
+                    addUserExamItemDto.UserExamItemOptionDtos.ToList().ForEach(x =>
+                    {
+                        userExamItemOptions.Add(_mapper.Map<UserExamItemOptions>(x));
+                    });
+                }
+            }
+
+            userExamItemOptions.ToInsert(_sessionContext);
+
+            await _userExamItemOptionRepository.ValidateAddAsync(userExamItemOptions, cancellationToken);
+
+            return userExamItemOptions;
+        }
+
+        private async Task<UserExamItem> AddUserExamItem(AddUserExamItemDto addUserExamItemDto, CancellationToken cancellationToken)
+        {
+            var userExamItem = _mapper.Map<UserExamItem>(addUserExamItemDto);
+
+            userExamItem.ToInsert(_sessionContext);
+
+            await _userExamItemRepository.ValidateAddAsync(userExamItem, cancellationToken);
+
+            if (addUserExamItemDto.QuestionType.CheckSelectType())
+            {
+                if (addUserExamItemDto.UserExamItemOptionDtos != null)
+                {
+                    addUserExamItemDto.UserExamItemOptionDtos.ForEach(x => x.UserExamItemId = userExamItem.Id);
+                }
+            }
+
+            return userExamItem;
+        }
+
+        private async Task DeleteUserExamItemOptions(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
+        {
+            await _userExamItemOptionRepository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
+        }
+
+        private SqlSugar.ISugarQueryable<UserExamResultViewResponse> GetQueryable(UserExamPagedRequest queryRequest)
+        {
+            if (_sessionContext.UserId != null)
+            {
+                queryRequest.UserId = _sessionContext.UserId;
+            }
+            var expression = queryRequest.GetExpression();
+            var userExamTable = _repository.Queryable().Where(expression);
+
+            var examManageExpression = queryRequest.GetExamManageExpression();
+            var examManageTable = new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(examManageExpression);
+
+            var queryable = userExamTable.InnerJoin(examManageTable, (u, e) => u.ExamId == e.Id).Select((u, e) => new UserExamResultViewResponse
+            {
+                Id = u.Id,
+                CutoffScore = e.CutoffScore,
+                TotalScore = e.TotalScore,
+                ExamName = e.Name,
+                Score = u.Score ?? 0,
+                Status = u.Status,
+                SortIndex = u.SortIndex,
+                ExamStatus = u.ExamStatus,
+                IsSuccess = u.IsSuccess,
+                EndTime = e.EndTime,
+                StartTime = e.StartTime,
+                TimeSpan = e.TimeSpan,
+                ExamType = e.ExamType,
+                ExamId = e.Id
+            });
+            return queryable;
+        }
+        #endregion
+
+    }
+}

+ 697 - 0
src/Hotline.Application/Exam/Service/Practices/PracticeService.cs

@@ -0,0 +1,697 @@
+using Exam.Application.Interface.Practices;
+using Exam.ExamManages;
+using Exam.Infrastructure.Data.Entity;
+using Exam.Infrastructure.Data.Interface;
+using Exam.Insfrastructure.Service.Service;
+using Exam.Practices;
+using Exam.Questions;
+using Exam.Repository.Sqlsugar;
+using Exam.Repository.Sqlsugar.Repositories;
+using Exam.Share;
+using Exam.Share.ViewResponses.Exam;
+using Exam.Share.ViewResponses.Practices;
+using Exam.TestPapers;
+using Exam.Trains;
+using Hotline.Application.Exam.Core.Constants;
+using Hotline.Application.Exam.Core.Extensions;
+using Hotline.Application.Exam.Core.Utilities;
+using Hotline.Application.Exam.QueryExtensions.Practices;
+using Hotline.Application.Exam.QueryExtensions.TestPapers;
+using Hotline.Exams.Practices;
+using Hotline.Exams.Sourcewares;
+using Hotline.Exams.TestPapers;
+using Hotline.Repository.SqlSugar;
+using Hotline.Repository.SqlSugar.DataPermissions;
+using Hotline.Repository.SqlSugar.Exam.Core.Constants;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.Practices;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.Questions;
+using Hotline.Repository.SqlSugar.Extensions;
+using Hotline.Share.Dtos.Practices;
+using Hotline.Share.Dtos.Questions;
+using Hotline.Share.Dtos.TestPapers;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.Requests.TestPaper;
+using Hotline.Share.Requests.Train;
+using Hotline.Share.ViewResponses;
+using Hotline.Share.ViewResponses.Exam;
+using Hotline.Share.ViewResponses.Practices;
+using MapsterMapper;
+using NPOI.OpenXmlFormats.Dml;
+using NPOI.SS.Formula.Functions;
+using SqlSugar;
+using System.Linq;
+using XF.Domain.Authentications;
+using XF.Domain.Dependency;
+
+namespace Hotline.Application.Exam.Service.Practices
+{
+    public class PracticeService : ApiService<Practice, AddPracticeDto, UpdatePracticeDto, HotlineDbContext>, IPracticeService, IScopeDependency
+    {
+        private readonly IPracticeRepository _repository;
+        private readonly IPracticeRecordRepository _practiceRecordRepository;
+        private readonly IPracticeQuestionOptionsRepository _practiceQuestionOptionsRepository;
+        private readonly IPracticeQuestionRepository _practiceQuestionRepository;
+        private readonly IPracticeQuestionSourcewareRepository _practiceQuestionSourcewareRepository;
+        private readonly IPracticeQuestionKnowladgeRepository _practiceQuestionKnowladgeRepository;
+        private readonly IPracticeResultItemRepository _practiceResultItemRepository;
+        private readonly IPracticeResultRepository _practiceResultRepository;
+        private readonly IPracticeAnswerRepository _practiceAnswerRepository;
+        private readonly IDataPermissionFilterBuilder _dataPermissionFilterBuilder;
+        private readonly IServiceProvider _serviceProvider;
+        private readonly ISessionContext _sessionContext;
+        private readonly IMapper _mapper;
+        public PracticeService(IPracticeRepository repository,
+            IPracticeRecordRepository practiceRecordRepository,
+            IPracticeQuestionOptionsRepository practiceQuestionOptionsRepository,
+            IPracticeQuestionRepository practiceQuestionRepository,
+            IPracticeQuestionSourcewareRepository practiceQuestionSourcewareRepository,
+            IPracticeQuestionKnowladgeRepository practiceQuestionKnowladgeRepository,
+            IPracticeResultItemRepository practiceResultItemRepository,
+            IPracticeResultRepository practiceResultRepository,
+            IPracticeAnswerRepository practiceAnswerRepository,
+            IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider,
+            ISessionContext sessionContext,
+            IMapper mapper) : base(repository, mapper, sessionContext)
+        {
+            this._repository = repository;
+            this._practiceRecordRepository = practiceRecordRepository;
+            this._practiceQuestionOptionsRepository = practiceQuestionOptionsRepository;
+            this._practiceQuestionRepository = practiceQuestionRepository;
+            this._practiceQuestionSourcewareRepository = practiceQuestionSourcewareRepository;
+            this._practiceQuestionKnowladgeRepository = practiceQuestionKnowladgeRepository;
+            this._practiceResultItemRepository = practiceResultItemRepository;
+            this._practiceResultRepository = practiceResultRepository;
+            this._practiceAnswerRepository = practiceAnswerRepository;
+            this._dataPermissionFilterBuilder = dataPermissionFilterBuilder;
+            this._serviceProvider = serviceProvider;
+            this._sessionContext = sessionContext;
+            this._mapper = mapper;
+        }
+
+        #region public method
+        public async Task<PracticeDto> GetAsync(EntityQueryRequest entityQueryRequest)
+        {
+            throw new NotImplementedException();
+        }
+
+        public async Task<(int, List<PracticeViewResponse>)> GetListAsync(PracticePagedRequest queryRequest)
+        {
+            SqlSugar.ISugarQueryable<PracticeViewResponse> queryResult = QueryResult(queryRequest);
+
+            var total = await queryResult.CountAsync();
+            var items = await queryResult.ToListAsync();
+
+            return (total, items);
+        }
+
+        public async Task<PageViewResponse<PracticeViewResponse>> GetPagedListAsync(PracticePagedRequest queryRequest)
+        {
+            SqlSugar.ISugarQueryable<PracticeViewResponse> queryResult = QueryResult(queryRequest);
+
+            var total = await queryResult.CountAsync();
+            var items = await queryResult.ToPageListAsync(queryRequest.PageIndex, queryRequest.PageSize);
+
+            return new PracticePageViewResponse
+            {
+                Items = items,
+                Pagination = new Pagination(queryRequest.PageIndex, queryRequest.PageSize, total)
+            };
+        }
+
+        public override async Task<string> AddAsync(AddPracticeDto actionRequest, CancellationToken cancellationToken)
+        {
+            var questions = await ExactractQuestion(actionRequest);
+            base.StartTran();
+            actionRequest.Code = await GenerateCode(BusiConstants.PracticeCode,3);
+            var id = await base.AddAsync(actionRequest, cancellationToken);
+            var addPracticeQuestionDtos = new List<AddPracticeQuestionDto>();
+            questions.ForEach(item =>
+            {
+                var addPracticeQuestionDto = _mapper.Map<AddPracticeQuestionDto>(item);
+                addPracticeQuestionDto.PracticeId = id;
+                addPracticeQuestionDto.QuestionId = item.Id;
+                addPracticeQuestionDtos.Add(addPracticeQuestionDto);
+            });
+
+            var practiceRecord = await AddPracticeRecord(id, cancellationToken);
+
+            base.Entity.PracticeRecords = new List<PracticeRecord>();
+                base.Entity.PracticeRecords.Add(practiceRecord); 
+
+            base.Entity.PracticeQuestions = await AddPracticeQuestions(addPracticeQuestionDtos, cancellationToken);
+
+            var practiceQuestionOptions = await AddPracticeQuestionOptions(addPracticeQuestionDtos, cancellationToken);
+
+            var practiceQuestionKnowladges = await AddPracticeQuestionKnowladgeAsync(addPracticeQuestionDtos, cancellationToken);
+
+            var practiceQuestionSourcewares = await AddPracticeQuestionSourcewareAsync(addPracticeQuestionDtos, cancellationToken);
+
+            base.Entity.PracticeQuestions.ForEach(item =>
+            {
+                item.PracticeQuestionKnowladges = practiceQuestionKnowladges.Where(x => item.Id == x.PracticeQuestionId).ToList();
+                item.PracticeQuestionOptionses = practiceQuestionOptions.Where(x => item.Id == x.PracticeQuestionId).ToList();
+                item.PracticeQuestionSourcewares = practiceQuestionSourcewares.Where(x => item.Id == x.PracticeQuestionId).ToList();
+            });
+
+            await base.Complete(base.Entity, OperationConstant.Create);
+
+            return id;
+        }
+
+
+        public async Task<List<PracticeQuestionViewResponse>> GetPracticeQuestionViewResponses(PracticeQuestionGroupRequest practiceQuestionGroupRequest)
+        {
+            var expression = practiceQuestionGroupRequest.GetExpression();
+            var practiceQuestionTable = _practiceQuestionRepository.Queryable().Where(expression);
+
+            var queryable = await practiceQuestionTable.ToListAsync();
+
+            var result = queryable.GroupBy(x => x.QuestionType).Select(m => new PracticeQuestionViewResponse
+            {
+                QuestionType = m.Key,
+                Questions = m.Select(n => new SimpleViewResponse
+                {
+                    Id = n.Id
+                }).ToList()
+            }).ToList();
+
+            return result;
+        }
+
+        public async Task<List<PracticeQuestionViewResponse>> GetViewPracticeQuestions(PracticeQuestionGroupRequest practiceQuestionGroupRequest)
+        {
+            var expression = practiceQuestionGroupRequest.GetExpression();
+            var practiceQuestionTable = _practiceQuestionRepository.Queryable().Where(expression);
+            var practiceResultTable = _practiceResultRepository.Queryable();
+
+            var queryable = await practiceQuestionTable
+                .LeftJoin(practiceResultTable, (q, r) => q.Id == r.PracticeQuestionId)
+                .Select((q, r) => new GradingExamViewResponse
+                {
+                    Id = q.Id,
+                    IsCorrect = r.IsCorrect,
+                    QuestionType = q.QuestionType
+                })
+                .ToListAsync();
+
+            var result = queryable.GroupBy(x => x.QuestionType).Select(m => new PracticeQuestionViewResponse
+            {
+                QuestionType = m.Key,
+                Questions = m.Select(n => new SimpleViewResponse
+                {
+                    Id = n.Id
+                }).ToList()
+            }).ToList();
+
+            return result;
+        }
+
+
+        public async Task<PracticeQuestionDto> GetPracticeQuestion(PracticeQuestionRequest practiceQuestionRequest)
+        {
+            PracticeQuestionDto practiceQuestionDto = await QueryPracticeQuestion(practiceQuestionRequest);
+
+            return practiceQuestionDto;
+        }
+
+
+
+        public async Task<ViewPracticeQuestionDto> ViewPracticeQuestion(PracticeQuestionRequest practiceQuestionRequest)
+        {
+            var practiceQuestionDto = await QueryPracticeQuestion(practiceQuestionRequest);
+
+            var viewPracticeQuestionDto = _mapper.Map<ViewPracticeQuestionDto>(practiceQuestionDto);
+
+            if (viewPracticeQuestionDto != null)
+            {
+                var practiceResultItemTable = _practiceResultItemRepository.Queryable();
+                var practiceResultTable = _practiceResultRepository.Queryable().Where(x => x.PracticeQuestionId == practiceQuestionRequest.PracticeQuestionId);
+
+                var practiceResultItems = await practiceResultTable.InnerJoin(practiceResultItemTable, (r, i) => r.Id == i.PracticeResultId).Select((r, i) => i).ToListAsync();
+
+                var practiceAnswers = await _practiceAnswerRepository.Queryable().Where(x => x.PracticeQuestionId == practiceQuestionRequest.PracticeQuestionId).ToListAsync();
+
+                viewPracticeQuestionDto.PracticeQuestionOptionsDtos.ForEach(item =>
+                {
+                    item.IsSelected = practiceResultItems.Any(x => x.QuestionOptionId == item.QuestionOptionId);
+
+                    item.IsAnswer = practiceAnswers.Any(x => x.PracticeOptionId == item.QuestionOptionId);
+                });
+
+                viewPracticeQuestionDto.Answer = string.Join(",", viewPracticeQuestionDto.PracticeQuestionOptionsDtos.Where(x => x.IsAnswer).Select(m => m.Label));
+            }
+
+            return viewPracticeQuestionDto;
+        }
+
+        public async Task<PracticeQuestionDto> Practice(SubmitPracticeDto submitPracticeDto, CancellationToken cancellationToken)
+        {
+            var current = await _practiceQuestionRepository.GetAsync(submitPracticeDto.PracticeQuestionId);
+
+            var practiceResult = await AddPracticeResult(submitPracticeDto, cancellationToken);
+
+            practiceResult.PracticeResultItems = await AddPracticeResultItem(submitPracticeDto, cancellationToken);
+
+            await _practiceResultRepository.AddNav(practiceResult).Include(x => x.PracticeResultItems).ExecuteCommandAsync();
+
+            var nextId = await _practiceQuestionRepository.Queryable().Where(x => x.PracticeId == current.PracticeId && x.SortIndex > current.SortIndex).OrderBy(x => x.SortIndex).Select(x => x.Id).FirstAsync();
+
+            var practiceQuestionRequest = new PracticeQuestionRequest
+            {
+                PracticeQuestionId = nextId
+            };
+
+            return await QueryPracticeQuestion(practiceQuestionRequest);
+        }
+
+        private async Task<PracticeResult> AddPracticeResult(SubmitPracticeDto submitPracticeDto, CancellationToken cancellationToken)
+        {
+            var practiceQuetionOptions = await _practiceQuestionOptionsRepository.Queryable().Where(x => x.PracticeQuestionId == submitPracticeDto.PracticeQuestionId && x.IsAnswer).ToListAsync();
+
+            var practiceRecordTable = _practiceRecordRepository.Queryable();
+            var practiceQuestionTable = _practiceQuestionRepository.Queryable().Where(x => x.Id == submitPracticeDto.PracticeQuestionId);
+
+            var practiceRecordId = await practiceRecordTable.InnerJoin(practiceQuestionTable, (p, q) => p.PracticeId == q.PracticeId)
+                                .Select((p, q) => p.Id).FirstAsync();
+
+
+            var practiceResult = new PracticeResult
+            {
+                PracticeRecordId = practiceRecordId,
+                PracticeQuestionId = submitPracticeDto.PracticeQuestionId,
+                IsCorrect = submitPracticeDto.PracticeResultItemDtos.Select(x => x.QuestionOptionId).All(m => practiceQuetionOptions.Any(t => t.QuestionOptionId == m))
+            };
+
+            practiceResult.ToInsert(_sessionContext);
+
+            await _practiceResultRepository.ValidateAddAsync(practiceResult, cancellationToken);
+
+            submitPracticeDto.PracticeResultId = practiceResult.Id;
+
+            return practiceResult;
+        }
+
+        public async Task Complete(CompletePracticeRecordDto completePracticeRecordDto, CancellationToken cancellationToken)
+        {
+            var practiceRecord = await _practiceRecordRepository.Queryable().Where(x => x.PracticeId == completePracticeRecordDto.PracticeId && x.UserId == _sessionContext.UserId).FirstAsync();
+
+            practiceRecord.IsComplete = true;
+
+            practiceRecord.ToUpdate(_sessionContext);
+
+            await _practiceRecordRepository.ValidateUpdateAsync(practiceRecord, cancellationToken);
+        }
+        #endregion
+
+        #region private method
+
+        private async Task<List<PracticeResultItem>> AddPracticeResultItem(SubmitPracticeDto submitPracticeDto, CancellationToken cancellationToken)
+        {
+            if (submitPracticeDto.PracticeResultItemDtos == null) return null;
+
+            var practiceResultItems = new List<PracticeResultItem>();
+
+            submitPracticeDto.PracticeResultItemDtos.ForEach(item =>
+            {
+                var practiceResultItem = _mapper.Map<PracticeResultItem>(item);
+                practiceResultItem.PracticeResultId = submitPracticeDto.PracticeResultId;
+                practiceResultItem.ToInsert(_sessionContext);
+                practiceResultItems.Add(practiceResultItem);
+            });
+
+            await _practiceResultItemRepository.ValidateAddAsync(practiceResultItems, cancellationToken);
+
+            return practiceResultItems;
+        }
+        private async Task<PracticeQuestionDto> QueryPracticeQuestion(PracticeQuestionRequest practiceQuestionRequest)
+        {
+            var practiceQuestion = await _practiceQuestionRepository.GetAsync(x => x.Id == practiceQuestionRequest.PracticeQuestionId);
+
+            var practiceQuestionDto = _mapper.Map<PracticeQuestionDto>(practiceQuestion);
+
+            practiceQuestionDto.PracticeQuestionOptionsDtos = await GetPracticeQuestionOptions(practiceQuestionRequest);
+
+            var practiceResultItems = await GetPracticeResultItems(practiceQuestionRequest);
+
+            if (practiceResultItems != null)
+            {
+                practiceQuestionDto.PracticeQuestionOptionsDtos.ForEach(item =>
+                {
+                    item.IsSelected = practiceResultItems.Any(x => x.QuestionOptionId == item.QuestionOptionId);
+                });
+            }
+
+            practiceQuestionDto.PracticeQuestionSourcewareDtos = await GetPracticeQuestionSourceware(practiceQuestionRequest);
+
+            practiceQuestionDto.PracticeQuestionKnowladgeDtos = await GetPracticeQuestionKnowladge(practiceQuestionRequest);
+            return practiceQuestionDto;
+        }
+
+        private async Task<List<PracticeResultItem>> GetPracticeResultItems(PracticeQuestionRequest practiceQuestionRequest)
+        {
+            var practiceRecordRepository = new ExamRepository<PracticeRecord>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var practiceRecordTable = practiceRecordRepository.Queryable().Where(x=>x.UserId == _sessionContext.UserId);
+
+            var practiceResultItems = practiceRecordTable.InnerJoin<PracticeResult>((p, r) => p.Id == r.PracticeRecordId)
+                                      .InnerJoin<PracticeResultItem>((p, r, i) => i.PracticeResultId == r.Id)
+                                      .Where((p, r, i) => r.PracticeQuestionId == practiceQuestionRequest.PracticeQuestionId)
+                                      .Select((p, r, i) => i);
+
+            return await practiceResultItems.ToListAsync();
+        }
+
+        private async Task<List<PracticeQuestionKnowladgeDto>> GetPracticeQuestionKnowladge(PracticeQuestionRequest practiceQuestionRequest)
+        {
+            var knowledgeRepository = new ExamRepository<KnowledgeBase.Knowledge>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+
+            var expression = practiceQuestionRequest.GetExpression();
+            var practiceQuestionTable = _practiceQuestionRepository.Queryable().Where(expression);
+            var practiceQuestionKnowladgeTable = _practiceQuestionKnowladgeRepository.Queryable();
+            var knowlegdeTable = knowledgeRepository.Queryable();
+            var queryResult = practiceQuestionTable.InnerJoin(practiceQuestionKnowladgeTable, (t, ts) => t.QuestionId == ts.QuestionId && t.Id == ts.PracticeQuestionId)
+                .InnerJoin(knowlegdeTable, (t, ts, sw) => ts.KnowladgeId == sw.Id)
+                .Select((t, ts, sw) => new PracticeQuestionKnowladgeDto
+                {
+                    KnowladgeId = ts.KnowladgeId,
+                    QuestionId = ts.QuestionId,
+                    Title = sw.Title,
+                });
+
+
+            return await queryResult.ToListAsync();
+        }
+
+        private async Task<List<PracticeQuestionSourcewareDto>> GetPracticeQuestionSourceware(PracticeQuestionRequest practiceQuestionRequest)
+        {
+
+            var sourcewareRepository = new ExamRepository<Sourceware>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+
+            var expression = practiceQuestionRequest.GetExpression();
+            var practiceQuestionTable = _practiceQuestionRepository.Queryable().Where(expression);
+            var practiceQuestionSourcewareTable = _practiceQuestionSourcewareRepository.Queryable();
+            var sourcewareTable = sourcewareRepository.Queryable();
+            var queryResult = practiceQuestionTable.InnerJoin(practiceQuestionSourcewareTable, (t, ts) => t.QuestionId == ts.QuestionId && t.Id == ts.PracticeQuestionId)
+                .InnerJoin(sourcewareTable, (t, ts, sw) => ts.SourcewareId == sw.Id)
+                .Select((t, ts, sw) => new PracticeQuestionSourcewareDto
+                {
+                    AttachmentId = sw.AttachmentId,
+                    SourcewareId = ts.SourcewareId,
+                    QuestionId = ts.QuestionId,
+                    Name = sw.Name,
+                });
+
+
+            return await queryResult.ToListAsync();
+        }
+
+        private async Task<List<PracticeQuestionOptionsDto>> GetPracticeQuestionOptions(PracticeQuestionRequest practiceQuestionRequest)
+        {
+            var expression = practiceQuestionRequest.GetExpression();
+            var practiceQuestionTable = _practiceQuestionRepository.Queryable().Where(expression);
+            var practiceQuestionOptionTable = _practiceQuestionOptionsRepository.Queryable();
+
+            var queryResult = practiceQuestionTable.InnerJoin(practiceQuestionOptionTable, (p, o) => p.QuestionId == o.QuestionId && p.Id == o.PracticeQuestionId)
+                .Select((p, o) => new PracticeQuestionOptionsDto
+                {
+                    Content = o.Content,
+                    Label = o.Label,
+                    PracticeQuestionId = o.PracticeQuestionId,
+                    QuestionOptionId = o.QuestionOptionId
+                });
+
+            return await queryResult.ToListAsync();
+        }
+        private SqlSugar.ISugarQueryable<PracticeViewResponse> QueryResult(PracticePagedRequest queryRequest)
+        {
+            var expression = queryRequest.GetExpression();
+            var practiceTable = _repository.Queryable().Where(expression);
+            var practiceRecordTable = _practiceRecordRepository.Queryable();
+
+            var queryResult = practiceTable.InnerJoin(practiceRecordTable, (p, r) => p.Id == r.PracticeId).Select((p, r) => new PracticeViewResponse
+            {
+                Id = p.Id,
+                Code = p.Code,
+                Count = p.Count,
+                PracticeTime = r.PracticeTime,
+                PracticeType = p.PracticeType
+            });
+            return queryResult;
+        }
+
+        private async Task<List<PracticeQuestionSourceware>> AddPracticeQuestionSourcewareAsync(List<AddPracticeQuestionDto> practiceQuestionDtos, CancellationToken cancellationToken)
+        {
+            var questionSourcewareRepository = new ExamRepository<QuestionSourceware>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+
+            var quesitonIds = practiceQuestionDtos.Select(x => x.QuestionId);
+
+            var questionSourceware = await questionSourcewareRepository.Queryable().Where(x => quesitonIds.Contains(x.QuestionId)).ToListAsync();
+
+            var practiceQuestionSourcewares = new List<PracticeQuestionSourceware>();
+
+            practiceQuestionDtos.Where(x => x.QuestionType.CheckSelectType()).ToList().ForEach(x =>
+            {
+                var practiceQuestion = base.Entity?.PracticeQuestions.FirstOrDefault(n => n.QuestionId == x.QuestionId) ?? null;
+
+                var options = questionSourceware.Where(n => x.QuestionId == n.QuestionId).ToList();
+
+                if (options != null)
+                {
+                    options.ForEach(item =>
+                    {
+                        var practiceQuestionSourceware = _mapper.Map<QuestionSourceware, PracticeQuestionSourceware>(item);
+
+                        practiceQuestionSourceware.PracticeQuestionId = practiceQuestion?.Id;
+
+                        practiceQuestionSourceware.ToInsert(_sessionContext);
+
+                        practiceQuestionSourcewares.Add(practiceQuestionSourceware);
+                    });
+                }
+            });
+
+            await _practiceQuestionSourcewareRepository.ValidateAddAsync(practiceQuestionSourcewares, cancellationToken);
+
+            return practiceQuestionSourcewares;
+        }
+
+        private async Task<List<PracticeQuestionKnowladge>> AddPracticeQuestionKnowladgeAsync(List<AddPracticeQuestionDto> practiceQuestionDtos, CancellationToken cancellationToken)
+        {
+            var questionKnowladgeRepository = new ExamRepository<QuestionKnowladge>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+
+            var quesitonIds = practiceQuestionDtos.Select(x => x.QuestionId);
+
+            var questionKnowladge = await questionKnowladgeRepository.Queryable().Where(x => quesitonIds.Contains(x.QuestionId)).ToListAsync();
+
+            var practiceQuestionKnowladges = new List<PracticeQuestionKnowladge>();
+
+            practiceQuestionDtos.Where(x => x.QuestionType.CheckSelectType()).ToList().ForEach(x =>
+            {
+                var practiceQuestion = base.Entity?.PracticeQuestions.FirstOrDefault(n => n.QuestionId == x.QuestionId) ?? null;
+                var options = questionKnowladge.Where(n => x.QuestionId == n.QuestionId).ToList();
+
+                if (options != null)
+                {
+                    options.ForEach(item =>
+                    {
+                        var practiceQuestionKnowladge = _mapper.Map<QuestionKnowladge, PracticeQuestionKnowladge>(item);
+
+                        practiceQuestionKnowladge.PracticeQuestionId = practiceQuestion?.Id;
+
+                        practiceQuestionKnowladge.ToInsert(_sessionContext);
+
+                        practiceQuestionKnowladges.Add(practiceQuestionKnowladge);
+                    });
+                }
+            });
+
+            await _practiceQuestionKnowladgeRepository.ValidateAddAsync(practiceQuestionKnowladges, cancellationToken);
+
+            return practiceQuestionKnowladges;
+        }
+
+        private async Task<List<PracticeQuestionOptions>> AddPracticeQuestionOptions(List<AddPracticeQuestionDto> practiceQuestionDtos, CancellationToken cancellationToken)
+        {
+            var questionOptionRepository = new ExamRepository<QuestionOptions>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+
+            var quesitonIds = practiceQuestionDtos.Select(x => x.QuestionId);
+
+            var questionOptions = await questionOptionRepository.Queryable().Where(x => quesitonIds.Contains(x.QuestionId)).ToListAsync();
+
+            var practiceQuestionOptions = new List<PracticeQuestionOptions>();
+
+            practiceQuestionDtos.Where(x => x.QuestionType.CheckSelectType()).ToList().ForEach(x =>
+            {
+                var practiceQuestion = base.Entity?.PracticeQuestions.FirstOrDefault(n=> n.QuestionId == x.QuestionId) ?? null;
+                var options = questionOptions.Where(n => x.QuestionId == n.QuestionId).ToList();
+
+                if (options != null)
+                {
+                    options.ForEach(item =>
+                    {
+                        var practiceQuestionOption = _mapper.Map<QuestionOptions, PracticeQuestionOptions>(item);
+
+                        practiceQuestionOption.PracticeQuestionId = practiceQuestion?.Id;
+
+                        practiceQuestionOption.QuestionOptionId = item.Id;
+
+                        practiceQuestionOption.ToInsert(_sessionContext);
+
+                        practiceQuestionOptions.Add(practiceQuestionOption);
+                    });
+                }
+
+            });
+
+            await _practiceQuestionOptionsRepository.ValidateAddAsync(practiceQuestionOptions, cancellationToken);
+
+            return practiceQuestionOptions;
+        }
+
+        private async Task<List<PracticeQuestion>> AddPracticeQuestions(List<AddPracticeQuestionDto> addPracticeQuestionDtos, CancellationToken cancellationToken)
+        {
+            var practiceQuestions = new List<PracticeQuestion>();
+
+            var sortIndex = 0;
+
+            addPracticeQuestionDtos.ForEach(item =>
+            {
+                var practiceQuestion = _mapper.Map<PracticeQuestion>(item);
+                practiceQuestion.SortIndex = sortIndex;
+                practiceQuestions.Add(practiceQuestion);
+                sortIndex++;
+            });
+
+            practiceQuestions.ToInsert(_sessionContext);
+
+            await _practiceQuestionRepository.ValidateAddAsync(practiceQuestions, cancellationToken);
+
+            return practiceQuestions;
+
+        }
+
+        private async Task<PracticeRecord> AddPracticeRecord(string practiceId, CancellationToken cancellationToken)
+        {
+            var practiceRecordDto = new PracticeRecordDto
+            {
+                PracticeId = practiceId,
+                UserId = _sessionContext.UserId,
+                PracticeTime = DateTime.Now
+            };
+
+            var practiceRecord = _mapper.Map<PracticeRecord>(practiceRecordDto);
+
+            practiceRecord.ToInsert(_sessionContext);
+
+            await _practiceRecordRepository.ValidateAddAsync(practiceRecord, cancellationToken);
+
+            return practiceRecord;
+        }
+
+        private async Task<List<Question>> ExactractQuestion(AddPracticeDto actionRequest)
+        {
+            if (actionRequest.PracticeTagDtos == null) return new List<Question>();
+
+            var tagIds = actionRequest.PracticeTagDtos.Select(x => x.TagId).ToList();
+
+            var questionTagRepostiory = new ExamRepository<QuestionTag>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var questionRepository = new ExamRepository<Question>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+
+            var questionTagTable = questionTagRepostiory.Queryable();
+            var questionTable = questionRepository.Queryable();
+
+            // 按照标签获取试题,至少取一道
+            if (actionRequest.Count < actionRequest.PracticeTagDtos.Count)
+            {
+                questionTagTable = questionTagTable.Where(x => tagIds.Contains(x.TagId)).Take(actionRequest.Count);
+            }
+            else
+            {
+                var unionQuestions = new List<ISugarQueryable<QuestionTag>>();
+                var ids = new List<string>();
+                // 保证每个标签至少获取一道题
+                tagIds.ForEach(v =>
+                {
+                    var unionQuestion = questionTagTable;
+                    unionQuestion= unionQuestion.Where(x => x.TagId == v).Take(1);
+                    ids.Add(questionTagRepostiory.Queryable().Where(x => x.TagId == v).Select(x => x.Id).First());
+                    unionQuestions.Add(unionQuestion);
+                    questionTagTable = questionTagRepostiory.Queryable();
+                });
+
+                var mainQuesiton = questionTagTable;
+                mainQuesiton = mainQuesiton.Where(x => tagIds.Contains(x.TagId) && !ids.Contains(x.Id)).Take(actionRequest.Count - tagIds.Count);
+
+                unionQuestions.Add(mainQuesiton);
+
+                questionTagTable = questionTagRepostiory.Queryable();
+                questionTagTable = questionTagRepostiory.UnionAll(unionQuestions.ToArray());
+            }
+
+            var queryResult = questionTagTable.InnerJoin(questionTable, (t, q) => t.QuestionId == q.Id)
+                              .Select((t, q) => q);
+
+            return await queryResult.ToListAsync();
+
+        }
+
+        private async Task<string> GenerateCode(string codePrefix, int length)
+        {
+            var trainPlan = await _repository.Queryable().Where(x => x.CreationTime.Date == DateTime.Now.Date).OrderByDescending(x => x.CreationTime).FirstAsync();
+
+
+            var code = string.Empty;
+            if (trainPlan != null)
+            {
+                code = trainPlan.Code;
+            }
+            code = CodeUtility.GenerateCode(codePrefix, length, code);
+            return code;
+        }
+        #endregion
+
+        #region protect method
+
+        protected override async Task CompleteAdd(Practice entity)
+        {
+            await base.AddNav(entity).Include(x => x.PracticeRecords)
+                  .Include(x => x.PracticeQuestions)
+                  .ThenInclude(x => x.PracticeQuestionKnowladges)
+                  .Include(x => x.PracticeQuestions, new InsertNavOptions
+                  {
+                      OneToManyIfExistsNoInsert = true
+                  })
+                  .ThenInclude(x => x.PracticeQuestionOptionses)
+                  .Include(x => x.PracticeQuestions, new InsertNavOptions
+                  {
+                      OneToManyIfExistsNoInsert = true
+                  })
+                  .ThenInclude(x => x.PracticeQuestionSourcewares).ExecuteCommandAsync();
+        }
+
+        public async Task<TagQuestionCountViewResponse> GetTagQuestionCount(TagQuestionCountForPracticeRequest tagQuestionCountForPracticeRequest)
+        {
+            var expression = tagQuestionCountForPracticeRequest.GetExpression();
+            var questionExpression = tagQuestionCountForPracticeRequest.GetQuestionExpression();
+
+            var tagQuestionRepository = new ExamRepository<QuestionTag>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+            var quesitonRepository = new ExamRepository<Question>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
+
+            var tagQuestionTable = tagQuestionRepository.Queryable().Where(expression);
+            var questionTable = quesitonRepository.Queryable().Where(questionExpression);
+
+            var taqQuestions = await tagQuestionTable.LeftJoin(questionTable, (t, q) => t.QuestionId == q.Id)
+                .Select((t, q) => new TagQuestionCountViewResponse
+                {
+                    TotalCount = SqlFunc.AggregateCount(t.Id)
+                })
+                .ToListAsync();
+
+            return taqQuestions.Count() > 0 ? taqQuestions.FirstOrDefault() : new TagQuestionCountViewResponse
+            {
+                TotalCount = 0
+            };
+        }
+
+        #endregion
+    }
+}

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä