Browse Source

Merge branch 'feature/exam' into dev
合并冲突

guqiang 1 month ago
parent
commit
4ce9020704
69 changed files with 2040 additions and 499 deletions
  1. 9 0
      src/Exam.Infrastructure.Data/Entity/EntityQueryRequest.cs
  2. 6 0
      src/Exam.Infrastructure.Validation/Validation/ErrorMessage.cs
  3. 19 0
      src/Exam.Infrastructure.Web/Extensions/ExpressionableExtensions.cs
  4. 15 0
      src/Exam.Infrastructure.Web/Utilities/ExpressionableUtility.cs
  5. 26 0
      src/Exam.Infrastructure.Web/Utilities/TypeAdapterConfigUtility.cs
  6. 2 2
      src/Hotline.Api/Controllers/Exam/ExamTagController.cs
  7. 108 0
      src/Hotline.Api/Controllers/Exam/ExtractRuleController.cs
  8. 68 4
      src/Hotline.Api/Controllers/Exam/QuestionController.cs
  9. 2 2
      src/Hotline.Api/Controllers/Exam/SourcewareCategoryController.cs
  10. 2 2
      src/Hotline.Api/Controllers/Exam/SourcewaresController.cs
  11. 9 0
      src/Hotline.Application/Exam/Constants/ApiRoutes/ExtractRuleApiRoute.cs
  12. 8 0
      src/Hotline.Application/Exam/Constants/ApiRoutes/QuestionApiRoute.cs
  13. 24 0
      src/Hotline.Application/Exam/Extensions/ActionRequestExtensions.cs
  14. 2 0
      src/Hotline.Application/Exam/Interface/ExamManages/IExtractRuleService.cs
  15. 11 1
      src/Hotline.Application/Exam/Mappers/MapperConfigs.cs
  16. 25 0
      src/Hotline.Application/Exam/QueryExtensions/ExamManages/ExtractRuleQueryExtensions.cs
  17. 21 0
      src/Hotline.Application/Exam/QueryExtensions/ExamManages/TagQuestionQueryExtensions.cs
  18. 12 6
      src/Hotline.Application/Exam/QueryExtensions/Questions/QuestionQueryExtesions.cs
  19. 7 7
      src/Hotline.Application/Exam/QueryExtensions/Sourcewares/SourcewareQueryExtensions.cs
  20. 40 0
      src/Hotline.Application/Exam/QueryExtensions/TestPapers/TestPaperQueryExtensions.cs
  21. 6 3
      src/Hotline.Application/Exam/Service/ExamManages/ExamTagService.cs
  22. 427 0
      src/Hotline.Application/Exam/Service/ExamManages/ExtractRuleService.cs
  23. 0 77
      src/Hotline.Application/Exam/Service/Questions/QuestionAnswerService.cs
  24. 0 76
      src/Hotline.Application/Exam/Service/Questions/QuestionKnowladgeService.cs
  25. 0 79
      src/Hotline.Application/Exam/Service/Questions/QuestionOptionsService.cs
  26. 398 20
      src/Hotline.Application/Exam/Service/Questions/QuestionService.cs
  27. 0 76
      src/Hotline.Application/Exam/Service/Questions/QuestionSourcewareService.cs
  28. 0 75
      src/Hotline.Application/Exam/Service/Questions/QuestionTagService.cs
  29. 6 3
      src/Hotline.Application/Exam/Service/Sourcewares/SourcewareCategoryService.cs
  30. 5 2
      src/Hotline.Application/Exam/Service/Sourcewares/SourcewareService.cs
  31. 441 0
      src/Hotline.Application/Exam/Service/TestPapers/TestPaperService.cs
  32. 0 2
      src/Hotline.Application/Hotline.Application.csproj
  33. 1 1
      src/Hotline.Repository.SqlSugar/Exam/Repositories/ExamManages/ExamTagRepository.cs
  34. 25 4
      src/Hotline.Repository.SqlSugar/Exam/Repositories/ExamRepository.cs
  35. 1 1
      src/Hotline.Repository.SqlSugar/Exam/Repositories/Questions/QuestionRepository.cs
  36. 1 1
      src/Hotline.Repository.SqlSugar/Exam/Repositories/Sourcewares/SourcewareCategoryRepository.cs
  37. 1 1
      src/Hotline.Repository.SqlSugar/Exam/Repositories/Sourcewares/SourcewareRepository.cs
  38. 33 1
      src/Hotline.Repository.SqlSugar/Exam/Validators/ExamManages/ExamTagValidator.cs
  39. 3 0
      src/Hotline.Repository.SqlSugar/Exam/Validators/Questions/QuestionAnswerValidator.cs
  40. 4 0
      src/Hotline.Repository.SqlSugar/Exam/Validators/Questions/QuestionKnowladgeValidator.cs
  41. 3 0
      src/Hotline.Repository.SqlSugar/Exam/Validators/Questions/QuestionOptionsValidator.cs
  42. 4 0
      src/Hotline.Repository.SqlSugar/Exam/Validators/Questions/QuestionSourcewareValidator.cs
  43. 3 0
      src/Hotline.Repository.SqlSugar/Exam/Validators/Questions/QuestionTagValidator.cs
  44. 17 3
      src/Hotline.Repository.SqlSugar/Exam/Validators/Questions/QuestionValidator.cs
  45. 4 0
      src/Hotline.Repository.SqlSugar/Exam/Validators/Questions/TagQuestionValidator.cs
  46. 33 3
      src/Hotline.Repository.SqlSugar/Exam/Validators/Sourcewares/SourcewareCategoryValidator.cs
  47. 12 2
      src/Hotline.Repository.SqlSugar/Exam/Validators/Sourcewares/SourcewareValidator.cs
  48. 5 1
      src/Hotline.Repository.SqlSugar/Extensions/ApiServiceExtension.cs
  49. 0 19
      src/Hotline.Repository.SqlSugar/Interfaces/IExamTagRepository.cs
  50. 10 6
      src/Hotline.Repository.SqlSugar/Service/ApiService.cs
  51. 0 1
      src/Hotline.Repository.SqlSugar/Validate/BaseValidator.cs
  52. 6 0
      src/Hotline.Share/Dtos/Questions/QuestionSourcewareDto.cs
  53. 2 1
      src/Hotline.Share/Dtos/TestPapers/ExtractRuleDto.cs
  54. 21 1
      src/Hotline.Share/Dtos/TestPapers/TagQuestionDto.cs
  55. 9 2
      src/Hotline.Share/Dtos/TestPapers/TestPaperDto.cs
  56. 49 0
      src/Hotline.Share/Dtos/TestPapers/TestPaperItemDto.cs
  57. 17 2
      src/Hotline.Share/Dtos/TestPapers/TestPaperRuleDto.cs
  58. 3 2
      src/Hotline.Share/Requests/Exam/ExtractRulePagedRequest.cs
  59. 14 0
      src/Hotline.Share/Requests/Exam/TagQuestionRequest.cs
  60. 4 3
      src/Hotline.Share/Requests/Exam/TestPaperPagedRequest.cs
  61. 1 1
      src/Hotline.Share/Requests/Question/QuestionPagedRequest.cs
  62. 15 0
      src/Hotline.Share/Requests/Sourceware/SourcewarePagedRequest.cs
  63. 2 1
      src/Hotline.Share/ViewResponses/Exam/ExtractRuleViewResponse.cs
  64. 33 0
      src/Hotline.Share/ViewResponses/Exam/TagQuestionViewResponse.cs
  65. 1 1
      src/Hotline.Share/ViewResponses/TestPapers/TestPaperViewResponse.cs
  66. 1 1
      src/Hotline/Exams/ExamManages/ExtractRule.cs
  67. 1 1
      src/Hotline/Exams/Questions/Question.cs
  68. 1 1
      src/Hotline/Exams/TestPapers/TestPaperItem.cs
  69. 1 1
      src/Hotline/Exams/TestPapers/TestPaperRule.cs

+ 9 - 0
src/Exam.Infrastructure.Data/Entity/EntityQueryRequest.cs

@@ -1,4 +1,6 @@
 using System.ComponentModel;
+using System.Linq.Expressions;
+using System.Text.Json.Serialization;
 
 namespace Exam.Infrastructure.Data.Entity
 {
@@ -15,5 +17,12 @@ namespace Exam.Infrastructure.Data.Entity
         /// </summary>
         [Description("主键集合")]
         public List<string> Ids { get; set; }
+
+        /// <summary>
+        /// 查询表达式
+        /// </summary>
+        [Description("查询表达式")]
+        [JsonIgnore]
+        public LambdaExpression Expression { get; set; }
     }
 }

+ 6 - 0
src/Exam.Infrastructure.Validation/Validation/ErrorMessage.cs

@@ -95,6 +95,12 @@
         public const string EmptyUploadFile = "上传文件件为空";
 
         public const string ServiceError = "服务错误";
+
+        public const string IsRefrence = "{0}({1})正在被使用,不可以删除";
+
+        public const string IsSimpleRefrence = "{0}正在被使用,不可以删除";
+
+        public const string RequestFail = "请求失败!";
     }
 
     public static class RegexConstant

+ 19 - 0
src/Exam.Infrastructure.Web/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;
+        }
+    }
+}

+ 15 - 0
src/Exam.Infrastructure.Web/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/Exam.Infrastructure.Web/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;
+        }
+    }
+}

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

@@ -16,8 +16,8 @@ namespace Hotline.Api.Controllers.Exam
 {
     public class ExamTagController : BaseController
     {
-        private IExamTagService _examTagService;
-        private ISessionContext _sessionContext;
+        private readonly IExamTagService _examTagService;
+        private readonly ISessionContext _sessionContext;
         public ExamTagController(IExamTagService examTagService,ISessionContext sessionContext)
         {
             _examTagService = examTagService;

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

@@ -0,0 +1,108 @@
+using Exam.Application.Interface.Exam;
+using Exam.Infrastructure.Data.Entity;
+using Exam.Infrastructure.Data.Extensions;
+using Exam.Share.ViewResponses.Exam;
+using Hotline.Application.Exam.Constants.ApiRoutes;
+using Hotline.Application.Exam.Service.ExamManages;
+using Hotline.Share.Dtos.ExamManages;
+using Hotline.Share.Dtos.TestPapers;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.Requests.Sourceware;
+using Hotline.Share.ViewResponses.Exam;
+using Hotline.Share.ViewResponses.Sourcewares;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using XF.Domain.Authentications;
+
+namespace Hotline.Api.Controllers.Exam
+{
+    public class ExtractRuleController : BaseController
+    {
+        private readonly IExtractRuleService _extractRuleService;
+        private readonly ISessionContext _sessionContext;
+        public ExtractRuleController(IExtractRuleService extractRuleService,ISessionContext sessionContext)
+        {
+            _extractRuleService = extractRuleService;
+            _sessionContext = sessionContext;
+        }
+
+        /// <summary>
+        /// 新增考试标签
+        /// </summary>
+        /// <param name="extractRuleDto"></param>
+        /// <returns></returns>
+        [HttpPost(ExtractRuleApiRoute.Add)]
+        public async Task Add([FromBody] ExtractRuleDto extractRuleDto)
+        {
+            extractRuleDto.InitRequest(_sessionContext);
+            await _extractRuleService.AddAsync(extractRuleDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 修改考试标签
+        /// </summary>
+        /// <param name="extractRuleDto"></param>
+        /// <returns></returns>
+        [HttpPut(ExtractRuleApiRoute.Update)]
+        public async Task Update([FromBody] ExtractRuleDto extractRuleDto)
+        {
+            extractRuleDto.InitRequest(_sessionContext);
+            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 sourcewarePagedRequest)
+        {
+            var sourcewarePageViewResponse = await _extractRuleService.GetPagedListAsync(sourcewarePagedRequest);
+
+            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="tagQuestionRequest"></param>
+        /// <returns></returns>
+        [HttpPost(ExtractRuleApiRoute.GetTagQuestionCount)]
+        public async Task<List<TagQuestionViewResponse>> GetTagQuestionCount([FromBody]TagQuestionRequest tagQuestionRequest)
+        {
+            var tagQuestionViewResponses = await _extractRuleService.GetTagQuestionCount(tagQuestionRequest);
+
+            return tagQuestionViewResponses;
+        }
+    }
+}

+ 68 - 4
src/Hotline.Api/Controllers/Exam/QuestionController.cs

@@ -1,23 +1,87 @@
 using Exam.Application.Interface.Questions;
+using Exam.Infrastructure.Data.Entity;
+using Exam.Infrastructure.Data.Extensions;
+using Exam.Share.ViewResponses.Question;
+using Hotline.Application.Exam.Constants.ApiRoutes;
 using Hotline.Share.Dtos.Questions;
-using Microsoft.AspNetCore.Authorization;
+using Hotline.Share.Requests.Question;
 using Microsoft.AspNetCore.Mvc;
+using XF.Domain.Authentications;
 
 namespace Hotline.Api.Controllers.Exam
 {
     public class QuestionController : BaseController
     {
         private readonly IQuestionService _questionService;
-        public QuestionController(IQuestionService questionService)
+        private readonly ISessionContext _sessionContext;
+        public QuestionController(IQuestionService questionService,ISessionContext sessionContext)
         {
             _questionService = questionService;
+            _sessionContext = sessionContext;
         }
 
-        [HttpPost]
-        [AllowAnonymous]
+        /// <summary>
+        /// 新增题库
+        /// </summary>
+        /// <param name="questionDto"></param>
+        /// <returns></returns>
+        [HttpPost(QuestionApiRoute.Add)]
         public async Task Add([FromBody] QuestionDto questionDto)
         {
+            questionDto.InitRequest(_sessionContext);
             await _questionService.AddAsync(questionDto, HttpContext.RequestAborted);
         }
+
+        /// <summary>
+        /// 修改题库
+        /// </summary>
+        /// <param name="questionDto"></param>
+        /// <returns></returns>
+        [HttpPut(QuestionApiRoute.Update)]
+        public async Task Update([FromBody] QuestionDto questionDto)
+        {
+            questionDto.InitRequest(_sessionContext);
+            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;
+        }
     }
 }

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

@@ -13,8 +13,8 @@ namespace Hotline.Api.Controllers.Exam
 {
     public class SourcewareCategoryController : BaseController
     {
-        private ISourcewareCategoryService _sourcewareCategoryService;
-        private ISessionContext _sessionContext;
+        private readonly ISourcewareCategoryService _sourcewareCategoryService;
+        private readonly ISessionContext _sessionContext;
         public SourcewareCategoryController(ISourcewareCategoryService sourcewareCategoryService,ISessionContext sessionContext)
         {
             _sourcewareCategoryService = sourcewareCategoryService;

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

@@ -12,8 +12,8 @@ namespace Hotline.Api.Controllers.Exam
 {
     public class SourcewaresController : BaseController
     {
-        private ISourcewareService _sourcewareService;
-        private ISessionContext _sessionContext;
+        private readonly ISourcewareService _sourcewareService;
+        private readonly ISessionContext _sessionContext;
 
         public SourcewaresController(ISourcewareService sourcewareService,ISessionContext sessionContext)
         {

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

+ 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
+    {
+    }
+}

+ 24 - 0
src/Hotline.Application/Exam/Extensions/ActionRequestExtensions.cs

@@ -1,4 +1,5 @@
 using Exam.Infrastructure.Data.Entity;
+using Exam.Infrastructure.Data.Interface;
 using Exam.Infrastructure.Extensions;
 using XF.Domain.Authentications;
 
@@ -21,5 +22,28 @@ namespace Exam.Infrastructure.Data.Extensions
             actionRequest.OrgName = sessionContext.OrgName;
             actionRequest.OrgLevel = sessionContext.OrgLevel;
         }
+    
+    
+        public static void InitRequest<TDest,TSource>(this List<TDest> actionRequests, TSource actionRequest) 
+            where TDest : ActionRequest
+            where TSource : ActionRequest
+        {
+            foreach(var item in actionRequests)
+            {
+                item.InitRequest(actionRequest);
+            }
+        }
+
+        public static void InitRequest<TDest, TSource>(this TDest dest, TSource source) 
+            where TDest:ActionRequest
+            where TSource:ActionRequest
+        {
+            dest.UserId = source.UserId;
+            dest.UserName = source.UserName;
+            dest.OrgId = source.OrgId;
+            dest.AreaId = source.AreaId;
+            dest.OrgName = source.OrgName;
+            dest.OrgLevel = source.OrgLevel;
+        }
     }
 }

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

@@ -4,10 +4,12 @@ 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<ExtractRuleDto,ExtractRule>
     {
+        public Task<List<TagQuestionViewResponse>> GetTagQuestionCount(TagQuestionRequest tagQuestionRequest);
     }
 }

+ 11 - 1
src/Hotline.Application/Exam/Mappers/MapperConfigs.cs

@@ -1,4 +1,13 @@
-using Mapster;
+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
 {
@@ -6,6 +15,7 @@ namespace Exam.Application.Mappers
     {
         public void Register(TypeAdapterConfig config)
         {
+            config.ForType<QuestionDto, Question>().IgnoreNullValues(true);
         }
     }
 }

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

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

@@ -0,0 +1,21 @@
+using Exam.Infrastructure.Web.Utilities;
+using Exam.Questions;
+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.IsNotNull(), x => tagQuestionRequest.TagIds.Contains(x.TagId)).ToExpression();
+
+            return expression;
+        }
+    }
+}

+ 12 - 6
src/Hotline.Application/Exam/QueryExtensions/Questions/QuestionQueryExtesions.cs

@@ -2,6 +2,8 @@
 using Exam.Infrastructure.Extensions;
 using Exam.Questions;
 using Hotline.Share.Requests.Question;
+using JiebaNet.Segmenter.Common;
+using SqlSugar;
 using System.Linq.Expressions;
 
 namespace Hotline.Application.Exam.QueryExtensions.Questions
@@ -12,9 +14,10 @@ namespace Hotline.Application.Exam.QueryExtensions.Questions
         {
             Expression<Func<Question, bool>> expression = m => m.Id != null;
 
-            expression = expression.And(x => questionPagedRequest.DifficultyLevel == x.DifficultyLevel, questionPagedRequest.DifficultyLevel);
-            expression = expression.And(x => questionPagedRequest.Title == x.Title, questionPagedRequest.Title);
-
+            Expressionable<Question> expressionable = new Expressionable<Question>();
+            expressionable.AndIF(questionPagedRequest.DifficultyLevel.IsNotNull(), x => questionPagedRequest.DifficultyLevel == x.DifficultyLevel);
+            expressionable.AndIF(questionPagedRequest.Title.IsNotNullOrEmpty(), x => x.Title.Contains(questionPagedRequest.Title));
+            expression = expressionable.ToExpression();
             return expression;
         }
 
@@ -22,8 +25,9 @@ namespace Hotline.Application.Exam.QueryExtensions.Questions
         {
             Expression<Func<QuestionTag, bool>> expression = m => m.Id != null;
 
-            expression = expression.And(x => questionPagedRequest.TagId == x.TagId, questionPagedRequest.TagId);
-
+            Expressionable<QuestionTag> expressionable = new Expressionable<QuestionTag>();
+            expressionable.AndIF(questionPagedRequest.TagId.IsNotNullOrEmpty(), x => questionPagedRequest.TagId == x.TagId);
+            expression = expressionable.ToExpression();
             return expression;
         }
 
@@ -31,7 +35,9 @@ namespace Hotline.Application.Exam.QueryExtensions.Questions
         {
             Expression<Func<ExamTag, bool>> expression = m => m.Id != null;
 
-            expression = expression.And(x => questionPagedRequest.TagId == x.Id, questionPagedRequest.TagId);
+            Expressionable<ExamTag> expressionable = new Expressionable<ExamTag>();
+            expressionable.AndIF(questionPagedRequest.TagId.IsNotNullOrEmpty(), x => questionPagedRequest.TagId == x.Id);
+            expression = expressionable.ToExpression();
 
             return expression;
         }

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

@@ -1,4 +1,5 @@
 using Exam.Infrastructure.Extensions;
+using Exam.Infrastructure.Web.Utilities;
 using Hotline.Exams.Sourcewares;
 using Hotline.Share.Requests.Sourceware;
 using SqlSugar;
@@ -8,16 +9,15 @@ namespace Exam.Application.QueryExtensions.Sourcewares
 {
     public static class SourcewareQueryExtesions
     {
-        public static Expression<Func<Sourceware,bool>> GetExpression(this SourcewarePagedRequest sourcewarePagedRequest)
+        public static Expression<Func<Sourceware, bool>> GetExpression(this SourcewarePagedRequest sourcewarePagedRequest)
         {
-            Expression<Func<Sourceware, bool>> expression = m=>m.Id!=null;
+            Expression<Func<Sourceware, bool>> expression = m => m.Id != null;
 
-            Expressionable<Sourceware> expressionable = new Expressionable<Sourceware>();
 
-            expressionable = expressionable.AndIF(sourcewarePagedRequest.Name.IsNotNullOrEmpty(), m => m.Name.Contains(sourcewarePagedRequest.Name));
-            expressionable = expressionable.AndIF(sourcewarePagedRequest.SourcewareType.IsNotNullOrEmpty(),m=>m.SourcewareType == sourcewarePagedRequest.SourcewareType);
-
-            expression = expressionable.ToExpression();
+            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;
         }

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

@@ -0,0 +1,40 @@
+using Exam.Infrastructure.Data.Entity;
+using Exam.Infrastructure.Extensions;
+using Exam.Infrastructure.Web.Utilities;
+using Exam.TestPapers;
+using Hotline.Share.Requests.Exam;
+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.IsNotNull(), x => entityQueryRequest.Ids.Contains(x.TestPaperId))
+                .ToExpression();
+
+            return expression;
+        }
+    }
+}

+ 6 - 3
src/Hotline.Application/Exam/Service/ExamManages/ExamTagService.cs

@@ -15,6 +15,7 @@ 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;
@@ -28,16 +29,18 @@ namespace Hotline.Application.Exam.Service.ExamManages
     public class ExamTagService : ApiService<ExamTag, ExamTagDto,HotlineDbContext>,IExamTagService, IScopeDependency
     {
         private readonly IExamTagRepository _repository;
-        public ExamTagService(IExamTagRepository repository) : base(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 entity.Adapt<ExamTagDto>();
+            return _mapper.Map<ExamTagDto>(entity);
         }
 
         public async Task<(int, List<ExamTagViewResponse>)> GetListAsync(ExamTagRequest queryRequest)
@@ -81,7 +84,7 @@ namespace Hotline.Application.Exam.Service.ExamManages
                 ParentId = m.ParentId,
                 SortIndex = m.SortIndex,
                 Status = m.Status,
-                Children = m.Children.Adapt<List<ExamTagViewResponse>>()
+                Children = _mapper.Map<List<ExamTagViewResponse>>(m.Children)
             }).ToList();
 
 

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

@@ -0,0 +1,427 @@
+using Exam.Application.Interface.Exam;
+using Exam.ExamManages;
+using Exam.Infrastructure.Data.Entity;
+using Exam.Infrastructure.Data.Extensions;
+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.QueryExtensions.ExamManages;
+using Hotline.Repository.SqlSugar;
+using Hotline.Repository.SqlSugar.DataPermissions;
+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.Dependency;
+
+namespace Hotline.Application.Exam.Service.ExamManages
+{
+    public class ExtractRuleService : ApiService<ExtractRule, ExtractRuleDto, HotlineDbContext>,IExtractRuleService, IScopeDependency
+    {
+        private readonly IExtractRuleRepository _repository;
+        private readonly ITagQuestionRepository _tagQuestionRepository;
+        private readonly IRuleTagRepository _ruleTagRepository;
+        private readonly IDataPermissionFilterBuilder _dataPermissionFilterBuilder;
+        private readonly IServiceProvider _serviceProvider;
+        private readonly IMapper _mapper;
+        public ExtractRuleService(IExtractRuleRepository repository,
+            ITagQuestionRepository tagQuestionRepository,
+            IRuleTagRepository ruleTagRepository,
+             IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider,
+            IMapper mapper) : base(repository, mapper)
+        {
+            _repository = repository;
+            _tagQuestionRepository = tagQuestionRepository;
+            _ruleTagRepository = ruleTagRepository;
+            _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)
+        {
+            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 AddAsync(ExtractRuleDto actionRequest, CancellationToken cancellationToken)
+        {
+            await base.AddAsync(actionRequest, cancellationToken);
+
+            await AddRuleTags(actionRequest,cancellationToken);
+
+            await AddTagQuestions(actionRequest, cancellationToken);
+        }
+
+        /// <summary>
+        /// 修改抽题规则
+        /// </summary>
+        /// <param name="actionRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        public override async Task UpdateAsync(ExtractRuleDto actionRequest, CancellationToken cancellationToken)
+        {
+            await base.UpdateAsync(actionRequest, cancellationToken);
+
+            await ModifyRuleTags(actionRequest, cancellationToken);
+
+            await ModifyTagQuestions(actionRequest, cancellationToken);
+        }
+
+        /// <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.IsNotNull(), 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.IsNotNull(), 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>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(expression);
+            var questionTable = new ExamRepository<Question>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
+
+            var queryable = questionTagTable.LeftJoin(questionTable, (t, q) => t.QuestionId == q.Id)
+                .GroupBy((t,q)=> new { t.TagId, q.QuestionType }).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
+            });
+            return querable;
+        }
+
+        /// <summary>
+        /// 新增标签试题
+        /// </summary>
+        /// <param name="actionRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        private async Task AddTagQuestions(ExtractRuleDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.TagQuestionDtos == null) return;
+
+            var tagQuestionDtos = actionRequest.TagQuestionDtos.Where(x => x.OperationStatus == OperationStatus.Add).ToList();
+
+            var tagQuestions = _mapper.Map<List<TagQuestion>>(tagQuestionDtos);
+
+            tagQuestionDtos.InitRequest(actionRequest);
+
+            tagQuestions.ForEach(x => x.RuleId = actionRequest.Id);
+
+            tagQuestions.ToInsert(tagQuestionDtos);
+
+            await _tagQuestionRepository.AddWithValidateAsync(tagQuestions, cancellationToken);
+        }
+
+        /// <summary>
+        /// 新增规则标签
+        /// </summary>
+        /// <param name="actionRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        private async Task AddRuleTags(ExtractRuleDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.RuleTagDtos == null) return;
+
+            var ruleTagDtos = actionRequest.RuleTagDtos.Where(x => x.OperationStatus == OperationStatus.Add).ToList();
+
+            var ruleTags = _mapper.Map<List<RuleTag>>(ruleTagDtos);
+
+            ruleTagDtos.InitRequest(actionRequest);
+
+            ruleTags.ForEach(x => x.RuleId = actionRequest.Id);
+
+            ruleTags.ToInsert(ruleTagDtos);
+
+            await _ruleTagRepository.AddWithValidateAsync(ruleTags, cancellationToken);
+
+        }
+
+        /// <summary>
+        /// 修改标签试题数
+        /// </summary>
+        /// <param name="actionRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        /// <exception cref="NotImplementedException"></exception>
+        private async Task ModifyTagQuestions(ExtractRuleDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.TagQuestionDtos == null) return;
+
+            await AddTagQuestions(actionRequest, cancellationToken);
+
+            await UpdateTagQuestions(actionRequest ,cancellationToken);
+
+            var ruleTagDtos = actionRequest.TagQuestionDtos.Where(x => x.OperationStatus == OperationStatus.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);
+        }
+
+        /// <summary>
+        /// 修改标签试题数
+        /// </summary>
+        /// <param name="actionRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        /// <exception cref="NotImplementedException"></exception>
+        private async Task UpdateTagQuestions(ExtractRuleDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.TagQuestionDtos == null) return;
+
+            var tagQuestionDtos = actionRequest.TagQuestionDtos.Where(x => x.OperationStatus == OperationStatus.Update).ToList();
+            var ids = tagQuestionDtos.Select(x => x.Id).ToList();
+            var tagQuestions = await _tagQuestionRepository.Queryable().Where(x => ids.Contains(x.Id)).ToListAsync();
+
+            var entitys = new List<TagQuestion>();
+            tagQuestionDtos.ForEach(x =>
+            {
+                var entity = tagQuestions.FirstOrDefault(x => x.Id == x.Id);
+                entity = _mapper.Map(x, entity);
+                entity.RuleId = actionRequest.Id;
+                entitys.Add(entity);
+            });
+            tagQuestionDtos.InitRequest(actionRequest);
+
+            entitys.ToUpdate(tagQuestionDtos);
+
+            await _tagQuestionRepository.UpdateWithValidateAsync(entitys, cancellationToken);
+        }
+
+        /// <summary>
+        /// 修改规则标签
+        /// </summary>
+        /// <param name="actionRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        /// <exception cref="NotImplementedException"></exception>
+        private async Task ModifyRuleTags(ExtractRuleDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.RuleTagDtos == null) return;
+
+            await AddRuleTags(actionRequest, cancellationToken);
+
+            await UpdateRuleTags(actionRequest, cancellationToken);
+
+            var ruleTagDtos = actionRequest.RuleTagDtos.Where(x => x.OperationStatus == OperationStatus.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>();
+
+            await DeleteRuleTags(entityQueyRequest, cancellationToken);
+        }
+
+        private async Task UpdateRuleTags(ExtractRuleDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.RuleTagDtos == null) return;
+
+            var ruleTagDtos = actionRequest.RuleTagDtos.Where(x => x.OperationStatus == OperationStatus.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);
+                entity.RuleId = actionRequest.Id;
+                entitys.Add(entity);
+            });
+            ruleTagDtos.InitRequest(actionRequest);
+
+            entitys.ToUpdate(ruleTagDtos);
+
+            await _ruleTagRepository.UpdateWithValidateAsync(entitys,cancellationToken);
+
+        }
+
+
+        /// <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>(_repository.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>(_repository.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();
+        }
+        #endregion
+    }
+}

+ 0 - 77
src/Hotline.Application/Exam/Service/Questions/QuestionAnswerService.cs

@@ -1,77 +0,0 @@
-using Exam.Application;
-using Exam.Infrastructure.Data.Entity;
-using Exam.Insfrastructure.Service.Service;
-using Exam.Questions;
-using Exam.Share;
-using Hotline.Application.Exam.QueryExtensions.Questions;
-using Hotline.Repository.SqlSugar;
-using Hotline.Repository.SqlSugar.Exam.Interfaces.Questions;
-using Hotline.Share.Dtos.Questions;
-using Hotline.Share.Requests.Question;
-using Mapster;
-using System.ComponentModel;
-using XF.Domain.Dependency;
-
-namespace Hotline.Application.Exam.Service.Questions
-{
-    /// <summary>
-    /// 试题参考答案服务
-    /// </summary>
-    [Description("试题参考答案服务")]
-    public class QuestionAnswerService : ApiService<QuestionAnswer, QuestionAnswerDto,HotlineDbContext>,IQuestionAnswerService, IScopeDependency
-    {
-        private readonly IQuestionAnswerRepository _repository;
-        public QuestionAnswerService(IQuestionAnswerRepository repository) : base(repository)
-        {
-            _repository = repository;
-        }
-
-        public async Task<QuestionAnswerDto> GetAsync(EntityQueryRequest entityQueryRequest)
-        {
-            var entity = await _repository.GetAsync(entityQueryRequest.Id);
-
-            return entity.Adapt<QuestionAnswerDto>();
-        }
-
-        public async Task<(int, List<QuestionAnswerViewResponse>)> GetListAsync(QuestionAnswerPagedRequest queryRequest)
-        {
-            var query = _repository.Queryable();
-
-            var result = await query.Select(m => new QuestionAnswerViewResponse { 
-                				QuestionId = m.QuestionId,
-								Answer = m.Answer,
-								Id = m.Id,
-				            }).ToListAsync();
-
-            var total = await query.CountAsync();
-
-            return (total,result);
-        }
-
-        public async Task<PageViewResponse<QuestionAnswerViewResponse>> GetPagedListAsync(QuestionAnswerPagedRequest queryRequest)
-        {
-            var expression = queryRequest.GetExpression();
-            var questionAnswerTable = _repository.Queryable().Where(expression);
-
-            var queryable = questionAnswerTable.Select((m) => new QuestionAnswerViewResponse
-            {
-                				QuestionId = m.QuestionId,
-								Answer = m.Answer,
-								Id = m.Id,
-				            });
-
-            var list = await queryable.ToPageListAsync(queryRequest.PageIndex, queryRequest.PageSize);
-            var total = await queryable.CountAsync();
-
-            var result = new PageViewResponse<QuestionAnswerViewResponse>
-            {
-                Items = list,
-                Pagination = new Pagination(queryRequest.PageIndex, queryRequest.PageSize, total)
-            };
-
-            return result;
-        }
-    }
-}
-
-

+ 0 - 76
src/Hotline.Application/Exam/Service/Questions/QuestionKnowladgeService.cs

@@ -1,76 +0,0 @@
-using Exam.Infrastructure.Data.Entity;
-using Exam.Insfrastructure.Service.Service;
-using Exam.Questions;
-using Exam.Share;
-using Hotline.Application.Exam.QueryExtensions.Questions;
-using Hotline.Repository.SqlSugar;
-using Hotline.Repository.SqlSugar.Exam.Interfaces.Questions;
-using Hotline.Share.Dtos.Questions;
-using Hotline.Share.Requests.Question;
-using Mapster;
-using System.ComponentModel;
-using XF.Domain.Dependency;
-
-namespace Exam.Application
-{
-    /// <summary>
-    /// 关联知识服务
-    /// </summary>
-    [Description("关联知识服务")]
-    public class QuestionKnowladgeService : ApiService<QuestionKnowladge, QuestionKnowladgeDto,HotlineDbContext>,IQuestionKnowladgeService, IScopeDependency
-    {
-        private readonly IQuestionKnowladgeRepository _repository;
-        public QuestionKnowladgeService(IQuestionKnowladgeRepository repository) : base(repository)
-        {
-            _repository = repository;
-        }
-
-        public async Task<QuestionKnowladgeDto> GetAsync(EntityQueryRequest entityQueryRequest)
-        {
-            var entity = await _repository.GetAsync(entityQueryRequest.Id);
-
-            return entity.Adapt<QuestionKnowladgeDto>();
-        }
-
-        public async Task<(int, List<QuestionKnowladgeViewResponse>)> GetListAsync(QuestionKnowladgePagedRequest queryRequest)
-        {
-            var query = _repository.Queryable();
-
-            var result = await query.Select(m => new QuestionKnowladgeViewResponse { 
-                				QuestionId = m.QuestionId,
-								KnowladgeId = m.KnowladgeId,
-								Id = m.Id,
-				            }).ToListAsync();
-
-            var total = await query.CountAsync();
-
-            return (total,result);
-        }
-
-        public async Task<PageViewResponse<QuestionKnowladgeViewResponse>> GetPagedListAsync(QuestionKnowladgePagedRequest queryRequest)
-        {
-            var expression = queryRequest.GetExpression();
-            var questionKnowladgeTable = _repository.Queryable().Where(expression);
-
-            var queryable = questionKnowladgeTable.Select((m) => new QuestionKnowladgeViewResponse
-            {
-                				QuestionId = m.QuestionId,
-								KnowladgeId = m.KnowladgeId,
-								Id = m.Id,
-				            });
-
-            var list = await queryable.ToPageListAsync(queryRequest.PageIndex, queryRequest.PageSize);
-            var total = await queryable.CountAsync();
-
-            var result = new PageViewResponse<QuestionKnowladgeViewResponse>
-            {
-                Items = list,
-                Pagination = new Pagination(queryRequest.PageIndex, queryRequest.PageSize, total)
-            };
-
-            return result;
-        }
-    }
-}
-
-

+ 0 - 79
src/Hotline.Application/Exam/Service/Questions/QuestionOptionsService.cs

@@ -1,79 +0,0 @@
-using Exam.Application;
-using Exam.Infrastructure.Data.Entity;
-using Exam.Insfrastructure.Service.Service;
-using Exam.Questions;
-using Exam.Share;
-using Hotline.Application.Exam.QueryExtensions.Questions;
-using Hotline.Repository.SqlSugar;
-using Hotline.Repository.SqlSugar.Exam.Interfaces.Questions;
-using Hotline.Share.Dtos.Questions;
-using Hotline.Share.Requests.Question;
-using Mapster;
-using System.ComponentModel;
-using XF.Domain.Dependency;
-
-namespace Hotline.Application.Exam.Service.Questions
-{
-    /// <summary>
-    /// 试题选项服务
-    /// </summary>
-    [Description("试题选项服务")]
-    public class QuestionOptionsService : ApiService<QuestionOptions, QuestionOptionsDto,HotlineDbContext>,IQuestionOptionsService, IScopeDependency
-    {
-        private readonly IQuestionOptionsRepository _repository;
-        public QuestionOptionsService(IQuestionOptionsRepository repository) : base(repository)
-        {
-            _repository = repository;
-        }
-
-        public async Task<QuestionOptionsDto> GetAsync(EntityQueryRequest entityQueryRequest)
-        {
-            var entity = await _repository.GetAsync(entityQueryRequest.Id);
-
-            return entity.Adapt<QuestionOptionsDto>();
-        }
-
-        public async Task<(int, List<QuestionOptionsViewResponse>)> GetListAsync(QuestionOptionsPagedRequest queryRequest)
-        {
-            var query = _repository.Queryable();
-
-            var result = await query.Select(m => new QuestionOptionsViewResponse { 
-                				Content = m.Content,
-								IsAnswer = m.IsAnswer,
-								QuestionId = m.QuestionId,
-								Id = m.Id,
-				            }).ToListAsync();
-
-            var total = await query.CountAsync();
-
-            return (total,result);
-        }
-
-        public async Task<PageViewResponse<QuestionOptionsViewResponse>> GetPagedListAsync(QuestionOptionsPagedRequest queryRequest)
-        {
-            var expression = queryRequest.GetExpression();
-            var questionOptionsTable = _repository.Queryable().Where(expression);
-
-            var queryable = questionOptionsTable.Select((m) => new QuestionOptionsViewResponse
-            {
-                				Content = m.Content,
-								IsAnswer = m.IsAnswer,
-								QuestionId = m.QuestionId,
-								Id = m.Id,
-				            });
-
-            var list = await queryable.ToPageListAsync(queryRequest.PageIndex, queryRequest.PageSize);
-            var total = await queryable.CountAsync();
-
-            var result = new PageViewResponse<QuestionOptionsViewResponse>
-            {
-                Items = list,
-                Pagination = new Pagination(queryRequest.PageIndex, queryRequest.PageSize, total)
-            };
-
-            return result;
-        }
-    }
-}
-
-

+ 398 - 20
src/Hotline.Application/Exam/Service/Questions/QuestionService.cs

@@ -9,6 +9,7 @@ using Exam.Repository.Sqlsugar;
 using Exam.Repository.Sqlsugar.Repositories;
 using Exam.Share.ViewResponses.Question;
 using Hotline.Application.Exam.QueryExtensions.Questions;
+using Hotline.Exams.Sourcewares;
 using Hotline.Repository.SqlSugar;
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Repository.SqlSugar.Exam.Interfaces.Questions;
@@ -16,7 +17,18 @@ using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Share.Dtos.Questions;
 using Hotline.Share.Requests.Question;
 using Mapster;
+using SqlSugar;
 using XF.Domain.Dependency;
+using Exam.Infrastructure.Data.Extensions;
+using Exam.Infrastructure.Data.Interface;
+using Hotline.Import;
+using Exam.Infrastructure.Web.Utilities;
+using XF.Domain.Repository;
+using MapsterMapper;
+using XF.Domain.Entities;
+using Exam.Infrastructure.Extensions;
+using Exam.Infrastructure.Web.Extensions;
+using JiebaNet.Segmenter.Common;
 
 namespace Hotline.Application.Exam.Service.Questions
 {
@@ -31,14 +43,17 @@ namespace Hotline.Application.Exam.Service.Questions
         private readonly IQuestionKnowladgeRepository _questionKnowladgeRepository;
         private readonly IDataPermissionFilterBuilder _dataPermissionFilterBuilder;
         private readonly IServiceProvider _serviceProvider;
+        private readonly IMapper _mapper;
+
         public QuestionService(IQuestionRepository repository,
             IQuestionTagRepository questionTagRepository,
             IQuestionOptionsRepository questionOptionsRepository,
             IQuestionAnswerRepository questionAnswerRepository,
             IQuestionSourcewareRepository questionSourcewareRepository,
             IQuestionKnowladgeRepository questionKnowladgeRepository,
-            IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider
-            ) : base(repository)
+            IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider,
+            IMapper mapper
+            ) : base(repository,mapper)
         {
             _repository = repository;
             _questionTagRepository = questionTagRepository;
@@ -48,6 +63,7 @@ namespace Hotline.Application.Exam.Service.Questions
             _questionKnowladgeRepository = questionKnowladgeRepository;
             _dataPermissionFilterBuilder = dataPermissionFilterBuilder;
             _serviceProvider = serviceProvider;
+            _mapper = mapper;
         }
         #endregion
 
@@ -56,7 +72,20 @@ namespace Hotline.Application.Exam.Service.Questions
         {
             var entity = await _repository.GetAsync(entityQueryRequest.Id);
 
-            var questionDto = entity.Adapt<QuestionDto>();
+            var questionDto = _mapper.Map<QuestionDto>(entity);
+
+            if (questionDto != null)
+            {
+                questionDto.QuestionTagDtos = await GetQuestionTags(entityQueryRequest);
+
+                questionDto.QuestionAnswerDto = await GetQuestionAnswer(entityQueryRequest);
+
+                questionDto.QuestionKnowladgeDtos = await GetKnowladges(entityQueryRequest);
+
+                questionDto.QuestionOptionsDtos = await GetQuestionOptions(entityQueryRequest);
+
+                questionDto.QuestionSourcewareDtos = await GetQuestionSourcewares(entityQueryRequest);
+            }            
 
             return questionDto;
         }
@@ -67,7 +96,7 @@ namespace Hotline.Application.Exam.Service.Questions
             var questionTable = _repository.Queryable().Where(expression);
 
             var questionTagExpression = queryRequest.GetQuestionTagExpression();
-            var questionTagTable = new ExamRepository<QuestionTag>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(questionTagExpression);
+            var questionTagTable = _questionTagRepository.Queryable().Where(questionTagExpression);
 
             var examTagExpression = queryRequest.GetExamTagExpression();
             var examTagTable = new ExamRepository<ExamTag>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(examTagExpression);
@@ -102,20 +131,22 @@ namespace Hotline.Application.Exam.Service.Questions
             var examTagTable = new BaseRepository<ExamTag>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(examTagExpression);
 
 
-            var queryable = questionTable.InnerJoin(questionTagTable, (s, d) => s.Id == d.QuestionId).InnerJoin(examTagTable, (f, s, t) => s.TagId == t.Id).Select((s, d, f) => new QuestionViewResponse
+            var queryable = questionTable.InnerJoin(questionTagTable, (s, d) => s.Id == d.QuestionId).InnerJoin(examTagTable, (s,d, t) => d.TagId == t.Id).Select((s, d, t) => new QuestionViewResponse
             {
                 DifficultyLevel = s.DifficultyLevel,
                 FormalEnable = s.FormalEnable,
                 SimulateEnable = s.SimulateEnable,
                 SortIndex = s.SortIndex,
                 Status = s.Status,
-                Tag = f.Name
+                Tag = t.Name,
+                Title = s.Title,
+                Id = s.Id
             });
 
             var list = await queryable.ToPageListAsync(queryRequest.PageIndex, queryRequest.PageSize);
             var total = await queryable.CountAsync();
 
-            var result = new PageViewResponse<QuestionViewResponse>
+            var result = new QuestionPageViewResponse
             {
                 Items = list,
                 Pagination = new Pagination(queryRequest.PageIndex, queryRequest.PageSize, total)
@@ -138,6 +169,61 @@ namespace Hotline.Application.Exam.Service.Questions
 
             await AddSourcewares(actionRequest, cancellationToken);
         }
+
+        public override async Task UpdateAsync(QuestionDto actionRequest, CancellationToken cancellationToken)
+        {
+            await base.UpdateAsync(actionRequest, cancellationToken);
+
+            await ModifyQuestionTags(actionRequest, cancellationToken);
+
+            await ModifyQuestionOptions(actionRequest, cancellationToken);
+
+            await ModifyQuestionAnswer(actionRequest, cancellationToken);
+
+            await ModifyKnowladges(actionRequest, cancellationToken);
+
+            await ModifySourcewares(actionRequest, cancellationToken);
+        }
+
+        public override async Task DeleteAsync(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
+        {
+            await base.DeleteAsync(entityQueryRequest, cancellationToken);
+
+            var tmpEntityQueryRequest = ExpressionableUtility.CreateExpression<QuestionTag>()
+                .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.QuestionId == entityQueryRequest.Id)
+                .AndIF(entityQueryRequest.Ids.IsNotNull(), x => entityQueryRequest.Ids.Contains(x.QuestionId))
+                .ToEntityQueryRequest<QuestionTag>();
+
+            await DeleteQuestionTags(tmpEntityQueryRequest, cancellationToken);
+
+            tmpEntityQueryRequest = ExpressionableUtility.CreateExpression<QuestionOptions>()
+                .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.QuestionId == entityQueryRequest.Id)
+                .AndIF(entityQueryRequest.Ids.IsNotNull(), x => entityQueryRequest.Ids.Contains(x.QuestionId))
+                .ToEntityQueryRequest<QuestionOptions>();
+
+            await DeleteQuestionOptions(tmpEntityQueryRequest, cancellationToken);
+
+            tmpEntityQueryRequest = ExpressionableUtility.CreateExpression<QuestionAnswer>()
+                .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.QuestionId == entityQueryRequest.Id)
+                .AndIF(entityQueryRequest.Ids.IsNotNull(), x => entityQueryRequest.Ids.Contains(x.QuestionId))
+                .ToEntityQueryRequest<QuestionAnswer>();
+
+            await DeleteQuestionAnswer(tmpEntityQueryRequest, cancellationToken);
+
+            tmpEntityQueryRequest = ExpressionableUtility.CreateExpression<QuestionKnowladge>()
+                .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.QuestionId == entityQueryRequest.Id)
+                .AndIF(entityQueryRequest.Ids.IsNotNull(), x => entityQueryRequest.Ids.Contains(x.QuestionId))
+                .ToEntityQueryRequest<QuestionKnowladge>();
+
+            await DeleteKnowladges(tmpEntityQueryRequest, cancellationToken);
+
+            tmpEntityQueryRequest = ExpressionableUtility.CreateExpression<QuestionSourceware>()
+                .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.QuestionId == entityQueryRequest.Id)
+                .AndIF(entityQueryRequest.Ids.IsNotNull(), x => entityQueryRequest.Ids.Contains(x.QuestionId))
+                .ToEntityQueryRequest<QuestionSourceware>();
+
+            await DeleteSourcewares(tmpEntityQueryRequest, cancellationToken);
+        }
         #endregion
 
         #region private method
@@ -145,9 +231,15 @@ namespace Hotline.Application.Exam.Service.Questions
         {
             if (actionRequest.QuestionTagDtos == null) return;
 
-            var questionTags = actionRequest.QuestionTagDtos.Where(x => x.OperationStatus == OperationStatus.Add).Adapt<QuestionTag>();
+            var questionTagDtos = actionRequest.QuestionTagDtos.Where(x => x.OperationStatus == OperationStatus.Add).ToList();
 
-            questionTags.ToInsert(actionRequest);
+            var questionTags = _mapper.Map<List<QuestionTag>>(questionTagDtos);
+
+            questionTagDtos.InitRequest<QuestionTagDto, QuestionDto>(actionRequest);
+
+            questionTags.ForEach(x => x.QuestionId = actionRequest.Id);
+
+            questionTags.ToInsert(questionTagDtos);
 
             await _questionTagRepository.AddWithValidateAsync(questionTags, cancellationToken);
         }
@@ -160,13 +252,18 @@ namespace Hotline.Application.Exam.Service.Questions
             if (actionRequest.QuestionType == Share.Enums.Exams.EQuestionType.Essay || actionRequest.QuestionType == Share.Enums.Exams.EQuestionType.Blank)
                 return;
 
-            var questionOptionses = actionRequest.QuestionOptionsDtos.Where(x => x.OperationStatus == OperationStatus.Add).Adapt<QuestionOptions>();
+            var questionOptionseDtos = actionRequest.QuestionOptionsDtos.Where(x => x.OperationStatus == OperationStatus.Add).ToList();
+
+            var questionOptionses = _mapper.Map<List<QuestionOptions>>(questionOptionseDtos);
+
+            questionOptionseDtos.InitRequest<QuestionOptionsDto,QuestionDto>(actionRequest);
+
+            questionOptionses.ForEach(x => x.QuestionId = actionRequest.Id);
 
-            questionOptionses.ToInsert(actionRequest);
+            questionOptionses.ToInsert(questionOptionseDtos);
 
             await _questionOptionRepository.AddWithValidateAsync(questionOptionses, cancellationToken);
         }
-
         private async Task AddQuestionAnswer(QuestionDto actionRequest, CancellationToken cancellationToken)
         {
             if (actionRequest.QuestionAnswerDto == null) return;
@@ -175,8 +272,9 @@ namespace Hotline.Application.Exam.Service.Questions
             if (actionRequest.QuestionType == Share.Enums.Exams.EQuestionType.Multi
                 || actionRequest.QuestionType == Share.Enums.Exams.EQuestionType.Single
                 || actionRequest.QuestionType == Share.Enums.Exams.EQuestionType.Judge) return;
-
-            var questionAnswer = actionRequest.QuestionAnswerDto.Adapt<QuestionAnswer>();
+            actionRequest.QuestionAnswerDto.InitRequest<QuestionAnswerDto, QuestionDto>(actionRequest);
+            var questionAnswer = _mapper.Map<QuestionAnswer>(actionRequest.QuestionAnswerDto);
+            questionAnswer.QuestionId = actionRequest.Id;
 
             questionAnswer.ToInsert(actionRequest);
 
@@ -187,23 +285,303 @@ namespace Hotline.Application.Exam.Service.Questions
         {
             if (actionRequest.QuestionSourcewareDtos == null) return;
 
-            var questionSourcewares = actionRequest.QuestionSourcewareDtos.Where(x=>x.OperationStatus == OperationStatus.Add).Adapt<QuestionSourceware>();
+            var questionSourcewareDtos = actionRequest.QuestionSourcewareDtos.Where(x => x.OperationStatus == OperationStatus.Add).ToList();
 
-            questionSourcewares.ToInsert(actionRequest);
+            var questionSourcewares = _mapper.Map<List<QuestionSourceware>>(questionSourcewareDtos);
 
-            await _questionSourcewareRepository.AddWithValidateAsync(questionSourcewares,cancellationToken);
-        }
+            questionSourcewareDtos.InitRequest<QuestionSourcewareDto, QuestionDto>(actionRequest);
 
+            questionSourcewares.ForEach(x => x.QuestionId = actionRequest.Id);
+            
+            questionSourcewares.ToInsert(questionSourcewareDtos);
+
+            await _questionSourcewareRepository.AddWithValidateAsync(questionSourcewares, cancellationToken);
+        }
         private async Task AddKnowladges(QuestionDto actionRequest, CancellationToken cancellationToken)
         {
             if (actionRequest.QuestionKnowladgeDtos == null) return;
 
-            var questionKnowladges = actionRequest.QuestionKnowladgeDtos.Where(x => x.OperationStatus == OperationStatus.Add).Adapt<QuestionKnowladge>();
+            var questionKnoladgeDtos = actionRequest.QuestionKnowladgeDtos.Where(x => x.OperationStatus == OperationStatus.Add).ToList();
+
+            var questionKnowladges = _mapper.Map<List<QuestionKnowladge>>(questionKnoladgeDtos);
 
-            questionKnowladges.ToInsert(actionRequest);
+            questionKnoladgeDtos.InitRequest<QuestionKnowladgeDto, QuestionDto>(actionRequest);
+
+            questionKnowladges.ForEach(x => x.QuestionId = actionRequest.Id);
+
+            questionKnowladges.ToInsert(questionKnoladgeDtos);
 
             await _questionKnowladgeRepository.AddWithValidateAsync(questionKnowladges, cancellationToken);
         }
+        private async Task UpdateSourcewares(QuestionDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.QuestionSourcewareDtos == null) return;
+
+            var questionSourcewareDtos = actionRequest.QuestionSourcewareDtos.Where(x => x.OperationStatus == OperationStatus.Update).ToList();
+
+            var ids = questionSourcewareDtos.Select(x => x.Id);
+
+            var questionSourcewares = await _questionSourcewareRepository.Queryable().Where(x => ids.Contains(x.Id)).ToListAsync();
+
+            questionSourcewares = _mapper.Map<List<QuestionSourcewareDto>,List<QuestionSourceware>>(questionSourcewareDtos,questionSourcewares);
+
+
+            questionSourcewares.ForEach(x => x.QuestionId = actionRequest.Id);
+
+            questionSourcewares.ToUpdate(questionSourcewareDtos);
+
+            await _questionSourcewareRepository.UpdateWithValidateAsync(questionSourcewares, cancellationToken);
+        }
+        private async Task UpdateKnowladges(QuestionDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.QuestionKnowladgeDtos == null) return;
+
+            var questionKnowladgeDtos = actionRequest.QuestionKnowladgeDtos.Where(x => x.OperationStatus == OperationStatus.Add).ToList();
+
+            var ids = questionKnowladgeDtos.Select(x => x.Id);
+
+            var questionKnowladges = await _questionKnowladgeRepository.Queryable().Where(x => ids.Contains(x.Id)).ToListAsync();
+
+            questionKnowladges = _mapper.Map<List<QuestionKnowladgeDto>, List<QuestionKnowladge>>(questionKnowladgeDtos,questionKnowladges);
+
+            questionKnowladges.ForEach(x => x.QuestionId = actionRequest.Id);
+
+            questionKnowladges.ToUpdate(questionKnowladgeDtos);
+
+            await _questionKnowladgeRepository.UpdateWithValidateAsync(questionKnowladges, cancellationToken);
+        }
+        private async Task UpdateQuestionAnswer(QuestionDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.QuestionAnswerDto == null) return;
+
+            // 简单和填空没有选项
+            if (actionRequest.QuestionType == Share.Enums.Exams.EQuestionType.Multi
+                || actionRequest.QuestionType == Share.Enums.Exams.EQuestionType.Single
+                || actionRequest.QuestionType == Share.Enums.Exams.EQuestionType.Judge) return;
+
+            actionRequest.QuestionAnswerDto.InitRequest<QuestionAnswerDto, QuestionDto>(actionRequest);
+
+            var questionAnswer = _mapper.Map<QuestionAnswer>(actionRequest.QuestionAnswerDto);
+
+            questionAnswer.QuestionId = actionRequest.Id;            
+
+            questionAnswer.ToUpdate(actionRequest);
+
+            await _questionAnswerRepository.UpdateWithValidateAsync(questionAnswer, cancellationToken);
+        }
+        private async Task UpdateQuestionOptions(QuestionDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.QuestionOptionsDtos == null) return;
+
+            // 简单和填空没有选项
+            if (actionRequest.QuestionType == Share.Enums.Exams.EQuestionType.Essay || actionRequest.QuestionType == Share.Enums.Exams.EQuestionType.Blank)
+                return;
+
+            var questionOptionsDtos = actionRequest.QuestionOptionsDtos.Where(x => x.OperationStatus == OperationStatus.Update).ToList();
+
+            var ids = questionOptionsDtos.Select(x => x.Id);
+
+            var questionOptionses = await _questionOptionRepository.Queryable().Where(x => ids.Contains(x.Id)).ToListAsync();
+
+            var entitys = new List<QuestionOptions>();
+            foreach(var questionOptionsDto in questionOptionsDtos)
+            {
+                var entity = questionOptionses.FirstOrDefault(x => x.Id == questionOptionsDto.Id);
+                entitys.Add(_mapper.Map<QuestionOptionsDto, QuestionOptions>(questionOptionsDto, entity));
+            }
+
+            //questionOptionses =  _mapper.Map<List<QuestionOptionsDto>, List<QuestionOptions>>(questionOptionsDtos,questionOptionses);
+
+            entitys.ForEach(x => x.QuestionId = actionRequest.Id);
+
+            questionOptionsDtos.InitRequest(actionRequest);
+
+            entitys.ToUpdate(questionOptionsDtos);
+
+            await _questionOptionRepository.UpdateWithValidateAsync(entitys, cancellationToken);
+        }
+
+        private async Task UpdateQuestionTags(QuestionDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.QuestionTagDtos == null) return;
+
+            var questionTagDtos = actionRequest.QuestionTagDtos.Where(x => x.OperationStatus == OperationStatus.Update).ToList();
+
+            var ids = questionTagDtos.Select(x => x.Id);
+
+            var questionTags = await _questionTagRepository.Queryable().Where(x => ids.Contains(x.Id)).ToListAsync();
+
+            questionTags = _mapper.Map<List<QuestionTagDto>, List<QuestionTag>>(questionTagDtos,questionTags);
+
+
+            questionTags.ForEach(x => x.QuestionId = actionRequest.Id);
+
+            questionTags.ToUpdate(questionTagDtos);
+
+            await _questionTagRepository.UpdateWithValidateAsync(questionTags, cancellationToken);
+        }
+        private async Task DeleteSourcewares(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
+        {
+            await _questionSourcewareRepository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
+        }
+        private async Task DeleteKnowladges(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
+        {
+            await _questionKnowladgeRepository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
+        }
+        private async Task DeleteQuestionAnswer(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
+        {
+            await _questionAnswerRepository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
+        }
+        private async Task DeleteQuestionOptions(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
+        {
+            await _questionOptionRepository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
+        }
+        private async Task DeleteQuestionTags(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
+        {
+            await _questionTagRepository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
+        }
+        private async Task ModifySourcewares(QuestionDto actionRequest, CancellationToken cancellationToken)
+        {
+            await AddSourcewares(actionRequest, cancellationToken);
+
+            await UpdateSourcewares(actionRequest, cancellationToken);
+
+            var questionSourcewareDtos = actionRequest.QuestionSourcewareDtos.Where(x => x.OperationStatus == OperationStatus.Delete);
+            var ids = questionSourcewareDtos.Select(m => m.Id);
+            EntityQueryRequest entityQueryRequest = ResovleDelete<QuestionSourceware>(ids);
+
+            await DeleteSourcewares(entityQueryRequest, cancellationToken);
+        }
+
+        private async Task ModifyKnowladges(QuestionDto actionRequest, CancellationToken cancellationToken)
+        {
+            await AddKnowladges(actionRequest, cancellationToken);
+
+            await UpdateKnowladges(actionRequest, cancellationToken);
+
+            var questionKnowladgeDtos = actionRequest.QuestionKnowladgeDtos.Where(x => x.OperationStatus == OperationStatus.Delete);
+            var ids = questionKnowladgeDtos.Select(m => m.Id);
+            EntityQueryRequest entityQueryRequest = ResovleDelete<QuestionKnowladge>(ids);
+
+            await DeleteKnowladges(entityQueryRequest, cancellationToken);
+        }
+
+        private async Task ModifyQuestionAnswer(QuestionDto actionRequest, CancellationToken cancellationToken)
+        {
+            await AddQuestionAnswer(actionRequest, cancellationToken);
+
+            await UpdateQuestionAnswer(actionRequest, cancellationToken);
+
+            if (actionRequest.QuestionAnswerDto != null && actionRequest.QuestionAnswerDto.OperationStatus == OperationStatus.Delete)
+            {
+                var entityQueryRequest = new EntityQueryRequest
+                {
+                    Id = actionRequest.QuestionAnswerDto?.Id
+                };
+
+                await DeleteQuestionAnswer(entityQueryRequest, cancellationToken);
+            }
+
+        }
+
+        private async Task ModifyQuestionOptions(QuestionDto actionRequest, CancellationToken cancellationToken)
+        {
+            await AddQuestionOptions(actionRequest, cancellationToken);
+
+            await UpdateQuestionOptions(actionRequest, cancellationToken);
+
+            var questionOptionsDtos = actionRequest.QuestionOptionsDtos.Where(x => x.OperationStatus == OperationStatus.Delete);
+            var ids = questionOptionsDtos.Select(m => m.Id);
+            EntityQueryRequest entityQueryRequest = ResovleDelete<QuestionOptions>(ids);
+
+            await DeleteQuestionOptions(entityQueryRequest, cancellationToken);
+        }
+
+        private async Task ModifyQuestionTags(QuestionDto actionRequest, CancellationToken cancellationToken)
+        {
+            await AddQuestionTags(actionRequest, cancellationToken);
+
+            await UpdateQuestionTags(actionRequest, cancellationToken);
+
+            var questionTagDtos = actionRequest.QuestionTagDtos.Where(x => x.OperationStatus == OperationStatus.Delete);
+            var ids = questionTagDtos.Select(m => m.Id);
+            EntityQueryRequest entityQueryRequest = ResovleDelete<QuestionTag>(ids);
+
+            await DeleteQuestionTags(entityQueryRequest, cancellationToken);
+        }
+
+        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<QuestionSourcewareDto>> GetQuestionSourcewares(EntityQueryRequest entityQueryRequest)
+        {
+            var questionSourcewareTable = _questionSourcewareRepository.Queryable().Where(x => x.QuestionId == entityQueryRequest.Id);
+
+            var sourcewareTable = new ExamRepository<Sourceware>(_questionSourcewareRepository.UOW, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
+
+            var questionSourcewares = questionSourcewareTable.InnerJoin(sourcewareTable, (q, s) =>
+              q.SourcewareId == s.Id).Select((q, s) => new QuestionSourcewareDto
+              {
+                  Id = q.Id,
+                  SourcewareId = q.SourcewareId,
+                  Sourceware = s.Name
+              });
+
+            return await questionSourcewares.ToListAsync();
+        }
+
+        private async Task<List<QuestionOptionsDto>> GetQuestionOptions(EntityQueryRequest entityQueryRequest)
+        {
+            var questionOptionTable = await _questionOptionRepository.Queryable().Where(x => x.QuestionId == entityQueryRequest.Id).ToListAsync();
+
+            var questionOptions = _mapper.Map<List<QuestionOptionsDto>>(questionOptionTable);
+
+            return questionOptions;
+        }
+
+        private async Task<List<QuestionKnowladgeDto>> GetKnowladges(EntityQueryRequest entityQueryRequest)
+        {
+            var questionKnowladgeTable = await _questionKnowladgeRepository.Queryable().Where(x => x.QuestionId == entityQueryRequest.Id).ToListAsync();
+
+            var questionKnowladges = _mapper.Map<List<QuestionKnowladgeDto>>(questionKnowladgeTable);
+
+            return questionKnowladges;
+        }
+
+        private async Task<QuestionAnswerDto> GetQuestionAnswer(EntityQueryRequest entityQueryRequest)
+        {
+            var questionAnswer = await _questionAnswerRepository.GetAsync(entityQueryRequest.Id);
+
+            var questionAnswerDto = _mapper.Map<QuestionAnswerDto>(questionAnswer);
+
+            return questionAnswerDto;
+        }
+
+        private async Task<List<QuestionTagDto>> GetQuestionTags(EntityQueryRequest entityQueryRequest)
+        {
+            var questionTags = _questionTagRepository.Queryable().Where(x => x.QuestionId == entityQueryRequest.Id);
+
+            var examTags = new ExamRepository<ExamTag>(_questionTagRepository.UOW, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
+
+            var questionTagDtos = questionTags.InnerJoin(examTags, (q, t) => q.TagId == t.Id).Select((q, t) => new QuestionTagDto
+            {
+                Id = q.Id,
+                TagId = q.TagId,
+                Tag = t.Name,
+                QuestionId = q.QuestionId
+            });
+
+            return await questionTagDtos.ToListAsync();
+        }
         #endregion
     }
 }

+ 0 - 76
src/Hotline.Application/Exam/Service/Questions/QuestionSourcewareService.cs

@@ -1,76 +0,0 @@
-using Exam.Application;
-using Exam.Infrastructure.Data.Entity;
-using Exam.Insfrastructure.Service.Service;
-using Exam.Questions;
-using Exam.Share;
-using Hotline.Repository.SqlSugar;
-using Hotline.Repository.SqlSugar.Exam.Interfaces.Questions;
-using Hotline.Share.Dtos.Questions;
-using Hotline.Share.Requests.Question;
-using Mapster;
-using System.ComponentModel;
-using XF.Domain.Dependency;
-
-namespace Hotline.Application.Exam.Service.Questions
-{
-    /// <summary>
-    /// 关联课件服务
-    /// </summary>
-    [Description("关联课件服务")]
-    public class QuestionSourcewareService : ApiService<QuestionSourceware, QuestionSourcewareDto,HotlineDbContext>,IQuestionSourcewareService, IScopeDependency
-    {
-        private readonly IQuestionSourcewareRepository _repository;
-        public QuestionSourcewareService(IQuestionSourcewareRepository repository) : base(repository)
-        {
-            _repository = repository;
-        }
-
-        public async Task<QuestionSourcewareDto> GetAsync(EntityQueryRequest entityQueryRequest)
-        {
-            var entity = await _repository.GetAsync(entityQueryRequest.Id);
-
-            return entity.Adapt<QuestionSourcewareDto>();
-        }
-
-        public async Task<(int, List<QuestionSourcewareViewResponse>)> GetListAsync(QuestionSourcewarePagedRequest queryRequest)
-        {
-            var query = _repository.Queryable();
-
-            var result = await query.Select(m => new QuestionSourcewareViewResponse { 
-                				QuestionId = m.QuestionId,
-								SourcewareId = m.SourcewareId,
-								Id = m.Id,
-				            }).ToListAsync();
-
-            var total = await query.CountAsync();
-
-            return (total,result);
-        }
-
-        public async Task<PageViewResponse<QuestionSourcewareViewResponse>> GetPagedListAsync(QuestionSourcewarePagedRequest queryRequest)
-        {
-            var expression = queryRequest.GetExpression();
-            var questionSourcewareTable = _repository.Queryable().Where(expression);
-
-            var queryable = questionSourcewareTable.Select((m) => new QuestionSourcewareViewResponse
-            {
-                				QuestionId = m.QuestionId,
-								SourcewareId = m.SourcewareId,
-								Id = m.Id,
-				            });
-
-            var list = await queryable.ToPageListAsync(queryRequest.PageIndex, queryRequest.PageSize);
-            var total = await queryable.CountAsync();
-
-            var result = new PageViewResponse<QuestionSourcewareViewResponse>
-            {
-                Items = list,
-                Pagination = new Pagination(queryRequest.PageIndex, queryRequest.PageSize, total)
-            };
-
-            return result;
-        }
-    }
-}
-
-

+ 0 - 75
src/Hotline.Application/Exam/Service/Questions/QuestionTagService.cs

@@ -1,75 +0,0 @@
-using Exam.Infrastructure.Data.Entity;
-using Exam.Insfrastructure.Service.Service;
-using Exam.Questions;
-using Exam.Share;
-using Hotline.Repository.SqlSugar;
-using Hotline.Repository.SqlSugar.Exam.Interfaces.Questions;
-using Hotline.Share.Dtos.Questions;
-using Hotline.Share.Requests.Question;
-using Mapster;
-using System.ComponentModel;
-using XF.Domain.Dependency;
-
-namespace Exam.Application
-{
-    /// <summary>
-    /// 试题标签服务
-    /// </summary>
-    [Description("试题标签服务")]
-    public class QuestionTagService : ApiService<QuestionTag, QuestionTagDto,HotlineDbContext>,IQuestionTagService, IScopeDependency
-    {
-        private readonly IQuestionTagRepository _repository;
-        public QuestionTagService(IQuestionTagRepository repository) : base(repository)
-        {
-            _repository = repository;
-        }
-
-        public async Task<QuestionTagDto> GetAsync(EntityQueryRequest entityQueryRequest)
-        {
-            var entity = await _repository.GetAsync(entityQueryRequest.Id);
-
-            return entity.Adapt<QuestionTagDto>();
-        }
-
-        public async Task<(int, List<QuestionTagViewResponse>)> GetListAsync(QuestionTagPagedRequest queryRequest)
-        {
-            var query = _repository.Queryable();
-
-            var result = await query.Select(m => new QuestionTagViewResponse { 
-                				TagId = m.TagId,
-								QuestionId = m.QuestionId,
-								Id = m.Id,
-				            }).ToListAsync();
-
-            var total = await query.CountAsync();
-
-            return (total,result);
-        }
-
-        public async Task<PageViewResponse<QuestionTagViewResponse>> GetPagedListAsync(QuestionTagPagedRequest queryRequest)
-        {
-            var expression = queryRequest.GetExpression();
-            var questionTagTable = _repository.Queryable().Where(expression);
-
-            var queryable = questionTagTable.Select((m) => new QuestionTagViewResponse
-            {
-                				TagId = m.TagId,
-								QuestionId = m.QuestionId,
-								Id = m.Id,
-				            });
-
-            var list = await queryable.ToPageListAsync(queryRequest.PageIndex, queryRequest.PageSize);
-            var total = await queryable.CountAsync();
-
-            var result = new PageViewResponse<QuestionTagViewResponse>
-            {
-                Items = list,
-                Pagination = new Pagination(queryRequest.PageIndex, queryRequest.PageSize, total)
-            };
-
-            return result;
-        }
-    }
-}
-
-

+ 6 - 3
src/Hotline.Application/Exam/Service/Sourcewares/SourcewareCategoryService.cs

@@ -11,6 +11,7 @@ using Hotline.Repository.SqlSugar.Exam.Interfaces.Sourcewares;
 using Hotline.Share.Dtos.Sourcewares;
 using Hotline.Share.Requests.Sourceware;
 using Mapster;
+using MapsterMapper;
 using System.ComponentModel;
 using XF.Domain.Dependency;
 using XF.Domain.Exceptions;
@@ -24,16 +25,18 @@ namespace Exam.Application.Service.Sourcewares
     public class SourcewareCategoryService : ApiService<SourcewareCategory, SourcewareCategoryDto,HotlineDbContext>,ISourcewareCategoryService, IScopeDependency
     {
         private readonly ISourcewareCategoryRepository _repository;
-        public SourcewareCategoryService(ISourcewareCategoryRepository repository) : base(repository)
+        private readonly IMapper _mapper;
+        public SourcewareCategoryService(ISourcewareCategoryRepository repository,IMapper mapper) : base(repository,mapper)
         {
             _repository = repository;
+            _mapper = mapper;
         }
 
         public async Task<SourcewareCategoryDto> GetAsync(EntityQueryRequest entityQueryRequest)
         {
             var entity = await _repository.GetAsync(entityQueryRequest.Id);
 
-            return entity.Adapt<SourcewareCategoryDto>();
+            return _mapper.Map<SourcewareCategoryDto>(entity);
         }
 
         public async Task<(int, List<SourcewareCategoryViewResponse>)> GetListAsync(SourcewareCategoryRequest queryRequest)
@@ -76,7 +79,7 @@ namespace Exam.Application.Service.Sourcewares
                 ParentId = m.ParentId,
                 SortIndex = m.SortIndex,
                 LastModificationTime = m.LastModificationTime,
-                Children = m.Children.Adapt<List<SourcewareCategoryViewResponse>>()
+                Children = _mapper.Map<List<SourcewareCategoryViewResponse>>(m.Children)
             }).ToList();
 
 

+ 5 - 2
src/Hotline.Application/Exam/Service/Sourcewares/SourcewareService.cs

@@ -11,6 +11,7 @@ using Hotline.Share.Dtos.Sourcewares;
 using Hotline.Share.Requests.Sourceware;
 using Hotline.Share.ViewResponses.Sourcewares;
 using Mapster;
+using MapsterMapper;
 using SqlSugar;
 using System.ComponentModel;
 using XF.Domain.Dependency;
@@ -26,11 +27,13 @@ namespace Hotline.Application.Exam.Service.Sourcewares
         private readonly ISourcewareRepository _repository;
         private readonly IDataPermissionFilterBuilder _datePermissionFilterBuilder;
         private readonly IServiceProvider _serviceProvider;
-        public SourcewareService(ISourcewareRepository repository, IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider) : base(repository)
+        private readonly IMapper _mapper;
+        public SourcewareService(ISourcewareRepository repository, IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider,IMapper mapper) : base(repository, mapper)
         {
             _repository = repository;
             _datePermissionFilterBuilder = dataPermissionFilterBuilder;
             _serviceProvider = serviceProvider;
+            _mapper = mapper;
         }
 
         #region public method
@@ -38,7 +41,7 @@ namespace Hotline.Application.Exam.Service.Sourcewares
         {
             var entity = await _repository.GetAsync(entityQueryRequest.Id);
 
-            return entity.Adapt<SourcewareDto>();
+            return _mapper.Map<SourcewareDto>(entity);
         }
 
         public async Task<(int, List<SourcewareViewResponse>)> GetListAsync(SourcewarePagedRequest queryRequest)

+ 441 - 0
src/Hotline.Application/Exam/Service/TestPapers/TestPaperService.cs

@@ -0,0 +1,441 @@
+using Exam.Application.Interface.TestPapers;
+using Exam.ExamManages;
+using Exam.Infrastructure.Data.Entity;
+using Exam.Infrastructure.Data.Extensions;
+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.TestPaper;
+using Exam.TestPapers;
+using Hotline.Application.Exam.QueryExtensions.TestPapers;
+using Hotline.Repository.SqlSugar;
+using Hotline.Repository.SqlSugar.DataPermissions;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.TestPapers;
+using Hotline.Repository.SqlSugar.Extensions;
+using Hotline.Share.Dtos.TestPapers;
+using Hotline.Share.Requests.Exam;
+using Hotline.Share.Tools;
+using JiebaNet.Segmenter.Common;
+using MapsterMapper;
+using Org.BouncyCastle.Crypto;
+using SqlSugar;
+using SqlSugar.Extensions;
+using XF.Domain.Dependency;
+
+namespace Hotline.Application.Exam.Service.TestPapers
+{
+    public class TestPaperService : ApiService<TestPaper, TestPaperDto, HotlineDbContext>, ITestPaperService, IScopeDependency
+    {
+        private readonly ITestPaperRepository _repository;
+        private readonly ITestPaperItemRepository _testPaperItemRepository;
+        private readonly ITestPaperRuleRepository _testPaperRuleRepository;
+        private readonly ITestPaperRuleTagRepository _testPaperRuleTagRepository;
+        private readonly IDataPermissionFilterBuilder _dataPermissionFilterBuilder;
+        private readonly IServiceProvider _serviceProvider;
+        private readonly IMapper _mapper;
+
+        public TestPaperService(ITestPaperRepository repository,
+            ITestPaperItemRepository testPaperItemRepository,
+            ITestPaperRuleRepository testPaperRuleRepository,
+            ITestPaperRuleTagRepository testPaperRuleTagRepository,
+            IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider,
+            IMapper mapper) : base(repository, mapper)
+        {
+            _repository = repository;
+            _testPaperItemRepository = testPaperItemRepository;
+            _testPaperRuleRepository = testPaperRuleRepository;
+            _testPaperRuleTagRepository = testPaperRuleTagRepository;
+            _dataPermissionFilterBuilder = dataPermissionFilterBuilder;
+            _serviceProvider = serviceProvider;
+            _mapper = mapper;
+        }
+
+        #region public method
+        public async Task<TestPaperDto> GetAsync(EntityQueryRequest entityQueryRequest)
+        {
+            var entity = await _repository.GetAsync(entityQueryRequest.Id);
+
+            var testPaperDto = _mapper.Map<TestPaperDto>(entity);
+
+            testPaperDto.TestPaperItemDtos = await GetTestPaperItemDtos(entityQueryRequest);
+
+            testPaperDto.TestPaperRuleDtos = await GetTestPaperRuleDtos(entityQueryRequest);
+
+            return testPaperDto;
+        }
+
+        public async Task<(int, List<TestPaperViewResponse>)> GetListAsync(TestPaperPagedRequest queryRequest)
+        {
+            ISugarQueryable<TestPaperViewResponse> queryable = QueryResult(queryRequest);
+
+            var result = await queryable.ToListAsync();
+
+            var total = await queryable.CountAsync();
+
+            return (total, result);
+        }
+
+        public async Task<PageViewResponse<TestPaperViewResponse>> GetPagedListAsync(TestPaperPagedRequest queryRequest)
+        {
+            ISugarQueryable<TestPaperViewResponse> queryable = QueryResult(queryRequest);
+
+            var list = await queryable.ToPageListAsync(queryRequest.PageIndex, queryRequest.PageSize);
+            var total = await queryable.CountAsync();
+
+            var result = new TestPaperPageViewResponse
+            {
+                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 AddAsync(TestPaperDto actionRequest, CancellationToken cancellationToken)
+        {
+            await base.AddAsync(actionRequest, cancellationToken);
+
+            await AddTestPaperItems(actionRequest,cancellationToken);
+
+            await AddTestPaperRules(actionRequest, cancellationToken);
+
+            await AddTestPaperRuleTags(actionRequest, cancellationToken);
+        }
+
+        public override async Task UpdateAsync(TestPaperDto actionRequest, CancellationToken cancellationToken)
+        {
+            await base.UpdateAsync(actionRequest, cancellationToken);
+
+            await ModifyTestPaperItems(actionRequest, cancellationToken);
+
+            await ModifyTestPaperRules(actionRequest, cancellationToken);
+
+            await ModifyTestPaperRuleTags(actionRequest, cancellationToken);
+        }
+
+        /// <summary>
+        /// 删除试卷
+        /// </summary>
+        /// <param name="entityQueryRequest"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        public override async Task DeleteAsync(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
+        {
+            var testPaperRuleTags = await GetTestPaperRuleTags(entityQueryRequest);
+
+            await base.DeleteAsync(entityQueryRequest, cancellationToken);
+
+            var tempEntityQueryRequest = ExpressionableUtility.CreateExpression<TestPaperItem>()
+               .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.TestPaperId == entityQueryRequest.Id)
+               .AndIF(entityQueryRequest.Ids.IsNotNull(), x => entityQueryRequest.Ids.Contains(x.TestPaperId)).ToEntityQueryRequest<TestPaperItem>();
+
+            await DeleteTestPaperItems(tempEntityQueryRequest, cancellationToken);
+
+            tempEntityQueryRequest = ExpressionableUtility.CreateExpression<TestPaperRule>()
+                .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.TestPaperId == entityQueryRequest.Id)
+                .AndIF(entityQueryRequest.Ids.IsNotNull(), x => entityQueryRequest.Ids.Contains(x.TestPaperId)).ToEntityQueryRequest<TestPaperRule>();
+
+
+            await DeleteTestPaperRules(tempEntityQueryRequest, cancellationToken);
+
+
+            var ids = testPaperRuleTags.Select(x => x.Id).ToList();
+            tempEntityQueryRequest = ExpressionableUtility.CreateExpression<TestPaperRuleTag>()
+                .AndIF(ids.IsNotNull(), x => ids.Contains(x.Id)).ToEntityQueryRequest<TestPaperRuleTag>();
+
+            await DeleteTestPaperRuleTags(tempEntityQueryRequest, cancellationToken);
+        }
+
+        private async Task<List<TestPaperRuleTag>> GetTestPaperRuleTags(EntityQueryRequest entityQueryRequest)
+        {
+            var expression = entityQueryRequest.GetTestPaperRuleExpression();
+            var testPageRuleTable =  _testPaperRuleRepository.Queryable().Where(expression);
+            var testPageRuleTagTable = _testPaperRuleTagRepository.Queryable();
+
+            var query = testPageRuleTagTable.InnerJoin(testPageRuleTable, (t, r) => t.TestPaperRuleId == r.Id).Select(x => x);
+
+            return await query.ToListAsync();
+        }
+        #endregion
+
+        #region private method
+        /// <summary>
+        /// 获取查询结果
+        /// </summary>
+        /// <param name="queryRequest"></param>
+        /// <returns></returns>
+        private ISugarQueryable<TestPaperViewResponse> QueryResult(TestPaperPagedRequest queryRequest)
+        {
+            var expression = queryRequest.GetExpression();
+            var query = _repository.Queryable().Where(expression);
+            var querable = query.OrderBy(o => o.SortIndex).Select(m => new TestPaperViewResponse
+            {
+                Name = m.Name,
+                ExamType = m.ExamType,
+                Mode = m.Mode,
+                Code = m.Code,
+                Id = m.Id,
+                Status = m.Status,
+                SortIndex = m.SortIndex,
+                Remark = m.Remark,
+            });
+            return querable;
+        }
+
+
+
+        private async Task AddTestPaperRules(TestPaperDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.TestPaperRuleDtos == null) return;
+
+            var testPaperRuleDtos = actionRequest.TestPaperRuleDtos.Where(x => x.OperationStatus == OperationStatus.Add).ToList();
+            var testPaperRules = _mapper.Map<List<TestPaperRule>>(testPaperRuleDtos);
+
+            testPaperRuleDtos.InitRequest(actionRequest);
+
+            testPaperRules.ToInsert(testPaperRuleDtos);
+
+            await _testPaperRuleRepository.AddWithValidateAsync(testPaperRules, cancellationToken);
+
+        }
+
+        private async Task AddTestPaperItems(TestPaperDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.TestPaperItemDtos == null) return;
+
+            var testPaperItemDtos = actionRequest.TestPaperItemDtos.Where(x => x.OperationStatus == OperationStatus.Add).ToList();
+            var testPaperItems = _mapper.Map<List<TestPaperItem>>(testPaperItemDtos);
+
+            testPaperItemDtos.InitRequest(actionRequest);
+
+            testPaperItems.ToInsert(testPaperItemDtos);
+
+            await _testPaperItemRepository.AddWithValidateAsync(testPaperItems, cancellationToken);
+        }
+
+
+        private async Task AddTestPaperRuleTags(TestPaperDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.TestPaperRuleDtos == null) return;
+
+            var testPaperRuleDtos = actionRequest.TestPaperRuleDtos.Where(x=>x.OperationStatus == OperationStatus.Add);
+
+            var testPaperRuleTagDtos = testPaperRuleDtos.Select(n=>new TestPaperRuleTagDto
+            {
+                TagId = n.TagId,
+                TestPaperRuleId = n.Id
+            }).ToList();
+
+            testPaperRuleTagDtos.InitRequest(actionRequest);
+
+            var testPaperRuleTags = _mapper.Map<List<TestPaperRuleTag>>(testPaperRuleTagDtos);
+
+            testPaperRuleTags.ToInsert(testPaperRuleTagDtos);
+
+            await _testPaperRuleTagRepository.AddWithValidateAsync(testPaperRuleTags,cancellationToken);
+        }
+
+        private async Task ModifyTestPaperRules(TestPaperDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.TestPaperRuleDtos == null) return;
+
+            await AddTestPaperRules(actionRequest, cancellationToken);
+
+            await UpdateTestPaperRules(actionRequest,cancellationToken);
+
+            var entityQueryRequest = ExpressionableUtility.CreateExpression<TestPaperRule>()
+                .AndIF(actionRequest.Id.IsNotNullOrEmpty(), x => x.TestPaperId == actionRequest.Id).ToEntityQueryRequest<TestPaperRule>();
+
+            await DeleteTestPaperRules(entityQueryRequest, cancellationToken);
+        }
+
+        private async Task DeleteTestPaperRules(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
+        {
+            await _testPaperRuleRepository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
+        }
+
+        private async Task UpdateTestPaperRules(TestPaperDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.TestPaperRuleDtos == null) return;
+
+            var testPaperRuleDtos = actionRequest.TestPaperRuleDtos.Where(x => x.OperationStatus == OperationStatus.Update).ToList();
+
+            var ids = testPaperRuleDtos.Select(x => x.Id);
+
+            var testPaperRules = await _testPaperRuleRepository.Queryable().Where(x => ids.Contains(x.TestPaperId)).ToListAsync();
+
+            var entities = new List<TestPaperRule>();
+
+            foreach(var testPaperRuleDto in testPaperRuleDtos)
+            {
+                var entity = testPaperRules.FirstOrDefault(x => x.Id == testPaperRuleDto.Id);
+
+                entity = _mapper.Map(testPaperRuleDto, entity);
+
+                entity.TestPaperId = actionRequest.Id;
+            }
+
+            testPaperRuleDtos.InitRequest(actionRequest);
+
+            entities.ToUpdate(testPaperRuleDtos);
+
+            await _testPaperRuleRepository.UpdateWithValidateAsync(entities, cancellationToken);
+            
+        }
+
+        private async Task ModifyTestPaperRuleTags(TestPaperDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.TestPaperRuleDtos == null) return;
+
+            await AddTestPaperRuleTags(actionRequest, cancellationToken);
+
+            await UpdateTestPaperRuleTags(actionRequest, cancellationToken);
+
+            var ids = actionRequest.TestPaperRuleDtos.Where(x => x.OperationStatus == OperationStatus.Delete).Select(x=>x.Id).ToList();
+
+            var entityQueryRequest = ExpressionableUtility.CreateExpression<TestPaperRuleTag>()
+                .AndIF(ids.IsNullOrEmpty(), x => ids.Contains(x.TestPaperRuleId)).ToEntityQueryRequest<TestPaperRuleTag>();
+
+            await DeleteTestPaperRuleTags(entityQueryRequest, cancellationToken);
+        }
+
+        private async Task DeleteTestPaperRuleTags(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
+        {
+            await _testPaperRuleTagRepository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
+        }
+
+        private async Task UpdateTestPaperRuleTags(TestPaperDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.TestPaperRuleDtos == null) return;
+
+            var ruleIds = actionRequest.TestPaperRuleDtos.Where(x => x.OperationStatus == OperationStatus.Update).Select(x=>x.Id).ToList();
+
+            var testPaperRuleTags = await _testPaperRuleTagRepository.Queryable().Where(x => ruleIds.Contains(x.TestPaperRuleId) ).ToListAsync();
+
+            var entities = new List<TestPaperRuleTag>();
+
+            foreach (var testPaperRuleDto in actionRequest.TestPaperRuleDtos)
+            {
+                var entity = testPaperRuleTags.FirstOrDefault(x => x.TestPaperRuleId == testPaperRuleDto.Id);
+
+                entity.TagId = testPaperRuleDto.TagId;
+
+                entity.TestPaperRuleId = testPaperRuleDto.Id;
+            }
+
+            actionRequest.TestPaperRuleDtos.InitRequest(actionRequest);
+
+            entities.ToUpdate(actionRequest.TestPaperRuleDtos);
+
+            await _testPaperRuleTagRepository.UpdateWithValidateAsync(entities, cancellationToken);
+        }
+
+        private async Task ModifyTestPaperItems(TestPaperDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.TestPaperItemDtos == null) return;
+
+            await AddTestPaperItems(actionRequest, cancellationToken);
+
+            await UpdateTestPaperItems(actionRequest, cancellationToken);
+
+            var entityQueryRequest = ExpressionableUtility.CreateExpression<TestPaperItem>()
+                .AndIF(actionRequest.Id.IsNotNullOrEmpty(), x => x.TestPaperId == actionRequest.Id).ToEntityQueryRequest<TestPaperItem>();
+
+            await DeleteTestPaperItems(entityQueryRequest, cancellationToken);
+        }
+
+        private async Task DeleteTestPaperItems(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
+        {
+            await _testPaperItemRepository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
+        }
+
+        private async Task UpdateTestPaperItems(TestPaperDto actionRequest, CancellationToken cancellationToken)
+        {
+            if (actionRequest.TestPaperItemDtos == null) return;
+
+            var testPaperItemDtos = actionRequest.TestPaperItemDtos.Where(x => x.OperationStatus == OperationStatus.Update).ToList();
+
+            var ids = testPaperItemDtos.Select(x => x.Id);
+
+            var testPaperItems = await _testPaperItemRepository.Queryable().Where(x => ids.Contains(x.TestPaperId)).ToListAsync();
+
+            var entities = new List<TestPaperItem>();
+
+            foreach (var testPaperItemDto in testPaperItemDtos)
+            {
+                var entity = testPaperItems.FirstOrDefault(x => x.Id == testPaperItemDto.Id);
+
+                entity = _mapper.Map(testPaperItemDto, entity);
+
+                entity.TestPaperId = actionRequest.Id;
+            }
+
+            testPaperItemDtos.InitRequest(actionRequest);
+
+            entities.ToUpdate(testPaperItemDtos);
+
+            await _testPaperItemRepository.UpdateWithValidateAsync(entities, cancellationToken);
+        }
+
+        /// <summary>
+        /// 获取试卷规则
+        /// </summary>
+        /// <param name="entityQueryRequest"></param>
+        /// <returns></returns>
+        /// <exception cref="NotImplementedException"></exception>
+        private async Task<List<TestPaperRuleDto>> GetTestPaperRuleDtos(EntityQueryRequest entityQueryRequest)
+        {
+            var testPaperRuleTable = _testPaperRuleRepository.Queryable().Where(x => x.TestPaperId == entityQueryRequest.Id);
+
+            var testPaperRuleTags = await GetTestPaperRuleTags(entityQueryRequest);
+
+            var queryable = testPaperRuleTable.Select( t => new TestPaperRuleDto
+            {
+                Id = t.Id,
+                TestPaperId = t.TestPaperId,
+                Count =t.Count,
+                DifficultyLevel =t.DifficultyLevel,
+                SortIndex = t.SortIndex,
+                QuestionType = t.QuestionType,
+            });
+
+            var testPaperRuleTagDtos = _mapper.Map<List<TestPaperRuleTagDto>>(testPaperRuleTags);
+            var testPaperRuleList = await queryable.ToListAsync();
+            testPaperRuleList.ForEach(x => x.TestPaperRuleTagDto = testPaperRuleTagDtos.FirstOrDefault(n => n.TestPaperRuleId == x.Id));
+
+            return testPaperRuleList;
+
+        }
+
+        private async Task<List<TestPaperItemDto>> GetTestPaperItemDtos(EntityQueryRequest entityQueryRequest)
+        {
+            var testPaperRuleTable = _testPaperItemRepository.Queryable().Where(x => x.TestPaperId == entityQueryRequest.Id);
+
+            var questionTable = new ExamRepository<Question>(_testPaperItemRepository.UOW, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
+
+            var queryable = testPaperRuleTable.InnerJoin(questionTable, (t, q) => t.QuestionId == q.Id).Select((t, q) => new TestPaperItemDto
+            {
+                Id = t.Id,
+                QuestionId = t.QuestionId,
+                DifficultyLevel = q.DifficultyLevel,
+                Title = q.Title,
+                TestPaperId = t.TestPaperId,
+                QuestionType = q.QuestionType
+            });
+
+            return await queryable.ToListAsync();
+
+        }
+        #endregion
+    }
+}

+ 0 - 2
src/Hotline.Application/Hotline.Application.csproj

@@ -29,9 +29,7 @@
     <ProjectReference Include="..\XingTang.Sdk\XingTang.Sdk.csproj" />
   </ItemGroup>
   <ItemGroup>
-    <Folder Include="Exam\Service\ExamManages\" />
     <Folder Include="Exam\Service\Practices\" />
-    <Folder Include="Exam\Service\TestPapers\" />
     <Folder Include="Exam\Service\Trains\" />
   </ItemGroup>
 </Project>

+ 1 - 1
src/Hotline.Repository.SqlSugar/Exam/Repositories/ExamManages/ExamTagRepository.cs

@@ -20,7 +20,7 @@ namespace Exam.Repository.Sqlsugar
     {
         public ExamTagRepository(ISugarUnitOfWork<HotlineDbContext> uow, IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider) : base(uow, dataPermissionFilterBuilder, serviceProvider)
         {
-            Validator = new ExamTagValidator();
+            Validator = new ExamTagValidator(this,dataPermissionFilterBuilder, serviceProvider);
         }
 
         public async Task<List<ExamTag>> GetTreeAsync(Expression<Func<ExamTag, bool>> expression)

+ 25 - 4
src/Hotline.Repository.SqlSugar/Exam/Repositories/ExamRepository.cs

@@ -1,4 +1,5 @@
 using Exam.Infrastructure.Data.Entity;
+using Exam.Infrastructure.Extensions;
 using Exam.Infrastructure.Validation.Exceptions;
 using Exam.Infrastructure.Validation.Extensions;
 using Exam.Infrastructure.Validation.Validation;
@@ -7,8 +8,10 @@ using Hotline.Repository.SqlSugar;
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Repository.SqlSugar.Interface;
 using SqlSugar;
+using System.Linq.Expressions;
 using XF.Domain.Entities;
 using XF.Domain.Exceptions;
+using XF.Domain.Repository;
 
 namespace Exam.Repository.Sqlsugar.Repositories
 {
@@ -46,17 +49,35 @@ namespace Exam.Repository.Sqlsugar.Repositories
         }
 
         public async Task DeleteWithValidateAsync(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
-        {
+        {          
+
             if (entityQueryRequest.Ids != null && entityQueryRequest.Ids.Any())
             {
                 var entities = await base.QueryAsync(x => entityQueryRequest.Ids.Contains(x.Id));
 
+                await DoValidationAsync(entities, ValidatorTypeConstants.Remove);
+
                 await base.RemoveRangeAsync(entities, cancellationToken);
             }
-            else
+            else if(entityQueryRequest.Id.IsNotNullOrEmpty())
             {
+                var entity = new TEntity();
+                entity.Id = entityQueryRequest.Id;
+
+                await DoValidationAsync(entity, ValidatorTypeConstants.Remove);
+
                 await base.RemoveAsync(entityQueryRequest.Id);
             }
+            else
+            {
+                if (entityQueryRequest.Expression == null) return;
+
+                var entities = await base.QueryAsync((Expression<Func<TEntity, bool>>)entityQueryRequest.Expression);
+
+                await DoValidationAsync(entities, ValidatorTypeConstants.Remove);
+
+                await base.RemoveAsync((Expression<Func<TEntity, bool>>)entityQueryRequest.Expression, false, cancellationToken);
+            }
         }
 
         public async Task UpdateWithValidateAsync(TEntity entity, CancellationToken cancellationToken)
@@ -89,7 +110,7 @@ namespace Exam.Repository.Sqlsugar.Repositories
             if (!validatorResult.IsValid)
             {
                 var errorMessages = validatorResult.ErrorItems.Select(x => x.ErrorMessage);
-                throw new UserFriendlyException(string.Join(";", errorMessages));
+                throw new UserFriendlyException(ErrorMessage.RequestFail,string.Join(";", errorMessages));
             }
         }
 
@@ -115,7 +136,7 @@ namespace Exam.Repository.Sqlsugar.Repositories
             if (domainException.ValidationErrors.ErrorItems.Any())
             {
                 var errorMessages = domainException.ValidationErrors.ErrorItems.Select(x => x.ErrorMessage);
-                throw new UserFriendlyException(string.Join(";", errorMessages));
+                throw new UserFriendlyException(ErrorMessage.RequestFail, string.Join(";", errorMessages));
             }
         }
         #endregion

+ 1 - 1
src/Hotline.Repository.SqlSugar/Exam/Repositories/Questions/QuestionRepository.cs

@@ -18,7 +18,7 @@ namespace Exam.Repository.Sqlsugar
     {
         public QuestionRepository(ISugarUnitOfWork<HotlineDbContext> uow, IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider) : base(uow, dataPermissionFilterBuilder, serviceProvider)
         {
-            Validator = new QuestionValidator();
+            Validator = new QuestionValidator(this);
         }
     }
 }

+ 1 - 1
src/Hotline.Repository.SqlSugar/Exam/Repositories/Sourcewares/SourcewareCategoryRepository.cs

@@ -20,7 +20,7 @@ namespace Exam.Repository.Sqlsugar.Repositories.Sourcewares
     {
         public SourcewareCategoryRepository(ISugarUnitOfWork<HotlineDbContext> uow, IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider) : base(uow, dataPermissionFilterBuilder, serviceProvider)
         {
-            Validator = new SourcewareCategoryValidator();
+            Validator = new SourcewareCategoryValidator(this,dataPermissionFilterBuilder,serviceProvider);
         }
 
         /// <summary>

+ 1 - 1
src/Hotline.Repository.SqlSugar/Exam/Repositories/Sourcewares/SourcewareRepository.cs

@@ -17,7 +17,7 @@ namespace Exam.Repository.Sqlsugar.Repositories.Sourcewares
     {
         public SourcewareRepository(ISugarUnitOfWork<HotlineDbContext> uow, IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider) : base(uow, dataPermissionFilterBuilder, serviceProvider)
         {
-            Validator = new SourcewareValidator();
+            Validator = new SourcewareValidator(this);
         }
     }
 }

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

@@ -1,15 +1,29 @@
 using Exam.ExamManages;
 using Exam.Infrastructure.Extensions;
 using Exam.Infrastructure.Validation.Validation;
+using Exam.Practices;
+using Exam.Questions;
+using Exam.Repository.Sqlsugar.Repositories;
+using Exam.TestPapers;
 using FluentValidation;
+using Hotline.Exams.Sourcewares;
+using Hotline.Repository.SqlSugar.DataPermissions;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.ExamManages;
 using Hotline.Repository.SqlSugar.Validate;
 
 namespace Exam.Repository.Sqlsugar.Validators.ExamManages
 {
     public class ExamTagValidator:BaseValidator<ExamTag>
     {
-        public ExamTagValidator()
+        private readonly IExamTagRepository _examTagRepository;
+        private readonly IDataPermissionFilterBuilder _dataPermissionFilterBuilder;
+        private readonly IServiceProvider _serviceProvider;
+        public ExamTagValidator(IExamTagRepository examTagRepository, IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider)
         {
+            _examTagRepository = examTagRepository;
+            _dataPermissionFilterBuilder = dataPermissionFilterBuilder;
+            _serviceProvider = serviceProvider;
+
             RuleSet(ValidatorTypeConstants.Create, () =>
             {
                 BaseValidateRule();
@@ -23,6 +37,11 @@ namespace Exam.Repository.Sqlsugar.Validators.ExamManages
 
                 ValidateRuleWithModify();
             });
+
+            RuleSet(ValidatorTypeConstants.Remove, () =>
+            {
+                ValidateRuleWithRemove();
+            });
         }
 
         protected override void BaseValidateRule()
@@ -34,13 +53,26 @@ namespace Exam.Repository.Sqlsugar.Validators.ExamManages
         {
             base.ValidateRuleWithAdd();
             RuleFor(m => m.CreationTime).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(ExamTag.CreationTime))));
+            RuleFor(m => m.Name).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(ExamTag.Name))));
+            RuleFor(m => m.ParentId).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(ExamTag.ParentId))));
+            RuleFor(m => m.Name).Must(v => !_examTagRepository.Queryable().Any(x => x.Name == v)).WithMessage(x => string.Format(ErrorMessage.IsRepeat, x.GetType().GetDescription(nameof(ExamTag.Name))));
         }
 
         protected override void ValidateRuleWithModify()
         {
             base.ValidateRuleWithModify();
             RuleFor(m => m.LastModificationTime).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(ExamTag.LastModificationTime))));
+            RuleFor(m => m.Name).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(ExamTag.Name))));
+            RuleFor(m => m.ParentId).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(ExamTag.ParentId))));
+            RuleFor(m => m.Name).Must((t, v) => !_examTagRepository.Queryable().Any(x => x.Name == v && x.Id != t.Id)).WithMessage(x => string.Format(ErrorMessage.IsRepeat, x.GetType().GetDescription(nameof(ExamTag.Name))));
+        }
 
+        private void ValidateRuleWithRemove()
+        {
+            RuleFor(m => m.Id).Must(v => !new ExamRepository<QuestionTag>(_examTagRepository.UOW, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Any(x => x.TagId == v)).WithMessage(x => string.Format(ErrorMessage.IsRefrence, typeof(ExamTag).GetDescription(),x.Name));
+            RuleFor(m => m.Id).Must(v => !new ExamRepository<PracticeTag>(_examTagRepository.UOW, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Any(x => x.TagId == v)).WithMessage(x => string.Format(ErrorMessage.IsRefrence, typeof(ExamTag).GetDescription(), x.Name));
+            RuleFor(m => m.Id).Must(v => !new ExamRepository<RuleTag>(_examTagRepository.UOW, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Any(x => x.TagId == v)).WithMessage(x => string.Format(ErrorMessage.IsRefrence, typeof(ExamTag).GetDescription(), x.Name));
+            RuleFor(m => m.Id).Must(v => !new ExamRepository<TestPaperRuleTag>(_examTagRepository.UOW, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Any(x => x.TagId == v)).WithMessage(x => string.Format(ErrorMessage.IsRefrence, typeof(ExamTag).GetDescription(), x.Name));
         }
 
     }

+ 3 - 0
src/Hotline.Repository.SqlSugar/Exam/Validators/Questions/QuestionAnswerValidator.cs

@@ -2,6 +2,7 @@ using Exam.Infrastructure.Extensions;
 using Exam.Infrastructure.Validation.Validation;
 using Exam.Questions;
 using FluentValidation;
+using Hotline.Exams.Sourcewares;
 using Hotline.Repository.SqlSugar.Validate;
 
 namespace Exam.Repository.Sqlsugar.Validators.Questions
@@ -28,6 +29,8 @@ namespace Exam.Repository.Sqlsugar.Validators.Questions
         protected override void BaseValidateRule()
         {
             base.BaseValidateRule();
+            RuleFor(m => m.Answer).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(QuestionAnswer))));
+            RuleFor(m => m.QuestionId).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, typeof(Question).GetDescription()));
         }
 
         protected override void ValidateRuleWithAdd()

+ 4 - 0
src/Hotline.Repository.SqlSugar/Exam/Validators/Questions/QuestionKnowladgeValidator.cs

@@ -2,6 +2,8 @@ using Exam.Infrastructure.Extensions;
 using Exam.Infrastructure.Validation.Validation;
 using Exam.Questions;
 using FluentValidation;
+using Hotline.Exams.Sourcewares;
+using Hotline.KnowledgeBase;
 using Hotline.Repository.SqlSugar.Validate;
 
 namespace Exam.Repository.Sqlsugar.Validators.Questions
@@ -28,6 +30,8 @@ namespace Exam.Repository.Sqlsugar.Validators.Questions
         protected override void BaseValidateRule()
         {
             base.BaseValidateRule();
+            RuleFor(m => m.KnowladgeId).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, typeof(Knowledge).GetDescription()));
+            RuleFor(m => m.QuestionId).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, typeof(Question).GetDescription()));
         }
 
         protected override void ValidateRuleWithAdd()

+ 3 - 0
src/Hotline.Repository.SqlSugar/Exam/Validators/Questions/QuestionOptionsValidator.cs

@@ -1,3 +1,4 @@
+using Exam.ExamManages;
 using Exam.Infrastructure.Extensions;
 using Exam.Infrastructure.Validation.Validation;
 using Exam.Questions;
@@ -28,6 +29,8 @@ namespace Exam.Repository.Sqlsugar.Validators.Questions
         protected override void BaseValidateRule()
         {
             base.BaseValidateRule();
+            RuleFor(m => m.Content).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired,x.GetType().GetDescription(nameof(QuestionOptions))));
+            RuleFor(m => m.QuestionId).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, typeof(Question).GetDescription()));
         }
 
         protected override void ValidateRuleWithAdd()

+ 4 - 0
src/Hotline.Repository.SqlSugar/Exam/Validators/Questions/QuestionSourcewareValidator.cs

@@ -1,7 +1,9 @@
+using Exam.ExamManages;
 using Exam.Infrastructure.Extensions;
 using Exam.Infrastructure.Validation.Validation;
 using Exam.Questions;
 using FluentValidation;
+using Hotline.Exams.Sourcewares;
 using Hotline.Repository.SqlSugar.Validate;
 
 namespace Exam.Repository.Sqlsugar.Validators.Questions
@@ -28,6 +30,8 @@ namespace Exam.Repository.Sqlsugar.Validators.Questions
         protected override void BaseValidateRule()
         {
             base.BaseValidateRule();
+            RuleFor(m => m.SourcewareId).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, typeof(Sourceware).GetDescription()));
+            RuleFor(m => m.QuestionId).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, typeof(Question).GetDescription()));
         }
 
         protected override void ValidateRuleWithAdd()

+ 3 - 0
src/Hotline.Repository.SqlSugar/Exam/Validators/Questions/QuestionTagValidator.cs

@@ -1,3 +1,4 @@
+using Exam.ExamManages;
 using Exam.Infrastructure.Extensions;
 using Exam.Infrastructure.Validation.Validation;
 using Exam.Questions;
@@ -28,6 +29,8 @@ namespace Exam.Repository.Sqlsugar.Validators.Questions
         protected override void BaseValidateRule()
         {
             base.BaseValidateRule();
+            RuleFor(m => m.TagId).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, typeof(ExamTag).GetDescription()));
+            RuleFor(m => m.QuestionId).NotEmpty().WithMessage(x=>string.Format(ErrorMessage.IsRequired,typeof(Question).GetDescription()));
         }
 
         protected override void ValidateRuleWithAdd()

+ 17 - 3
src/Hotline.Repository.SqlSugar/Exam/Validators/Questions/QuestionValidator.cs

@@ -2,14 +2,20 @@ using Exam.Infrastructure.Extensions;
 using Exam.Infrastructure.Validation.Validation;
 using Exam.Questions;
 using FluentValidation;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.Questions;
 using Hotline.Repository.SqlSugar.Validate;
+using Newtonsoft.Json.Linq;
+using System.ComponentModel.DataAnnotations;
 
 namespace Exam.Repository.Sqlsugar.Validators.Questions
 {
-    public class QuestionValidator:BaseValidator<Question>
+    public class QuestionValidator : BaseValidator<Question>
     {
-        public QuestionValidator()
+        private readonly IQuestionRepository _questionRepository;
+        public QuestionValidator(IQuestionRepository questionRepository)
         {
+            _questionRepository = questionRepository;
+
             RuleSet(ValidatorTypeConstants.Create, () =>
             {
                 BaseValidateRule();
@@ -28,20 +34,28 @@ namespace Exam.Repository.Sqlsugar.Validators.Questions
         protected override void BaseValidateRule()
         {
             base.BaseValidateRule();
+            RuleFor(m => m.Title).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(Question.Title))));
+            RuleFor(m => m.DifficultyLevel).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(Question.DifficultyLevel))));
+            RuleFor(m => m.FormalEnable).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(Question.FormalEnable))));
+            RuleFor(m => m.SimulateEnable).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(Question.SimulateEnable))));
+
         }
 
         protected override void ValidateRuleWithAdd()
         {
             base.ValidateRuleWithAdd();
             RuleFor(m => m.CreationTime).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(Question.CreationTime))));
+
+            RuleFor(m => m.Title).Must(v => !_questionRepository.Queryable().Any(x => x.Title == v)).WithMessage(x => string.Format(ErrorMessage.IsRepeat, x.GetType().GetDescription(nameof(Question.Title))));
         }
 
         protected override void ValidateRuleWithModify()
         {
             base.ValidateRuleWithModify();
             RuleFor(m => m.LastModificationTime).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(Question.LastModificationTime))));
+            RuleFor(m => m.Title).Must((q, v, ctx) => !_questionRepository.Queryable().Where(x => x.Title ==v  && x.Id  != q.Id).Any()).WithMessage(x => string.Format(ErrorMessage.IsRepeat, x.GetType().GetDescription(nameof(Question.Title))));        }
 
-        }
+       
 
     }
 }

+ 4 - 0
src/Hotline.Repository.SqlSugar/Exam/Validators/Questions/TagQuestionValidator.cs

@@ -1,7 +1,9 @@
 using Exam.ExamManages;
 using Exam.Infrastructure.Extensions;
 using Exam.Infrastructure.Validation.Validation;
+using Exam.Questions;
 using FluentValidation;
+using Hotline.Exams.Sourcewares;
 using Hotline.Repository.SqlSugar.Validate;
 
 namespace Exam.Repository.Sqlsugar.Validators.Questions
@@ -28,6 +30,8 @@ namespace Exam.Repository.Sqlsugar.Validators.Questions
         protected override void BaseValidateRule()
         {
             base.BaseValidateRule();
+            RuleFor(m => m.TagId).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, typeof(ExamTag).GetDescription()));
+            RuleFor(m => m.RuleId).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, typeof(ExtractRule).GetDescription()));
         }
 
         protected override void ValidateRuleWithAdd()

+ 33 - 3
src/Hotline.Repository.SqlSugar/Exam/Validators/Sourcewares/SourcewareCategoryValidator.cs

@@ -1,15 +1,31 @@
+using Exam.ExamManages;
 using Exam.Infrastructure.Extensions;
 using Exam.Infrastructure.Validation.Validation;
+using Exam.Practices;
+using Exam.Questions;
+using Exam.Repository.Sqlsugar.Repositories;
+using Exam.Repository.Sqlsugar.Repositories.Sourcewares;
+using Exam.TestPapers;
 using FluentValidation;
 using Hotline.Exams.Sourcewares;
+using Hotline.Repository.SqlSugar.DataPermissions;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.ExamManages;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.Sourcewares;
 using Hotline.Repository.SqlSugar.Validate;
 
 namespace Exam.Repository.Sqlsugar.Validators.Sourcewares
 {
-    public class SourcewareCategoryValidator:BaseValidator<SourcewareCategory>
+    public class SourcewareCategoryValidator : BaseValidator<SourcewareCategory>
     {
-        public SourcewareCategoryValidator()
+        private readonly ISourcewareCategoryRepository _sourcewareCategoryRepository;
+        private readonly IDataPermissionFilterBuilder _dataPermissionFilterBuilder;
+        private readonly IServiceProvider _serviceProvider;
+        public SourcewareCategoryValidator(ISourcewareCategoryRepository sourcewareCategoryRepository, IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider)
         {
+            _sourcewareCategoryRepository = sourcewareCategoryRepository;
+            _dataPermissionFilterBuilder = dataPermissionFilterBuilder;
+            _serviceProvider = serviceProvider;
+
             RuleSet(ValidatorTypeConstants.Create, () =>
             {
                 BaseValidateRule();
@@ -23,25 +39,39 @@ namespace Exam.Repository.Sqlsugar.Validators.Sourcewares
 
                 ValidateRuleWithModify();
             });
+
+            RuleSet(ValidatorTypeConstants.Remove, () =>
+            {
+                ValidateRuleWithRemove();
+            });
         }
 
         protected override void BaseValidateRule()
         {
             base.BaseValidateRule();
+            RuleFor(m => m.Name).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(SourcewareCategory.Name))));
+            RuleFor(m => m.ParentId).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(SourcewareCategory.ParentId))));
+            RuleFor(m => m.SortIndex).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(SourcewareCategory.SortIndex))));
         }
 
         protected override void ValidateRuleWithAdd()
         {
             base.ValidateRuleWithAdd();
             RuleFor(m => m.CreationTime).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(SourcewareCategory.CreationTime))));
+            RuleFor(m => m.Name).Must(v => !_sourcewareCategoryRepository.Queryable().Any(x => x.Name == v)).WithMessage(x => string.Format(ErrorMessage.IsRepeat, x.GetType().GetDescription(nameof(SourcewareCategory.Name))));
+
         }
 
         protected override void ValidateRuleWithModify()
         {
             base.ValidateRuleWithModify();
             RuleFor(m => m.LastModificationTime).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(SourcewareCategory.LastModificationTime))));
-
+              RuleFor(m => m.Name).Must((t, v) => !_sourcewareCategoryRepository.Queryable().Any(x => x.Name == v && t.Id != x.Id)).WithMessage(x => string.Format(ErrorMessage.IsRepeat, x.GetType().GetDescription(nameof(SourcewareCategory.Name))));
         }
 
+        private void ValidateRuleWithRemove()
+        {
+            RuleFor(m => m.Id).Must(v => !new ExamRepository<Sourceware>(_sourcewareCategoryRepository.UOW, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Any(x => x.CategoryId == v)).WithMessage(x => x.Name.IsNullOrEmpty()? string.Format(ErrorMessage.IsSimpleRefrence, typeof(SourcewareCategory).GetDescription()): string.Format(ErrorMessage.IsRefrence, typeof(SourcewareCategory).GetDescription(), x.Name));
+        }
     }
 }

+ 12 - 2
src/Hotline.Repository.SqlSugar/Exam/Validators/Sourcewares/SourcewareValidator.cs

@@ -1,15 +1,20 @@
 using Exam.Infrastructure.Extensions;
 using Exam.Infrastructure.Validation.Validation;
+using Exam.Repository.Sqlsugar.Repositories.Sourcewares;
 using FluentValidation;
 using Hotline.Exams.Sourcewares;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.Sourcewares;
 using Hotline.Repository.SqlSugar.Validate;
 
 namespace Exam.Repository.Sqlsugar.Validators.Sourcewares
 {
     public class SourcewareValidator:BaseValidator<Sourceware>
     {
-        public SourcewareValidator()
+        private readonly ISourcewareRepository _sourcewareRepository;
+        public SourcewareValidator(ISourcewareRepository sourcewareRepository)
         {
+            _sourcewareRepository = sourcewareRepository;
+
             RuleSet(ValidatorTypeConstants.Create, () =>
             {
                 BaseValidateRule();
@@ -34,13 +39,18 @@ namespace Exam.Repository.Sqlsugar.Validators.Sourcewares
         {
             base.ValidateRuleWithAdd();
             RuleFor(m => m.CreationTime).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(Sourceware.CreationTime))));
+            RuleFor(m => m.Name).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(Sourceware.Name))));
+            RuleFor(m => m.CategoryId).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, typeof(SourcewareCategory).GetDescription()));
+            RuleFor(m => m.Name).Must(v => !_sourcewareRepository.Queryable().Any(x => x.Name == v)).WithMessage(x => string.Format(ErrorMessage.IsRepeat, x.GetType().GetDescription(nameof(Sourceware.Name))));
         }
 
         protected override void ValidateRuleWithModify()
         {
             base.ValidateRuleWithModify();
             RuleFor(m => m.LastModificationTime).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(Sourceware.LastModificationTime))));
-
+            RuleFor(m => m.Name).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(Sourceware.Name))));
+            RuleFor(m => m.CategoryId).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, typeof(SourcewareCategory).GetDescription()));
+            RuleFor(m => m.Name).Must((t,v) => !_sourcewareRepository.Queryable().Any(x => x.Name == v && t.Id != x.Id)).WithMessage(x => string.Format(ErrorMessage.IsRepeat, x.GetType().GetDescription(nameof(Sourceware.Name))));
         }
 
     }

+ 5 - 1
src/Hotline.Repository.SqlSugar/Extensions/ApiServiceExtension.cs

@@ -1,4 +1,5 @@
 using Exam.Infrastructure.Data.Entity;
+using Exam.Infrastructure.Extensions;
 using XF.Domain.Entities;
 using XF.Domain.Repository;
 
@@ -9,7 +10,10 @@ namespace Hotline.Repository.SqlSugar.Extensions
         public static void ToInsert<T,TActionRequest>(this T entity, TActionRequest actionRequest) where T : class, IEntity<string>, new()
         where TActionRequest : ActionRequest
         {
-            entity.Id = Guid.NewGuid().ToString();
+            if (entity.Id.IsNullOrEmpty())
+            {
+                entity.Id = actionRequest.Id ?? Guid.NewGuid().ToString();
+            }
 
             if (entity is CreationEntity)
             {

+ 0 - 19
src/Hotline.Repository.SqlSugar/Interfaces/IExamTagRepository.cs

@@ -1,19 +0,0 @@
-using Exam.ExamManages;
-using Hotline.Repository.SqlSugar;
-using Hotline.Repository.SqlSugar.Interface;
-using System.ComponentModel;
-using XF.Domain.Repository;
-
-namespace Hotline.Repository.Sqlsugar
-{
-    /// <summary>
-    /// 考试标签仓储接口
-    /// </summary>
-    [Description("考试标签仓储接口")]
-    public interface IExamTagRepository:IRepository<ExamTag>,IExamRepository<ExamTag,HotlineDbContext>
-    {
-       
-    }
-}
-
-

+ 10 - 6
src/Hotline.Repository.SqlSugar/Service/ApiService.cs

@@ -4,6 +4,7 @@ using Hotline.Repository.SqlSugar.Entitys;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Repository.SqlSugar.Interface;
 using Mapster;
+using MapsterMapper;
 using SqlSugar;
 using XF.Domain.Entities;
 
@@ -17,10 +18,13 @@ namespace Exam.Insfrastructure.Service.Service
         private readonly IExamRepository<T, TDBContext> _repository;
 
         private AbstractValidator<T> _validator;
-        public ApiService(IExamRepository<T, TDBContext> repository)
+
+        private IMapper _mapper;
+        public ApiService(IExamRepository<T, TDBContext> repository,IMapper mapper)
         {
             _repository = repository;
             _validator = _repository.Validator;
+            _mapper = mapper;
         } 
         #endregion
 
@@ -33,7 +37,7 @@ namespace Exam.Insfrastructure.Service.Service
         /// <returns></returns>
         public virtual async Task AddAsync(TActionRequest actionRequest, CancellationToken cancellationToken)
         {
-            var entity = actionRequest.Adapt<T>();
+            var entity = _mapper.Map<T>(actionRequest);
 
             entity.ToInsert(actionRequest);
 
@@ -49,7 +53,7 @@ namespace Exam.Insfrastructure.Service.Service
         /// <returns></returns>
         public virtual async Task AddAsync(List<TActionRequest> actionRequests, CancellationToken cancellationToken)
         {
-            var entities = actionRequests.Adapt<List<T>>();
+            var entities = _mapper.Map<List<T>>(actionRequests);
 
             entities.ToInsert(actionRequests);
 
@@ -77,7 +81,7 @@ namespace Exam.Insfrastructure.Service.Service
         {
             var entity = await _repository.GetAsync(actionRequest.Id);
 
-            entity = actionRequest.Adapt<TActionRequest, T>(entity);
+            entity = _mapper.Map<TActionRequest, T>(actionRequest,entity);
 
             entity.ToUpdate(actionRequest);
 
@@ -95,7 +99,7 @@ namespace Exam.Insfrastructure.Service.Service
             var ids = actionRequests.Select(x => x.Id).ToList();
             var entities = await _repository.QueryAsync(x => ids.Contains(x.Id));
 
-            entities = actionRequests.Adapt<List<TActionRequest>, List<T>>(entities);
+            entities = _mapper.Map<List<TActionRequest>, List<T>>(actionRequests,entities);
 
             entities.ToUpdate(actionRequests);
 
@@ -113,7 +117,7 @@ namespace Exam.Insfrastructure.Service.Service
             var ids = statusActionRequests.Select(x => x.Id).ToList();
             var entities = await _repository.QueryAsync(x => ids.Contains(x.Id));
 
-            entities = statusActionRequests.Adapt<List<StatusActionRequest>, List<T>>(entities);
+            entities = _mapper.Map<List<StatusActionRequest>, List<T>>(statusActionRequests,entities);
 
             entities.ToUpdate(statusActionRequests);
 

+ 0 - 1
src/Hotline.Repository.SqlSugar/Validate/BaseValidator.cs

@@ -19,7 +19,6 @@ namespace Hotline.Repository.SqlSugar.Validate
         protected virtual void BaseValidateRule()
         {
             RuleFor(m => m.Id).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(CreationEntity.Id))));
-            RuleFor(m => m.CreationTime).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(CreationEntity.CreationTime))));
             RuleFor(m => m.CreatorId).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(CreationEntity.CreatorId))));
             RuleFor(m => m.CreatorName).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(CreationEntity.CreatorName))));
             RuleFor(m => m.CreatorOrgId).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(CreationEntity.CreatorOrgId))));

+ 6 - 0
src/Hotline.Share/Dtos/Questions/QuestionSourcewareDto.cs

@@ -20,5 +20,11 @@ namespace Hotline.Share.Dtos.Questions
         /// </summary>
         [Description("课件Id")]
         public string SourcewareId { get; set; }
+
+        /// <summary>
+        /// 课件
+        /// </summary>
+        [Description("课件")]
+        public string Sourceware { get; set; }
     }
 }

+ 2 - 1
src/Hotline.Share/Dtos/TestPapers/ExtractRuleDto.cs

@@ -1,4 +1,5 @@
 using Exam.Infrastructure.Data.Entity;
+using Hotline.Share.Enums.Exams;
 using System.ComponentModel;
 
 namespace Hotline.Share.Dtos.TestPapers
@@ -25,7 +26,7 @@ namespace Hotline.Share.Dtos.TestPapers
         /// 规则类型
         /// </summary>
         [Description("规则类型")]
-        public int Type { get; set; }
+        public EExamType RuleType { get; set; }
 
         /// <summary>
         /// 备注

+ 21 - 1
src/Hotline.Share/Dtos/TestPapers/TagQuestionDto.cs

@@ -1,4 +1,6 @@
 using Exam.Infrastructure.Data.Entity;
+using Exam.Infrastructure.Extensions;
+using Hotline.Share.Enums.Exams;
 using System.ComponentModel;
 
 namespace Hotline.Share.Dtos.TestPapers
@@ -15,6 +17,12 @@ namespace Hotline.Share.Dtos.TestPapers
         [Description("标签Id")]
         public string TagId { get; set; }
 
+        /// <summary>
+        /// 标签
+        /// </summary>
+        [Description("标签")]
+        public string Tag { get; set; }
+
         /// <summary>
         /// 规则Id
         /// </summary>
@@ -25,7 +33,19 @@ namespace Hotline.Share.Dtos.TestPapers
         /// 试题类型
         /// </summary>
         [Description("试题类型")]
-        public int Type { get; set; }
+        public EQuestionType QuestionType { get; set; }
+
+        /// <summary>
+        /// 试题类型
+        /// </summary>
+        [Description("试题类型")]
+        public string QuestionTypeDes
+        {
+            get
+            {
+                return QuestionType.GetDescription();
+            }
+        }
 
         /// <summary>
         /// 试题数量

+ 9 - 2
src/Hotline.Share/Dtos/TestPapers/TestPaperDto.cs

@@ -1,4 +1,5 @@
 using Exam.Infrastructure.Data.Entity;
+using Hotline.Share.Enums.Exams;
 using System.ComponentModel;
 
 namespace Hotline.Share.Dtos.TestPapers
@@ -25,13 +26,13 @@ namespace Hotline.Share.Dtos.TestPapers
         /// 考试类型
         /// </summary>
         [Description("考试类型")]
-        public int Type { get; set; }
+        public EExamType Type { get; set; }
 
         /// <summary>
         /// 组卷方式
         /// </summary>
         [Description("组卷方式")]
-        public int Mode { get; set; }
+        public EExamMode Mode { get; set; }
 
         /// <summary>
         /// 备注
@@ -50,5 +51,11 @@ namespace Hotline.Share.Dtos.TestPapers
         /// </summary>
         [Description("试卷详情")]
         public List<TestPaperItemDto> TestPaperItemDtos { get; set; }
+
+        /// <summary>
+        /// 试卷组卷规则
+        /// </summary>
+        [Description("试卷组卷规则")]
+        public List<TestPaperRuleDto> TestPaperRuleDtos { get; set; }
     }
 }

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

@@ -1,4 +1,7 @@
 using Exam.Infrastructure.Data.Entity;
+using Exam.Infrastructure.Extensions;
+using Hotline.Share.Dtos.TrCallCenter;
+using Hotline.Share.Enums.Exams;
 using System.ComponentModel;
 
 namespace Hotline.Share.Dtos.TestPapers
@@ -14,5 +17,51 @@ namespace Hotline.Share.Dtos.TestPapers
         /// </summary>
         [Description("试题Id")]
         public string QuestionId { get; set; }
+
+        /// <summary>
+        /// 试卷Id
+        /// </summary>
+        [Description("试卷Id")]
+        public string TestPaperId { get; set; }
+
+        /// <summary>
+        /// 难度
+        /// </summary>
+        [Description("难度")]
+        public EDifficultyLevel DifficultyLevel { get; set; }
+
+        /// <summary>
+        /// 难度
+        /// </summary>
+        [Description("难度")]
+        public string DifficultyLevelDesc { get
+            {
+                return DifficultyLevel.GetDescription();
+            } 
+        }
+
+        /// <summary>
+        /// 题型
+        /// </summary>
+        [Description("题型")]
+        public EQuestionType QuestionType { get; set; }
+
+        /// <summary>
+        /// 题型
+        /// </summary>
+        [Description("题型")]
+        public string QuestionTypeDes
+        {
+            get
+            {
+                return QuestionType.GetDescription();
+            }
+        }
+
+        /// <summary>
+        /// 标题
+        /// </summary>
+        [Description("标题")]
+        public string Title { get; set; }
     }
 }

+ 17 - 2
src/Hotline.Share/Dtos/TestPapers/TestPaperRuleDto.cs

@@ -1,5 +1,7 @@
 using Exam.Infrastructure.Data.Entity;
+using Hotline.Share.Enums.Exams;
 using System.ComponentModel;
+using System.Text.Json.Serialization;
 
 namespace Hotline.Share.Dtos.TestPapers
 {
@@ -19,18 +21,31 @@ namespace Hotline.Share.Dtos.TestPapers
         /// 试题类型
         /// </summary>
         [Description("试题类型")]
-        public int Type { get; set; }
+        public EQuestionType QuestionType { get; set; }
 
         /// <summary>
         /// 试题难度
         /// </summary>
         [Description("试题难度")]
-        public int DifficultyLevel { get; set; }
+        public EDifficultyLevel DifficultyLevel { get; set; }
 
         /// <summary>
         /// 试题数量
         /// </summary>
         [Description("试题数量")]
         public int Count { get; set; }
+
+        /// <summary>
+        /// 组卷规则标签
+        /// </summary>
+        [Description("组卷规则标签")]
+        [JsonIgnore]
+        public TestPaperRuleTagDto TestPaperRuleTagDto { get; set; }
+
+        /// <summary>
+        /// 标签Id
+        /// </summary>
+        [Description("标签Id")]
+        public string TagId { get; set; }
     }
 }

+ 3 - 2
src/Hotline.Share/Requests/Exam/ExtractRulePagedRequest.cs

@@ -1,4 +1,5 @@
 using Exam.Infrastructure.Data.Interface;
+using Hotline.Share.Enums.Exams;
 
 namespace Hotline.Share.Requests.Exam
 {
@@ -8,8 +9,8 @@ namespace Hotline.Share.Requests.Exam
 
         public string Name { get; set; }
 
-        public int Type { get; set; }
+        public EExamType? ExamType { get; set; }
 
-        public int Status { get; set; }
+        public EPublicStatus? Status { get; set; }
     }
 }

+ 14 - 0
src/Hotline.Share/Requests/Exam/TagQuestionRequest.cs

@@ -0,0 +1,14 @@
+using Exam.Infrastructure.Data.Interface;
+using System.ComponentModel;
+
+namespace Hotline.Share.Requests.Exam
+{
+    public class TagQuestionRequest : IQueryRequest
+    {
+        /// <summary>
+        /// 标签Id
+        /// </summary>
+        [Description("标签Id")]
+        public List<string> TagIds { get; set; }
+    }
+}

+ 4 - 3
src/Hotline.Share/Requests/Exam/TestPaperPagedRequest.cs

@@ -1,4 +1,5 @@
 using Exam.Infrastructure.Data.Interface;
+using Hotline.Share.Enums.Exams;
 
 namespace Hotline.Share.Requests.Exam
 {
@@ -8,10 +9,10 @@ namespace Hotline.Share.Requests.Exam
 
         public string Name { get; set; }
 
-        public int Type { get; set; }
+        public EExamType? ExamType { get; set; }
 
-        public int Mode { get; set; }
+        public EExamMode? ExamMode { get; set; }
 
-        public int Status { get; set; }
+        public EPublicStatus? Status { get; set; }
     }
 }

+ 1 - 1
src/Hotline.Share/Requests/Question/QuestionPagedRequest.cs

@@ -7,7 +7,7 @@ namespace Hotline.Share.Requests.Question
     {
         public string TagId { get; set; }
 
-        public EDifficultyLevel DifficultyLevel { get; set; }
+        public EDifficultyLevel? DifficultyLevel { get; set; }
 
         public string Title { get; set; }
     }

+ 15 - 0
src/Hotline.Share/Requests/Sourceware/SourcewarePagedRequest.cs

@@ -1,11 +1,26 @@
 using Exam.Infrastructure.Data.Interface;
+using System.ComponentModel;
 
 namespace Hotline.Share.Requests.Sourceware
 {
     public record SourcewarePagedRequest:PagedRequest, IQueryRequest
     {
+        /// <summary>
+        /// 名称
+        /// </summary>
+        [Description("名称")]
         public string Name { get; set; }
 
+        /// <summary>
+        /// 课件格式
+        /// </summary>
+        [Description("课件格式")]
         public string SourcewareType { get; set; }
+
+        /// <summary>
+        /// 课件分类
+        /// </summary>
+        [Description("课件分类")]
+        public string SourcewareCategoryId { get; set; }
     }
 }

+ 2 - 1
src/Hotline.Share/ViewResponses/Exam/ExtractRuleViewResponse.cs

@@ -1,4 +1,5 @@
 using Exam.Infrastructure.Data.Interface;
+using Hotline.Share.Enums.Exams;
 using System.ComponentModel;
 
 namespace Exam.Share.ViewResponses.Exam
@@ -27,7 +28,7 @@ namespace Exam.Share.ViewResponses.Exam
         /// 状态
         /// </summary>
         [Description("状态")]
-        public int Status { get; set; }
+        public EPublicStatus Status { get; set; }
 
         /// <summary>
         /// 排序

+ 33 - 0
src/Hotline.Share/ViewResponses/Exam/TagQuestionViewResponse.cs

@@ -0,0 +1,33 @@
+using Exam.Infrastructure.Data.Interface;
+using Hotline.Share.Enums.Exams;
+using System.ComponentModel;
+
+namespace Hotline.Share.ViewResponses.Exam
+{
+    public class TagQuestionViewResponse : IViewResponse
+    {
+        /// <summary>
+        /// 标签Id
+        /// </summary>
+        [Description("标签Id")]
+        public string TagId { get; set; }
+
+        /// <summary>
+        /// 试题类型
+        /// </summary>
+        [Description("试题类型")]
+        public EQuestionType QuestionType { get; set; }
+
+        /// <summary>
+        /// 总数
+        /// </summary>
+        [Description("总数")]
+        public int TotalCount { get; set; }
+
+        /// <summary>
+        /// 主键
+        /// </summary>
+        [Description("主键")]
+        public string Id { get; set; }
+    }
+}

+ 1 - 1
src/Hotline.Share/ViewResponses/TestPapers/TestPaperViewResponse.cs

@@ -64,7 +64,7 @@ namespace Exam.Share.ViewResponses.TestPaper
         /// 状态
         /// </summary>
         [Description("状态")]
-        public int Status { get; set; }
+        public EPublicStatus Status { get; set; }
 
         /// <summary>
         /// 排序

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

@@ -30,7 +30,7 @@ namespace Exam.ExamManages
         /// </summary>
         [SugarColumn(ColumnDescription ="规则类型")]
         [Description("规则类型")]
-        public EExamMode RuleType { get; set; }
+        public EExamType RuleType { get; set; }
 
         /// <summary>
         /// 备注

+ 1 - 1
src/Hotline/Exams/Questions/Question.cs

@@ -52,7 +52,7 @@ namespace Exam.Questions
         /// </summary>
         [SugarColumn(ColumnDescription = "备注")]
         [Description("备注")]
-        public string Remark { get; set; }
+        public string? Remark { get; set; }
 
         /// <summary>
         /// 权重

+ 1 - 1
src/Hotline/Exams/TestPapers/TestPaperItem.cs

@@ -22,6 +22,6 @@ namespace Exam.TestPapers
         /// </summary>
         [SugarColumn(ColumnDescription = "试卷Id")]
         [Description("试卷Id")]
-        public string TestPaperItemId { get; set; }
+        public string TestPaperId { get; set; }
     }
 }

+ 1 - 1
src/Hotline/Exams/TestPapers/TestPaperRule.cs

@@ -23,7 +23,7 @@ namespace Exam.TestPapers
         /// </summary>
         [SugarColumn(ColumnDescription = "试题类型")]
         [Description("试题类型")]
-        public EExamType ExamType { get; set; }
+        public EQuestionType QuestionType { get; set; }
 
         /// <summary>
         /// 试题难度