Ver Fonte

更新接口

guqiang há 1 mês atrás
pai
commit
2ae43b3e51
36 ficheiros alterados com 842 adições e 218 exclusões
  1. 20 8
      src/Hotline.Api/Controllers/Exam/ExamManageController.cs
  2. 14 0
      src/Hotline.Api/Controllers/Exam/QuestionController.cs
  3. 12 0
      src/Hotline.Api/Controllers/Exam/TestPaperController.cs
  4. 2 2
      src/Hotline.Api/Controllers/Exam/UserExamController.cs
  5. 2 1
      src/Hotline.Application/Exam/Constants/ApiRoutes/ExamManageApiRoute.cs
  6. 9 0
      src/Hotline.Application/Exam/Constants/ApiRoutes/GenerateApiRoute.cs
  7. 3 1
      src/Hotline.Application/Exam/Constants/ApiRoutes/TestPaperApiRoute.cs
  8. 3 1
      src/Hotline.Application/Exam/Constants/Messages/SystemConstants.cs
  9. 9 0
      src/Hotline.Application/Exam/Interface/ExamManages/IExamManageService.cs
  10. 8 0
      src/Hotline.Application/Exam/Interface/Questions/IQuestionService.cs
  11. 4 0
      src/Hotline.Application/Exam/Interface/TestPapers/ITestPaperService.cs
  12. 7 0
      src/Hotline.Application/Exam/Interface/Train/ITrainRecordService.cs
  13. 214 0
      src/Hotline.Application/Exam/Proxy/TestPaperProxy.cs
  14. 13 0
      src/Hotline.Application/Exam/QueryExtensions/ExamManages/ExamManageQueryExtensions.cs
  15. 1 1
      src/Hotline.Application/Exam/QueryExtensions/TestPapers/TestPaperQueryExtensions.cs
  16. 12 0
      src/Hotline.Application/Exam/QueryExtensions/Trains/TrainPlanQueryExtensions.cs
  17. 102 1
      src/Hotline.Application/Exam/Service/ExamManages/ExamManageService.cs
  18. 2 10
      src/Hotline.Application/Exam/Service/Practices/PracticeService.cs
  19. 193 16
      src/Hotline.Application/Exam/Service/Questions/QuestionService.cs
  20. 88 155
      src/Hotline.Application/Exam/Service/TestPapers/TestPaperService.cs
  21. 8 0
      src/Hotline.Application/Exam/Service/Trains/TrainRecordService.cs
  22. 4 14
      src/Hotline.Application/Exam/Service/Trains/TrainTemplateService.cs
  23. 1 1
      src/Hotline.Repository.SqlSugar/Exam/Validators/ExamManages/ExtractRuleValidator.cs
  24. 7 0
      src/Hotline.Share/Dtos/Questions/QuestionOptionsDto.cs
  25. 3 0
      src/Hotline.Share/Dtos/TestPapers/TestPaperDto.cs
  26. 5 0
      src/Hotline.Share/Dtos/TestPapers/TestPaperItemDto.cs
  27. 1 1
      src/Hotline.Share/Enums/Exams/EExamMode.cs
  28. 9 0
      src/Hotline.Share/Requests/Exam/GenerateTestPaperRequest.cs
  29. 1 1
      src/Hotline.Share/Requests/TestPaper/TestPaperPagedRequest.cs
  30. 1 1
      src/Hotline.Share/Requests/TestPaper/TestPaperQuestionCountRequest.cs
  31. 34 0
      src/Hotline.Share/Requests/TestPaper/TestPaperQuestionRequest.cs
  32. 35 1
      src/Hotline.Share/Tools/StringExtensions.cs
  33. 7 0
      src/Hotline/Exams/ExamManages/ExamManage.cs
  34. 1 1
      src/Hotline/Exams/ExamManages/ExtractRule.cs
  35. 2 2
      src/Hotline/Exams/TestPapers/TestPaper.cs
  36. 5 0
      src/Hotline/Tools/ExcelHelper.cs

+ 20 - 8
src/Hotline.Api/Controllers/Exam/ExamManageController.cs

@@ -4,16 +4,17 @@ using Exam.Share.ViewResponses.Exam;
 using Hotline.Application.Exam.Constants.ApiRoutes;
 using Hotline.Application.Exam.Interface.ExamManages;
 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 _questionService;
-        public ExamManageController(IExamManageService questionService)
+        private readonly IExamManageService _examManageService;
+        public ExamManageController(IExamManageService examManageService)
         {
-            _questionService = questionService;
+            _examManageService = examManageService;
         }
 
         /// <summary>
@@ -24,7 +25,7 @@ namespace Hotline.Api.Controllers.Exam
         [HttpPost(ExamManageApiRoute.Add)]
         public async Task Add([FromBody] AddExamManageDto questionDto)
         {
-            await _questionService.AddAsync(questionDto, HttpContext.RequestAborted);
+            await _examManageService.AddAsync(questionDto, HttpContext.RequestAborted);
         }
 
         /// <summary>
@@ -35,7 +36,7 @@ namespace Hotline.Api.Controllers.Exam
         [HttpPut(ExamManageApiRoute.Update)]
         public async Task Update([FromBody] UpdateExamManageDto questionDto)
         {
-            await _questionService.UpdateAsync(questionDto, HttpContext.RequestAborted);
+            await _examManageService.UpdateAsync(questionDto, HttpContext.RequestAborted);
         }
 
         /// <summary>
@@ -46,7 +47,7 @@ namespace Hotline.Api.Controllers.Exam
         [HttpDelete(ExamManageApiRoute.Delete)]
         public async Task Delete([FromBody] EntityQueryRequest entityQueryRequest)
         {
-            await _questionService.DeleteAsync(entityQueryRequest, HttpContext.RequestAborted);
+            await _examManageService.DeleteAsync(entityQueryRequest, HttpContext.RequestAborted);
         }
 
         /// <summary>
@@ -57,7 +58,7 @@ namespace Hotline.Api.Controllers.Exam
         [HttpPost(ExamManageApiRoute.GetPagedList)]
         public async Task<ExamManagePageViewResponse> GetPagedList([FromBody] ExamManagePagedRequest questionPagedRequest)
         {
-            var questionPageViewResponse = await _questionService.GetPagedListAsync(questionPagedRequest);
+            var questionPageViewResponse = await _examManageService.GetPagedListAsync(questionPagedRequest);
 
             return questionPageViewResponse as ExamManagePageViewResponse;
         }
@@ -70,12 +71,23 @@ namespace Hotline.Api.Controllers.Exam
         [HttpGet(ExamManageApiRoute.Get)]
         public async Task<ExamManageDto> Get(string id)
         {
-            var questionDto = await _questionService.GetAsync(new EntityQueryRequest
+            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);
+        }
     }
 }

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

@@ -5,6 +5,7 @@ using Hotline.Application.Exam.Constants.ApiRoutes;
 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
@@ -95,5 +96,18 @@ namespace Hotline.Api.Controllers.Exam
             return await _questionService.GetListAsync(questionPagedRequest);
         }
 
+
+
+        /// <summary>
+        /// 导入Excel
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost(ExamManageApiRoute.ImportExcel)]
+        [AllowAnonymous]
+        public async Task ImportExcel(IFormFile files)
+        {
+            await _questionService.ImportExcel(files, HttpContext.RequestAborted);
+        }
+
     }
 }

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

@@ -6,6 +6,7 @@ using Exam.Share.ViewResponses.TestPaper;
 using Hotline.Application.Exam.Constants.ApiRoutes;
 using Hotline.Share.Dtos.TestPapers;
 using Hotline.Share.Requests.Exam;
+using Hotline.Share.Requests.TestPaper;
 using Hotline.Share.ViewResponses.Exam;
 using Microsoft.AspNetCore.Mvc;
 
@@ -94,5 +95,16 @@ namespace Hotline.Api.Controllers.Exam
 
             return testPaperQuestionCountViewResponses;
         }
+
+        /// <summary>
+        /// 根据规则获取试题
+        /// </summary>
+        /// <param name="testPaperQuestionRequest"></param>
+        /// <returns></returns>
+        [HttpGet(TestPaperApiRoute.GetQuestions)]
+        public async Task GetQuestions([FromQuery]TestPaperQuestionRequest testPaperQuestionRequest)
+        {
+            await _testPaperService.GetQuestionDtos(testPaperQuestionRequest);
+        }
     }
 }

+ 2 - 2
src/Hotline.Api/Controllers/Exam/UserExamController.cs

@@ -34,7 +34,7 @@ namespace Hotline.Api.Controllers.Exam
         /// <summary>
         /// 开始考试
         /// </summary>
-        /// <param name="addUserExamDto"></param>
+        /// <param name="startUserExamDto"></param>
         /// <returns></returns>
         [HttpPost(UserExamApiRoute.Start)]
         public async Task Start([FromBody] StartUserExamDto startUserExamDto)
@@ -100,7 +100,7 @@ namespace Hotline.Api.Controllers.Exam
         /// <summary>
         /// 阅卷
         /// </summary>
-        /// <param name="gradingExtamDto"></param>
+        /// <param name="gradingExtamItemDto"></param>
         /// <returns></returns>
         [HttpPost(UserExamApiRoute.Grading)]
         public async Task<GradingExamQuestionDto> Grading([FromBody] GradingExtamItemDto gradingExtamItemDto)

+ 2 - 1
src/Hotline.Application/Exam/Constants/ApiRoutes/ExamManageApiRoute.cs

@@ -2,7 +2,8 @@
 
 namespace Hotline.Application.Exam.Constants.ApiRoutes
 {
-    public class ExamManageApiRoute:ApiRoute
+    public class ExamManageApiRoute:GenerateApiRoute
     {
+        public const string ImportExcel = "ImportExcel";
     }
 }

+ 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";
+    }
+}

+ 3 - 1
src/Hotline.Application/Exam/Constants/ApiRoutes/TestPaperApiRoute.cs

@@ -2,8 +2,10 @@
 
 namespace Hotline.Application.Exam.Constants.ApiRoutes
 {
-    public class TestPaperApiRoute:ApiRoute
+    public class TestPaperApiRoute:GenerateApiRoute
     {
         public const string Count = "GetTestPaperQuestionCount";
+
+        public const string GetQuestions = "GetQuestions";
     }
 }

+ 3 - 1
src/Hotline.Application/Exam/Constants/Messages/SystemConstants.cs

@@ -2,6 +2,8 @@
 {
     public class SystemConstants
     {
-        public static string[] Labals = ["A", "B", "C", "D", "E", "F", "G", "H"];
+        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", "答案"];
     }
 }

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

@@ -4,10 +4,19 @@ 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);
+
     }
 }

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

@@ -4,10 +4,18 @@ 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);
     }
 }

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

@@ -2,8 +2,10 @@
 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;
 
 namespace Exam.Application.Interface.TestPapers
@@ -13,5 +15,7 @@ namespace Exam.Application.Interface.TestPapers
         public Task<List<TestPaperQuestionCountViewResponse>> GetTestPaperQuestionCount(TestPaperQuestionCountRequest testPaperQuestionCountRequest);
         
         public Task GenerateTestPaper(GenerateTestPaperRequest generateTestPaperRequest,CancellationToken cancellationToken);
+
+        public Task<List<QuestionDto>> GetQuestionDtos(TestPaperQuestionRequest testPaperQuestionRequest);
     }
 }

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

@@ -44,5 +44,12 @@ namespace Train.Application.Interface.Train
         /// <param name="completeTrainPracticeDto"></param>
         /// <returns></returns>
         Task CompleteTrainKnowladgeAsync(CompleteTrainKnowladgeDto completeTrainPracticeDto, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 统计培训结果
+        /// </summary>
+        /// <param name="trainResultReportPagedRequest"></param>
+        /// <returns></returns>
+        Task<List<TrainResultViewResponse>> AnalysisTrainResult(TrainResultReportPagedRequest trainResultReportPagedRequest);
     }
 }

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

@@ -0,0 +1,214 @@
+using Exam.Questions;
+using Exam.Repository.Sqlsugar.Repositories;
+using Exam.TestPapers;
+using Hotline.Exams.TestPapers;
+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 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;
+
+        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;
+        }
+
+        #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();
+        }
+        #endregion
+
+        #region private method
+
+
+        private async Task<List<TestPaperItemAnswer>> SyncQuestionAnswer(List<TestPaperItem> testPaperItems, CancellationToken cancellationToken)
+        {
+            var questionAnswersRepository = new ExamRepository<QuestionAnswer>(_repository.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.QueswerAnswerId = 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>(_repository.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>(_repository.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>(_repository.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
+    }
+}

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

@@ -21,5 +21,18 @@ namespace Hotline.Application.Exam.QueryExtensions.ExamManages
 
             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.Contains(generateExamTestPaperRequest.ExamManageId))
+                         .AndIF(generateExamTestPaperRequest.TestPaperId.IsNotNullOrEmpty(), x => x.TestPaperId.Contains(generateExamTestPaperRequest.TestPaperId))
+                         .And(x=>x.Mode == Share.Enums.Exams.EExamMode.Random)
+                         .ToExpression();
+
+            return expression;
+        }
     }
 }

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

@@ -2,7 +2,7 @@
 using Exam.Infrastructure.Extensions;
 using Exam.Infrastructure.Web.Utilities;
 using Exam.TestPapers;
-using Hotline.Share.Requests.Exam;
+using Hotline.Share.Requests.TestPaper;
 using JiebaNet.Segmenter.Common;
 using System.Linq.Expressions;
 

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

@@ -34,5 +34,17 @@ namespace Hotline.Application.Exam.QueryExtensions.Trains
 
             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)
+                .ToExpression();
+
+            return expression;
+        }
     }
 }

+ 102 - 1
src/Hotline.Application/Exam/Service/ExamManages/ExamManageService.cs

@@ -29,6 +29,15 @@ 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;
 
 namespace Hotline.Application.Exam.Service.ExamManages
 {
@@ -38,13 +47,26 @@ namespace Hotline.Application.Exam.Service.ExamManages
         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 IServiceProvider _serviceProvider;
         private readonly IMapper _mapper;
         private readonly ISessionContext _sessionContext;
+        private TestPaperProxy _testPaperProxy;
 
         public ExamManageService(IExamManageRepository repository,
             IExamQuestionScoreRepository examQuestionScoreRepository,
             IUserExamRepository userExamRepository,
+            ITestPaperRepository testPaperRepository,
+            ITestPaperItemRepository testPaperItemRepository,
+            ITestPaperItemAnswerRepository testPaperItemAnswerRepository,
+            ITestPaperItemOptionsRepository testPaperItemOptionsRepository,
+            ITestPaperItemSourcewareRepository testPaperItemSourcewareRepository,
+            ITestPaperItemKnowladgeRepository testPaperItemKnowladgeRepository,
             IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider,
             IMapper mapper, ISessionContext sessionContext) : base(repository, mapper, sessionContext)
         {
@@ -52,6 +74,12 @@ namespace Hotline.Application.Exam.Service.ExamManages
             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;
             _serviceProvider = serviceProvider;
             this._mapper = mapper;
             this._sessionContext = sessionContext;
@@ -101,7 +129,6 @@ namespace Hotline.Application.Exam.Service.ExamManages
 
             var result = await queryable.ToListAsync();
 
-
             var total = await queryable.CountAsync();
 
             return (total, result);
@@ -197,6 +224,80 @@ namespace Hotline.Application.Exam.Service.ExamManages
 
             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.GenerateQuestion(questions, generateExamTestPaperRequest.TestPaperId, cancellationToken);
+            }
+        }
+
+        private async Task<List<Question>> GetQuestions(List<TagQuestion> tagQuestionCounts)
+        {
+            var questionRepository = new ExamRepository<Question>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider);
+            var questionTagRepository = new ExamRepository<QuestionTag>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider);
+
+            var questionTable = questionRepository.Queryable();
+            var questionTagTable = questionTagRepository.Queryable();
+
+            if (tagQuestionCounts != null)
+            {
+                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;
+            }
+
+        }
+
+        private async Task<List<TagQuestion>> GetTagQuestions(ExamManage examManage)
+        {
+            var extractRuleRepository = new ExamRepository<ExtractRule>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider);
+            var ruleTagRepository = new ExamRepository<RuleTag>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider);
+            var tagQuestionRepository = new ExamRepository<TagQuestion>(_repository.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
 

+ 2 - 10
src/Hotline.Application/Exam/Service/Practices/PracticeService.cs

@@ -134,22 +134,14 @@ namespace Hotline.Application.Exam.Service.Practices
 
             var practiceQuestionOptions = await AddPracticeQuestionOptions(addPracticeQuestionDtos, cancellationToken);
 
-            base.Entity.PracticeQuestions.ForEach(item =>
-            {
-                item.PracticeQuestionOptionses = practiceQuestionOptions.Where(x=>item.Id == x.PracticeQuestionId).ToList();
-            });
-
             var practiceQuestionKnowladges = await AddPracticeQuestionKnowladgeAsync(addPracticeQuestionDtos, cancellationToken);
 
-            base.Entity.PracticeQuestions.ForEach(item =>
-            {
-                item.PracticeQuestionKnowladges = practiceQuestionKnowladges.Where(x => item.Id == x.PracticeQuestionId).ToList();
-            });
-
             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();
             });
 

+ 193 - 16
src/Hotline.Application/Exam/Service/Questions/QuestionService.cs

@@ -30,6 +30,14 @@ using Hotline.Application.Exam.Constants.Messages;
 using Hotline.Repository.SqlSugar.Exam.Core.Constants;
 using NPOI.SS.Formula.Functions;
 using XF.Domain.Authentications;
+using Hotline.Tools;
+using Microsoft.AspNetCore.Http;
+using System.Dynamic;
+using NPOI.Util.ArrayExtensions;
+using System.Threading.Tasks;
+using Hotline.Share.Enums.Exams;
+using Hotline.Share.Tools;
+using DocumentFormat.OpenXml.Drawing;
 
 namespace Hotline.Application.Exam.Service.Questions
 {
@@ -158,23 +166,11 @@ namespace Hotline.Application.Exam.Service.Questions
         {
             base.StartTran();
 
-            var id = await base.AddAsync(actionRequest, cancellationToken);
-
-            ResolveQuestionId(actionRequest,id);
-
-            base.Entity.QuestionTags = await AddQuestionTags(actionRequest, cancellationToken);
+            base.Entity= await AddQuestion(actionRequest, cancellationToken);
 
-            base.Entity.QuestionOptionses = await AddQuestionOptions(actionRequest, cancellationToken);
-
-            base.Entity.QuestionAnswerE = await AddQuestionAnswer(actionRequest, cancellationToken);
+            await base.Complete(base.Entity, OperationConstant.Create);
 
-            base.Entity.QuestionKnowladges =  await AddKnowladges(actionRequest, cancellationToken);
-
-            base.Entity.QuestionSourcewares = await AddSourcewares(actionRequest, cancellationToken);
-
-            await base.Complete(base.Entity, OperationConstant.Create);           
-
-            return id;
+            return base.Entity.Id;
         }
 
         public override async Task UpdateAsync(UpdateQuestionDto actionRequest, CancellationToken cancellationToken)
@@ -237,6 +233,168 @@ namespace Hotline.Application.Exam.Service.Questions
 
             await DeleteSourcewares(tmpEntityQueryRequest, cancellationToken);
         }
+
+        public async Task ImportExcel(IFormFile files, CancellationToken cancellationToken)
+        {
+            using (var stream = files.OpenReadStream())
+            {
+                var contents = ExcelHelper.Read(stream, true);
+
+                var questions = new List<Question>();
+                var examTags = await GetExamTags(contents);
+
+                base.StartTran();
+                contents.ForEach(async item =>
+                {
+                    var questionDto = BuildQuestion(item as ExpandoObject, examTags);
+
+                    var question = await AddQuestion(questionDto, cancellationToken);
+                    if (question != null)
+                        questions.Add(question);
+                });
+
+                await _repository.AddNav(questions)
+                            .Include(x => x.QuestionTags)
+                            .Include(x => x.QuestionOptionses)
+                            .Include(x => x.QuestionSourcewares)
+                            .Include(x => x.QuestionAnswerE)
+                            .Include(x => x.QuestionKnowladges).ExecuteCommandAsync();
+            }
+        }
+
+        private async Task<List<ExamTag>> GetExamTags(List<object> contents)
+        {
+            var examTags = new List<ExamTag>();
+
+            var tagNames = new List<string>();
+
+            contents.ForEach(item =>
+            {
+                var tagNameStr = ((ExpandoObject)item).GetValueOrDefault(SystemConstants.Labels[3])?.ToString();
+
+                if (tagNameStr.IsNotNullOrEmpty())
+                {
+                    var names = tagNameStr.Split(",").ToList();
+
+                    tagNames.AddRange(names);
+                }
+            });
+
+            var repository = new ExamRepository<ExamTag>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider);
+
+            examTags = await repository.Queryable().Where(x => tagNames.Contains(x.Name)).ToListAsync();
+
+            return examTags;
+        }
+
+        private AddQuestionDto BuildQuestion(ExpandoObject item,List<ExamTag> examTags)
+        {
+            if (item != null) {
+                var options = new List<string>();
+
+                ResolveOptions(options,item,6,15);
+
+                var answer = item.GetValueOrDefault(SystemConstants.ColumnNames[16]).ToString();
+
+                var questionDto = new AddQuestionDto
+                {
+                    Title = item.GetValueOrDefault(SystemConstants.ColumnNames[2]) != null ? item.GetValueOrDefault(SystemConstants.ColumnNames[5]).ToString() : string.Empty,
+                    QuestionTagDtos = BuildQuestionTags(item.GetValueOrDefault(SystemConstants.ColumnNames[1]).ToString(), examTags),
+                    DifficultyLevel = item.GetValueOrDefault(SystemConstants.ColumnNames[2]).ToString().ToEnumByDesc<EDifficultyLevel>(),
+                    QuestionType = item.GetValueOrDefault(SystemConstants.ColumnNames[0]).ToString().ToEnumByDesc<EQuestionType>(),
+                    FormalEnable = item.GetValueOrDefault(SystemConstants.ColumnNames[4]).ToString().ToEnumByDesc<ECheck>() == ECheck.Yes,
+                    SimulateEnable = item.GetValueOrDefault(SystemConstants.ColumnNames[5]).ToString().ToEnumByDesc<ECheck>() == ECheck.Yes,                   
+                };
+                questionDto.QuestionAnswerDto = BuildQuestionAnswer(answer, questionDto.QuestionType);
+
+                questionDto.QuestionOptionsDtos = BuildQuestionOptions(options, questionDto.QuestionType, answer);
+
+
+
+                return questionDto;
+            }
+
+            return null;
+        }
+
+        private List<AddQuestionOptionsDto> BuildQuestionOptions(List<string> options,EQuestionType questionType,string answer)
+        {
+            if (questionType.CheckSelectType())
+            {
+                var addQuestionOptionDtos = new List<AddQuestionOptionsDto>();
+
+                var index = 0;
+                options.ForEach(item =>
+                {
+                    var addQuestionOption = new AddQuestionOptionsDto
+                    {
+                        Content = item,
+                        Label = SystemConstants.Labels[index],
+                        IsAnswer = answer.Contains(SystemConstants.Labels[index])
+                    };
+
+                    addQuestionOptionDtos.Add(addQuestionOption);
+
+                    index++;
+                });
+
+                return addQuestionOptionDtos;
+            }
+            return null;
+        }
+
+        private void ResolveOptions(List<string> options, ExpandoObject item, int start, int end)
+        {
+            for(int i = start; i <= end; i++)
+            {
+                var option = item.GetValueOrDefault(SystemConstants.ColumnNames[i])?.ToString();
+                if (option.IsNotNullOrEmpty())
+                {
+                    options.Add(option.ToString());
+                }
+            }
+        }
+
+        private AddQuestionAnswerDto BuildQuestionAnswer(string? content, EQuestionType questionType)
+        {
+            if (!questionType.CheckSelectType())
+            {
+                var addQuestionAnswerDto = new AddQuestionAnswerDto
+                {
+                    Answer = content
+                };
+
+                return addQuestionAnswerDto;
+            }
+            return null;
+        }
+
+        private List<AddQuestionTagDto> BuildQuestionTags(string content,List<ExamTag> examTags)
+        {
+            if (content.IsNotNullOrEmpty())
+            {
+                var addQuestionTagDtos = new List<AddQuestionTagDto>();
+
+                var tagNames = content.Split(",");
+
+                tagNames.ToList().ForEach(item =>
+                {
+                    var examTag = examTags.FirstOrDefault(x => x.Name == item);
+
+                    if (examTag != null)
+                    {
+                        addQuestionTagDtos.Add(new AddQuestionTagDto
+                        {
+                            TagId = examTag.Id
+                        });
+                    }                    
+                });
+
+                return addQuestionTagDtos;
+            }
+
+            return null;
+        }
         #endregion
 
         #region private method
@@ -308,7 +466,7 @@ namespace Hotline.Application.Exam.Service.Questions
             questionOptionses.ForEach(m =>
             {
                 m.SortIndex = sortIndex;
-                m.Label = SystemConstants.Labals[sortIndex];
+                m.Label = SystemConstants.Labels[sortIndex];
                 sortIndex++;
             });
 
@@ -764,6 +922,25 @@ namespace Hotline.Application.Exam.Service.Questions
 
             return await questionTagDtos.ToListAsync();
         }
+
+        private async Task<Question> AddQuestion(AddQuestionDto actionRequest, CancellationToken cancellationToken)
+        {
+            var id = await base.AddAsync(actionRequest, cancellationToken);
+
+            ResolveQuestionId(actionRequest, id);
+
+            base.Entity.QuestionTags = await AddQuestionTags(actionRequest, cancellationToken);
+
+            base.Entity.QuestionOptionses = await AddQuestionOptions(actionRequest, cancellationToken);
+
+            base.Entity.QuestionAnswerE = await AddQuestionAnswer(actionRequest, cancellationToken);
+
+            base.Entity.QuestionKnowladges = await AddKnowladges(actionRequest, cancellationToken);
+
+            base.Entity.QuestionSourcewares = await AddSourcewares(actionRequest, cancellationToken);
+            
+            return base.Entity;
+        }
         #endregion
 
         #region protected method

+ 88 - 155
src/Hotline.Application/Exam/Service/TestPapers/TestPaperService.cs

@@ -10,10 +10,12 @@ using Exam.Insfrastructure.Service.Service;
 using Exam.Questions;
 using Exam.Repository.Sqlsugar;
 using Exam.Repository.Sqlsugar.Repositories;
+using Exam.Repository.Sqlsugar.Repositories.TestPapers;
 using Exam.Share.ViewResponses.TestPaper;
 using Exam.TestPapers;
 using Hotline.Application.Exam.Core.Extensions;
 using Hotline.Application.Exam.Extensions;
+using Hotline.Application.Exam.Proxy;
 using Hotline.Application.Exam.QueryExtensions.TestPapers;
 using Hotline.Exams.TestPapers;
 using Hotline.Repository.SqlSugar;
@@ -21,20 +23,24 @@ using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Repository.SqlSugar.Exam.Core.Constants;
 using Hotline.Repository.SqlSugar.Exam.Interfaces.Questions;
 using Hotline.Repository.SqlSugar.Exam.Interfaces.TestPapers;
+using Hotline.Repository.SqlSugar.Exam.Repositories.TestPapers;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Share.Dtos.Questions;
 using Hotline.Share.Dtos.TestPapers;
 using Hotline.Share.Dtos.Trains;
 using Hotline.Share.Requests.Exam;
+using Hotline.Share.Requests.TestPaper;
 using Hotline.Share.Tools;
 using Hotline.Share.ViewResponses.Exam;
 using JiebaNet.Segmenter.Common;
 using MapsterMapper;
+using Microsoft.Extensions.DependencyInjection;
 using NPOI.SS.Formula.Functions;
 using SqlSugar;
 using System.Collections.Immutable;
 using XF.Domain.Authentications;
 using XF.Domain.Dependency;
+using XF.Domain.Repository;
 
 namespace Hotline.Application.Exam.Service.TestPapers
 {
@@ -52,11 +58,12 @@ namespace Hotline.Application.Exam.Service.TestPapers
         private readonly IServiceProvider _serviceProvider;
         private readonly ISessionContext _sessionContext;
         private readonly IMapper _mapper;
+        private TestPaperProxy _testPaperProxy;
 
         public TestPaperService(ITestPaperRepository repository,
-            ITestPaperItemRepository testPaperItemRepository,
             ITestPaperRuleRepository testPaperRuleRepository,
             ITestPaperRuleTagRepository testPaperRuleTagRepository,
+            ITestPaperItemRepository testPaperItemRepository,
             ITestPaperItemAnswerRepository testPaperItemAnswerRepository,
             ITestPaperItemOptionsRepository testPaperItemOptionsRepository,
             ITestPaperItemSourcewareRepository testPaperItemSourcewareRepository,
@@ -138,40 +145,28 @@ namespace Hotline.Application.Exam.Service.TestPapers
 
             var testPaperItemAnswers = await AddTestPaperItemAnswer(actionRequest, cancellationToken);
 
-            base.Entity.TestPaperItems.ForEach(item =>
-            {
-                item.TestPaperItemAnswers = testPaperItemAnswers.Where(x => item.Id == x.TestPaperItemId).ToList();
-            });
-
-           var testPaperItemOptions =  await AddTestPaperItemOptions(actionRequest, cancellationToken);
-
-            base.Entity.TestPaperItems.ForEach(item =>
-            {
-                item.TestPaperItemOptionses = testPaperItemOptions.Where(x => item.Id == x.TestPaperItemId).ToList();
-            });
+            var testPaperItemOptions = await AddTestPaperItemOptions(actionRequest, cancellationToken);
 
             var testPaperItemKnowlagdes = await AddTestPaperItemKnowladgeAsync(actionRequest, cancellationToken);
 
-            base.Entity.TestPaperItems.ForEach(item =>
-            {
-                item.TestPaperItemKnowlagdes = testPaperItemKnowlagdes.Where(x => item.Id == x.TestPaperItemId).ToList();
-            });
-
-           var testPaperItemSourcewares = await AddTestPaperItemSourcewareAsync(actionRequest, cancellationToken);
+            var testPaperItemSourcewares = await AddTestPaperItemSourcewareAsync(actionRequest, cancellationToken);
 
             base.Entity.TestPaperItems.ForEach(item =>
             {
                 item.TestPaperItemSourcewares = testPaperItemSourcewares.Where(x => item.Id == x.TestPaperItemId).ToList();
+                item.TestPaperItemAnswers = testPaperItemAnswers.Where(x => item.Id == x.TestPaperItemId).ToList();
+                item.TestPaperItemKnowlagdes = testPaperItemKnowlagdes.Where(x => item.Id == x.TestPaperItemId).ToList();
+                item.TestPaperItemOptionses = testPaperItemOptions.Where(x => item.Id == x.TestPaperItemId).ToList();
             });
 
-            base.Entity.TestPaperRules = await AddTestPaperRules(actionRequest, cancellationToken);
+            //base.Entity.TestPaperRules = await AddTestPaperRules(actionRequest, cancellationToken);
 
-            var testPaperRuleTags = await AddTestPaperRuleTags(actionRequest, cancellationToken);
+            //var testPaperRuleTags = await AddTestPaperRuleTags(actionRequest, cancellationToken);
 
-            base.Entity.TestPaperRules?.ForEach(item =>
-            {
-                item.TestPaperRuleTags = testPaperRuleTags.Where(m => item.Id == m.TestPaperRuleId).ToList();
-            });
+            //base.Entity.TestPaperRules?.ForEach(item =>
+            //{
+            //    item.TestPaperRuleTags = testPaperRuleTags.Where(m => item.Id == m.TestPaperRuleId).ToList();
+            //});
 
             await base.Complete(base.Entity, OperationConstant.Create);
 
@@ -196,40 +191,29 @@ namespace Hotline.Application.Exam.Service.TestPapers
 
             var testPaperItemAnswers = await ModifyTestPaperItemAnswer(actionRequest, cancellationToken);
 
-            base.Entity.TestPaperItems.ForEach(item =>
-            {
-                item.TestPaperItemAnswers = testPaperItemAnswers.Where(x => item.Id == x.TestPaperItemId).ToList();
-            });
-
             var testPaperItemOptions = await ModifyTestPaperItemOptions(actionRequest, cancellationToken);
 
-            base.Entity.TestPaperItems.ForEach(item =>
-            {
-                item.TestPaperItemOptionses = testPaperItemOptions.Where(x => item.Id == x.TestPaperItemId).ToList();
-            });
-
             var testPaperItemKnowlagdes = await ModifyTestPaperItemKnowladgeAsync(actionRequest, cancellationToken);
 
-            base.Entity.TestPaperItems.ForEach(item =>
-            {
-                item.TestPaperItemKnowlagdes = testPaperItemKnowlagdes.Where(x => item.Id == x.TestPaperItemId).ToList();
-            });
-
             var testPaperItemSourcewares = await ModifyTestPaperItemSourcewareAsync(actionRequest, cancellationToken);
 
             base.Entity.TestPaperItems.ForEach(item =>
             {
-                item.TestPaperItemSourcewares = testPaperItemSourcewares.Where(x => item.Id == x.TestPaperItemId).ToList();
+                item.TestPaperItemAnswers = testPaperItemAnswers.Where(x => item.Id == x.TestPaperItemId).ToList();
+                item.TestPaperItemSourcewares = testPaperItemSourcewares.Where(x => item.Id == x.TestPaperItemId).ToList(); 
+                item.TestPaperItemKnowlagdes = testPaperItemKnowlagdes.Where(x => item.Id == x.TestPaperItemId).ToList();
+                item.TestPaperItemOptionses = testPaperItemOptions.Where(x => item.Id == x.TestPaperItemId).ToList();
             });
 
-            base.Entity.TestPaperRules = await ModifyTestPaperRules(actionRequest, cancellationToken);
+            //base.Entity.TestPaperRules = await ModifyTestPaperRules(actionRequest, cancellationToken);
 
-            var testPaperRuleTags = await ModifyTestPaperRuleTags(actionRequest, cancellationToken);
+            //var testPaperRuleTags = await ModifyTestPaperRuleTags(actionRequest, cancellationToken);
 
-            base.Entity.TestPaperRules.ForEach(item =>
-            {
-                item.TestPaperRuleTags = testPaperRuleTags.Where(x => item.Id == x.TestPaperRuleId).ToList();
-            });
+            //base.Entity.TestPaperRules.ForEach(item =>
+            //{
+
+            //    item.TestPaperRuleTags = testPaperRuleTags.Where(x => item.Id == x.TestPaperRuleId).ToList();
+            //});
 
             await base.Complete(base.Entity, OperationConstant.Update);
         }
@@ -294,115 +278,76 @@ namespace Hotline.Application.Exam.Service.TestPapers
         }
 
 
+        /// <summary>
+        /// 根据规则获取试题
+        /// </summary>
+        /// <param name="testPaperQuestionRequest"></param>
+        /// <returns></returns>
+        /// <exception cref="NotImplementedException"></exception>
+        public async Task<List<QuestionDto>> GetQuestionDtos(TestPaperQuestionRequest testPaperQuestionRequest)
+        {
+            var questions = await GetQuestions(testPaperQuestionRequest);
+
+            return questions;
+        }
+
+        /// <summary>
+        /// 组卷
+        /// </summary>
+        /// <param name="generateTestPaperRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
         public async Task GenerateTestPaper(GenerateTestPaperRequest generateTestPaperRequest, CancellationToken cancellationToken)
         {
             var testPaper = await _repository.GetAsync(x => x.Id == generateTestPaperRequest.TestPaperId);
 
             if (testPaper != null)
             {
+                //随机组卷
                 if (testPaper.Mode == Share.Enums.Exams.EExamMode.Random)
                 {
-                    List<TestPaperItem> testPaperItems = await SyncQuestion(generateTestPaperRequest, cancellationToken);
-
-                    await SyncQuestionOptions(testPaperItems, cancellationToken);
-                    await SyncQuestionSourcewares(testPaperItems, cancellationToken);
-                    await SyncQuestionKnowladge(testPaperItems, cancellationToken);
-                    await SyncQuestionAnswer(testPaperItems, cancellationToken);
-
+                    var questions = await GetQuestions(generateTestPaperRequest);
+                    _testPaperProxy = new TestPaperProxy(_repository,
+                        _testPaperItemRepository,
+                        _testPaperItemAnswerRepository,
+                        _testPaperItemOptionsRepository,
+                        _testPaperItemSourcewareRepository,
+                        _testPaperItemKnowladgeRepository,
+                        _dataPermissionFilterBuilder,
+                        _serviceProvider,
+                        _mapper,
+                        _sessionContext);
+
+                    await _testPaperProxy.GenerateQuestion(questions, generateTestPaperRequest.TestPaperId, cancellationToken);
                 }
             }
             
         }
 
-        private async Task SyncQuestionAnswer(List<TestPaperItem> testPaperItems, CancellationToken cancellationToken)
+        private async Task<List<QuestionDto>> GetQuestions(TestPaperQuestionRequest testPaperQuestionRequest)
         {
-            var questionAnswersRepository = new ExamRepository<QuestionAnswer>(_repository.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.QueswerAnswerId = item.Id;
-                testPaperItemAnswer.TestPaperItemId = testPaperItem?.Id ?? string.Empty;
-                testPaperItemAnswers.Add(testPaperItemAnswer);
-            });
-
-            testPaperItemAnswers.ToInsert(_sessionContext);
-
-            await _testPaperItemAnswerRepository.ValidateAddAsync(testPaperItemAnswers, cancellationToken);
-        }
-
-        private async Task SyncQuestionKnowladge(List<TestPaperItem> testPaperItems, CancellationToken cancellationToken)
-        {
-            var questionKnowladgesRepository = new ExamRepository<QuestionKnowladge>(_repository.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);
-        }
-
-        private async Task SyncQuestionSourcewares(List<TestPaperItem> testPaperItems, CancellationToken cancellationToken)
-        {
-            var questionSourcewaresRepository = new ExamRepository<QuestionSourceware>(_repository.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);
-        }
+            var questionRepository = new ExamRepository<Question>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider);
+            var questionTagRepository = new ExamRepository<QuestionTag>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider);
+            var questionTagTable = questionTagRepository.Queryable();
+            var questionTable = questionRepository.Queryable();
 
-        private async Task SyncQuestionOptions(List<TestPaperItem> testPaperItems, CancellationToken cancellationToken)
-        {
-            var questionOptionsRepository =new ExamRepository<QuestionOptions>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider);
-            var questionIds = testPaperItems.Select(x => x.QuestionId);
-            var questionOptions = await questionOptionsRepository.Queryable().Where(x=>questionIds.Contains(x.QuestionId)).ToListAsync();
+            var questions = await questionTable.InnerJoin(questionTagTable, (q,qt)=>q.Id == qt.QuestionId)
+                .Where((q, qt) => q.DifficultyLevel == testPaperQuestionRequest.DifficultyLevel 
+            && q.QuestionType == testPaperQuestionRequest.QuestionType && testPaperQuestionRequest.TagIds.Contains(qt.TagId)
+            )
+                .Select((q, qt) => q).Take(testPaperQuestionRequest.Count).OrderBy(SqlFunc.GetRandom()).ToListAsync();
 
-            var testPaperItemOptions = new List<TestPaperItemOptions>();
+            var questionDtos = new List<QuestionDto>();
 
-            questionOptions.ForEach(item =>
+            questions.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);
+                var questionDto = _mapper.Map<QuestionDto>(item);
             });
 
-            testPaperItemOptions.ToInsert(_sessionContext);
-
-            await _testPaperItemOptionsRepository.ValidateAddAsync(testPaperItemOptions, cancellationToken);
+            return questionDtos;
         }
 
-        private async Task<List<TestPaperItem>> SyncQuestion(GenerateTestPaperRequest generateTestPaperRequest, CancellationToken cancellationToken)
+        private async Task<List<Question>> GetQuestions(GenerateTestPaperRequest generateTestPaperRequest)
         {
             var questionRepository = new ExamRepository<Question>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider);
             var questionTagRepository = new ExamRepository<QuestionTag>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider);
@@ -416,25 +361,14 @@ namespace Hotline.Application.Exam.Service.TestPapers
             var questions = await questionTable.InnerJoin(questionTagTable, (q, qt) => q.Id == qt.QuestionId)
                 .InnerJoin(testPaperTagTable, (q, qt, tt) => qt.TagId == tt.TagId)
                 .InnerJoin(testPaperRuleTable, (q, qt, tt, tpr) => tt.TestPaperRuleId == tpr.Id)
-                .Where((q, qt, tt, tpr)=>q.QuestionType == tpr.QuestionType)
+                .Where((q, qt, tt, tpr) => q.QuestionType == tpr.QuestionType)
                 .Select((q, qt, tt, tpr) => q).Take(count).OrderBy(SqlFunc.GetRandom()).ToListAsync();
 
-            var testPaperItems = new List<TestPaperItem>();
 
-            questions.ForEach(item =>
-            {
-                var testPaperItem = _mapper.Map<TestPaperItem>(item);
-                testPaperItem.TestPaperId = generateTestPaperRequest.TestPaperId;
-                testPaperItem.QuestionId = item.Id;
-                testPaperItems.Add(testPaperItem);
-            });
-
-            testPaperItems.ToInsert(_sessionContext);
+            return questions;
+        }
 
-            await _testPaperItemRepository.ValidateAddAsync(testPaperItems,cancellationToken);
 
-            return testPaperItems;
-        }
         #endregion
 
         #region private method
@@ -566,10 +500,10 @@ namespace Hotline.Application.Exam.Service.TestPapers
                 var testPaperRuleId = string.Empty;
                 if (base.Entity != null)
                 {
-                    var testPaperRule = base.Entity.TestPaperRules.FirstOrDefault(x => x.TestPaperId == item.TestPaperId
-                    && x.QuestionType == item.QuestionType && x.Count == item.Count && x.DifficultyLevel == item.DifficultyLevel);
+                    //var testPaperRule = base.Entity.TestPaperRules.FirstOrDefault(x => x.TestPaperId == item.TestPaperId
+                    //&& x.QuestionType == item.QuestionType && x.Count == item.Count && x.DifficultyLevel == item.DifficultyLevel);
 
-                    testPaperRuleId = testPaperRule?.Id;
+                    //testPaperRuleId = testPaperRule?.Id;
                 }
 
                 item.TestPaperRuleTagDtos.ForEach(x =>
@@ -1074,8 +1008,8 @@ namespace Hotline.Application.Exam.Service.TestPapers
         protected override async Task CompleteAdd(TestPaper entity)
         {
             await base.AddNav(entity)
-                  .Include(x => x.TestPaperRules)
-                  .ThenInclude(x => x.TestPaperRuleTags)
+                  //.Include(x => x.TestPaperRules)
+                  //.ThenInclude(x => x.TestPaperRuleTags)
                   .Include(x => x.TestPaperItems)
                   .ThenInclude(x => x.TestPaperItemAnswers)
                   .Include(x => x.TestPaperItems, new InsertNavOptions
@@ -1093,8 +1027,8 @@ namespace Hotline.Application.Exam.Service.TestPapers
         protected override async Task CompleteUpdate(TestPaper entity)
         {
             await base.UpdateNav(entity)
-                 .Include(x => x.TestPaperRules)
-                 .ThenInclude(x => x.TestPaperRuleTags)
+                 //.Include(x => x.TestPaperRules)
+                 //.ThenInclude(x => x.TestPaperRuleTags)
                  .Include(x => x.TestPaperItems)
                  .ThenInclude(x => x.TestPaperItemAnswers)
                  .Include(x => x.TestPaperItems, new UpdateNavOptions
@@ -1108,7 +1042,6 @@ namespace Hotline.Application.Exam.Service.TestPapers
                  })
                  .ThenInclude(x => x.TestPaperItemSourcewares).ExecuteCommandAsync();
         }
-
         #endregion
     }
 }

+ 8 - 0
src/Hotline.Application/Exam/Service/Trains/TrainRecordService.cs

@@ -358,6 +358,14 @@ namespace Hotline.Application.Exam.Service.Trains
 
             return await queryResult.ToListAsync();
         }
+
+        public async Task<List<TrainResultViewResponse>> AnalysisTrainResult(TrainResultReportPagedRequest trainResultReportPagedRequest)
+        {
+            //var expression = trainResultReportPagedRequest.GetExpression();
+            //var 
+
+            return null;
+        }
         #endregion
 
 

+ 4 - 14
src/Hotline.Application/Exam/Service/Trains/TrainTemplateService.cs

@@ -125,10 +125,6 @@ namespace Hotline.Application.Exam.Service.Trains
 
             var trainPracticeOptions = await AddTrainPracticeOptionsAsync(actionRequest, cancellationToken);
 
-            base.Entity.TrainPractices.ForEach(item =>
-            {
-                item.TrainPracticeOptionses = trainPracticeOptions.Where(x => item.Id == x.TrainPracticeId).ToList();
-            });
 
             var trainPracticeKnowladges =  await AddTrainPracticeKnowladgeAsync(actionRequest, cancellationToken);
 
@@ -141,6 +137,8 @@ namespace Hotline.Application.Exam.Service.Trains
 
             base.Entity.TrainPractices.ForEach(item =>
             {
+                item.TrainPracticeKnowladges = trainPracticeKnowladges.Where(x => item.Id == x.TrainPracticeId).ToList();
+                item.TrainPracticeOptionses = trainPracticeOptions.Where(x => item.Id == x.TrainPracticeId).ToList();
                 item.TrainPracticeSourcewares = trainPracticeSourcewares.Where(x => item.Id == x.TrainPracticeId).ToList();
             });
 
@@ -162,22 +160,14 @@ namespace Hotline.Application.Exam.Service.Trains
 
             var trainPracticeOptions = await ModifyTrainPracticeOptionsAsync(actionRequest, cancellationToken);
 
-            base.Entity.TrainPractices.ForEach(item =>
-            {
-                item.TrainPracticeOptionses = trainPracticeOptions.Where(x => item.Id == x.TrainPracticeId).ToList();
-            });
-
             var trainPracticeKnowladges = await ModifyTrainPracticeKnowladgeAsync(actionRequest, cancellationToken);
 
-            base.Entity.TrainPractices.ForEach(item =>
-            {
-                item.TrainPracticeKnowladges = trainPracticeKnowladges.Where(x => item.Id == x.TrainPracticeId).ToList();
-            });
-
             var trainPracticeSourcewares = await ModifyTrainPracticeSourcewareAsync(actionRequest, cancellationToken);
 
             base.Entity.TrainPractices.ForEach(item =>
             {
+                item.TrainPracticeOptionses = trainPracticeOptions.Where(x => item.Id == x.TrainPracticeId).ToList();          
+                item.TrainPracticeKnowladges = trainPracticeKnowladges.Where(x => item.Id == x.TrainPracticeId).ToList();
                 item.TrainPracticeSourcewares = trainPracticeSourcewares.Where(x => item.Id == x.TrainPracticeId).ToList();
             });
 

+ 1 - 1
src/Hotline.Repository.SqlSugar/Exam/Validators/ExamManages/ExtractRuleValidator.cs

@@ -34,7 +34,7 @@ namespace Exam.Repository.Sqlsugar.Validators.ExamManages
         {
             base.BaseValidateRule();
 
-            RuleFor(m => m.RuleType).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(ExtractRule.RuleType))));
+            RuleFor(m => m.RuleType).NotNull().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(ExtractRule.RuleType))));
             RuleFor(m => m.Name).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(ExtractRule.Name))));
             RuleFor(m => m.Code).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(ExtractRule.Code))));
 

+ 7 - 0
src/Hotline.Share/Dtos/Questions/QuestionOptionsDto.cs

@@ -13,6 +13,13 @@ namespace Hotline.Share.Dtos.Questions
     public class QuestionOptionsDto:UpdateQuestionOptionsDto
     {
         
+        public string CompleteContent
+        {
+            get
+            {
+                return string.Concat(Label, ".", Content);
+            }
+        }
     }
 
     /// <summary>

+ 3 - 0
src/Hotline.Share/Dtos/TestPapers/TestPaperDto.cs

@@ -23,6 +23,7 @@ namespace Hotline.Share.Dtos.TestPapers
         /// 试卷组卷规则
         /// </summary>
         [Description("试卷组卷规则")]
+        [JsonIgnore]
         public new List<TestPaperRuleDto> TestPaperRuleDtos { get; set; }
     }
 
@@ -80,6 +81,7 @@ namespace Hotline.Share.Dtos.TestPapers
         /// 试卷组卷规则
         /// </summary>
         [Description("试卷组卷规则")]
+        [JsonIgnore]
         public List<AddTestPaperRuleDto> TestPaperRuleDtos { get; set; }
     }
 
@@ -106,6 +108,7 @@ namespace Hotline.Share.Dtos.TestPapers
         /// 试卷组卷规则
         /// </summary>
         [Description("试卷组卷规则")]
+        [JsonIgnore]
         public new List<UpdateTestPaperRuleDto> TestPaperRuleDtos { get; set; }
     }
 }

+ 5 - 0
src/Hotline.Share/Dtos/TestPapers/TestPaperItemDto.cs

@@ -5,6 +5,7 @@ using Hotline.Share.Dtos.Questions;
 using Hotline.Share.Enums.Exams;
 using Hotline.Share.Exams.Interface;
 using System.ComponentModel;
+using System.Text.Json.Serialization;
 
 namespace Hotline.Share.Dtos.TestPapers
 {
@@ -101,12 +102,14 @@ namespace Hotline.Share.Dtos.TestPapers
         /// 关联课件
         /// </summary>
         [Description("关联课件")]
+        [JsonIgnore]
         public List<AddTestPaperItemSourcewareDto> TestPaperItemSourcewareDtos { get; set; }
 
         /// <summary>
         /// 关联知识
         /// </summary>
         [Description("关联知识")]
+        [JsonIgnore]
         public  List<AddTestPaperItemKnowladgeDto> TestPaperItemKnowladgeDtos { get; set; }
 
     }
@@ -127,12 +130,14 @@ namespace Hotline.Share.Dtos.TestPapers
         /// 关联课件
         /// </summary>
         [Description("关联课件")]
+        [JsonIgnore]
         public new List<UpdateTestPaperItemSourcewareDto> TestPaperItemSourcewareDtos { get; set; }
 
         /// <summary>
         /// 关联知识
         /// </summary>
         [Description("关联知识")]
+        [JsonIgnore]
         public new List<UpdateTestPaperItemKnowladgeDto> TestPaperItemKnowladgeDtos { get; set; }
     }
 }

+ 1 - 1
src/Hotline.Share/Enums/Exams/EExamMode.cs

@@ -15,7 +15,7 @@ namespace Hotline.Share.Enums.Exams
         [Description("随机组题")]
         Random = 1,
         /// <summary>
-        /// 随机组题
+        /// 导入
         /// </summary>
         [Description("导入")]
         Import = 2,

+ 9 - 0
src/Hotline.Share/Requests/Exam/GenerateTestPaperRequest.cs

@@ -10,4 +10,13 @@ namespace Hotline.Share.Requests.Exam
         [Description("试卷Id")]
         public string TestPaperId { get; set; }
     }
+
+    public class GenerateExamTestPaperRequest : GenerateTestPaperRequest
+    {
+        /// <summary>
+        /// 考试Id
+        /// </summary>
+        [Description("考试Id")]
+        public string ExamManageId { get; set; }
+    }
 }

+ 1 - 1
src/Hotline.Share/Requests/Exam/TestPaperPagedRequest.cs → src/Hotline.Share/Requests/TestPaper/TestPaperPagedRequest.cs

@@ -1,7 +1,7 @@
 using Exam.Infrastructure.Data.Interface;
 using Hotline.Share.Enums.Exams;
 
-namespace Hotline.Share.Requests.Exam
+namespace Hotline.Share.Requests.TestPaper
 {
     public record TestPaperPagedRequest:PagedRequest,IQueryRequest
     {

+ 1 - 1
src/Hotline.Share/Requests/Exam/TestPaperQuestionCountRequest.cs → src/Hotline.Share/Requests/TestPaper/TestPaperQuestionCountRequest.cs

@@ -1,7 +1,7 @@
 using Exam.Infrastructure.Data.Interface;
 using System.ComponentModel;
 
-namespace Hotline.Share.Requests.Exam
+namespace Hotline.Share.Requests.TestPaper
 {
     public class TestPaperQuestionCountRequest: IQueryRequest
     {

+ 34 - 0
src/Hotline.Share/Requests/TestPaper/TestPaperQuestionRequest.cs

@@ -0,0 +1,34 @@
+using Exam.Infrastructure.Data.Interface;
+using Hotline.Share.Enums.Exams;
+using System.ComponentModel;
+
+namespace Hotline.Share.Requests.TestPaper
+{
+    public class TestPaperQuestionRequest : IQueryRequest
+    {
+        /// <summary>
+        /// 标签
+        /// </summary>
+        [Description("标签")]
+        public List<string> TagIds { get; set; }
+
+        /// <summary>
+        /// 题型
+        /// </summary>
+        [Description("题型")]
+        public EQuestionType QuestionType { get; set; }
+
+        /// <summary>
+        /// 数量
+        /// </summary>
+        [Description("数量")]
+        public int Count { get; set; }
+
+        /// <summary>
+        /// 难度
+        /// </summary>
+        [Description("难度")]
+        public EDifficultyLevel DifficultyLevel { get; set; }
+
+    }
+}

+ 35 - 1
src/Hotline.Share/Tools/StringExtensions.cs

@@ -1,4 +1,7 @@
-using PanGu;
+using Hotline.Share.Dtos.Bigscreen;
+using PanGu;
+using System.ComponentModel;
+using System.Reflection;
 using System.Security.Cryptography;
 using System.Text;
 using System.Text.RegularExpressions;
@@ -33,6 +36,37 @@ public static class StringExtensions
         return (TEnum)Enum.Parse(typeof(TEnum), value);
     }
 
+    /// <summary>
+    /// 将字符串转换为指定的枚举类型。
+    /// </summary>
+    /// <typeparam name="TEnum">枚举类型</typeparam>
+    /// <param name="description">要转换的字符串</param>
+    /// <returns>枚举值</returns>
+    public static TEnum ToEnumByDesc<TEnum>(this string description) where TEnum : struct
+    {
+        if (!typeof(TEnum).IsEnum)
+        {
+            throw new ArgumentException("TEnum must be an enumerated type");
+        }
+        var fieldInfos = typeof(TEnum).GetFields();
+
+        foreach(var fieldInfo in fieldInfos)
+        {
+            var desc = fieldInfo.GetCustomAttribute<DescriptionAttribute>();
+
+            if (desc == null) continue;
+
+            if(desc.Description == description)
+            {
+                return (TEnum)fieldInfo.GetValue(null);
+            }
+        }
+       
+
+        // 尝试解析字符串为枚举
+        return default(TEnum);
+    }
+
     /// <summary>
     /// 是否手机号码
     /// </summary>

+ 7 - 0
src/Hotline/Exams/ExamManages/ExamManage.cs

@@ -53,6 +53,13 @@ namespace Exam.ExamManages
         [Description("试卷Id")]
         public string TestPaperId { get; set; }
 
+        /// <summary>
+        /// 抽题规则Id
+        /// </summary>
+        [SugarColumn(ColumnDescription = "抽题规则Id")]
+        [Description("抽题规则Id")]
+        public string ExtractRuleId { get; set; }
+
         /// <summary>
         /// 可考次数
         /// </summary>

+ 1 - 1
src/Hotline/Exams/ExamManages/ExtractRule.cs

@@ -37,7 +37,7 @@ namespace Exam.ExamManages
         /// </summary>
         [SugarColumn(ColumnDescription ="备注")]
         [Description("备注")]
-        public string Remark { get; set; }
+        public string? Remark { get; set; }
 
         [Navigate(NavigateType.OneToMany,nameof(TagQuestion.RuleId))]
         public List<TagQuestion> TagQuestions { get; set; }

+ 2 - 2
src/Hotline/Exams/TestPapers/TestPaper.cs

@@ -56,7 +56,7 @@ namespace Exam.TestPapers
         [Navigate(NavigateType.OneToMany,nameof(TestPaperItem.TestPaperId))]
         public List<TestPaperItem> TestPaperItems { get; set; }
 
-        [Navigate(NavigateType.OneToMany,nameof(TestPaperRule.TestPaperId))]
-        public List<TestPaperRule> TestPaperRules { get; set; }
+        //[Navigate(NavigateType.OneToMany,nameof(TestPaperRule.TestPaperId))]
+        //public List<TestPaperRule> TestPaperRules { get; set; }
     }
 }

+ 5 - 0
src/Hotline/Tools/ExcelHelper.cs

@@ -20,5 +20,10 @@ namespace Hotline.Tools
             stream.Seek(0, SeekOrigin.Begin);
             return stream;
         }
+
+        public static List<object> Read(Stream stream,bool useHeaderRow)
+        {
+           return  MiniExcel.Query(stream, useHeaderRow).ToList();
+        }
     }
 }