Procházet zdrojové kódy

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

Dun.Jason před 1 měsícem
rodič
revize
851acd7162
66 změnil soubory, kde provedl 598 přidání a 297 odebrání
  1. 1 12
      src/Hotline.Api/Controllers/Exam/TrainRecordController.cs
  2. 13 0
      src/Hotline.Api/Controllers/Exam/TrainTemplateController.cs
  3. 10 0
      src/Hotline.Api/Controllers/Snapshot/RedPackController.cs
  4. 1 1
      src/Hotline.Api/Controllers/Snapshot/SnapshotController.cs
  5. 1 1
      src/Hotline.Api/Controllers/Snapshot/SnapshotOrderController.cs
  6. 0 2
      src/Hotline.Api/StartupExtensions.cs
  7. 1 1
      src/Hotline.Api/config/appsettings.Development.json
  8. 15 0
      src/Hotline.Application/Exam/Core/Constants/BusiConstants.cs
  9. 26 0
      src/Hotline.Application/Exam/Core/Utilities/CodeUtility.cs
  10. 1 7
      src/Hotline.Application/Exam/Interface/Train/ITrainRecordService.cs
  11. 1 0
      src/Hotline.Application/Exam/QueryExtensions/ExamManages/ExamManageQueryExtensions.cs
  12. 1 1
      src/Hotline.Application/Exam/QueryExtensions/Trains/TrainPlanQueryExtensions.cs
  13. 4 3
      src/Hotline.Application/Exam/QueryExtensions/Trains/TrainTemplateQueryExtensions.cs
  14. 70 8
      src/Hotline.Application/Exam/Service/ExamManages/ExamManageService.cs
  15. 65 15
      src/Hotline.Application/Exam/Service/Trains/TrainPlanService.cs
  16. 18 45
      src/Hotline.Application/Exam/Service/Trains/TrainRecordService.cs
  17. 33 1
      src/Hotline.Application/Exam/Service/Trains/TrainTemplateService.cs
  18. 7 7
      src/Hotline.Application/Identity/IdentityAppService.cs
  19. 1 1
      src/Hotline.Application/OrderApp/Handlers/SnapshotHandler/GuiderSystemTimeoutHandler.cs
  20. 1 1
      src/Hotline.Application/OrderApp/IOrderApplication.cs
  21. 2 1
      src/Hotline.Application/OrderApp/OrderApplication.cs
  22. 0 0
      src/Hotline.Application/Snapshot/DefaultSnapshotApplication.cs
  23. 7 0
      src/Hotline.Application/Snapshot/IRedPackApplication.cs
  24. 29 6
      src/Hotline.Application/Snapshot/IndustryApplication.cs
  25. 1 1
      src/Hotline.Application/Snapshot/Notifications/SnapshotHandler.cs
  26. 25 7
      src/Hotline.Application/Snapshot/RedPackApplication.cs
  27. 16 13
      src/Hotline.Application/Snapshot/SnapshotApplicationBase.cs
  28. 19 23
      src/Hotline.Application/Snapshot/SnapshotThirdAccountSupplier.cs
  29. 0 0
      src/Hotline.Application/Snapshot/ZiGongSnapshotApplication.cs
  30. 1 1
      src/Hotline.Repository.SqlSugar/Exam/Repositories/Trains/TrainPlanRepository.cs
  31. 1 1
      src/Hotline.Repository.SqlSugar/Exam/Repositories/Trains/TrainTemplateRepository.cs
  32. 3 0
      src/Hotline.Repository.SqlSugar/Exam/Validators/Trains/TrainPlanTemplateValidator.cs
  33. 11 1
      src/Hotline.Repository.SqlSugar/Exam/Validators/Trains/TrainPlanValidator.cs
  34. 3 0
      src/Hotline.Repository.SqlSugar/Exam/Validators/Trains/TrainRecordItemValidator.cs
  35. 3 0
      src/Hotline.Repository.SqlSugar/Exam/Validators/Trains/TrainRecordValidator.cs
  36. 10 1
      src/Hotline.Repository.SqlSugar/Exam/Validators/Trains/TrainTemplateValidator.cs
  37. 5 2
      src/Hotline.Repository.SqlSugar/Orders/CitizenRepository.cs
  38. 0 28
      src/Hotline.Repository.SqlSugar/Snapshot/SnapshotUserInfoRepository.cs
  39. 1 0
      src/Hotline.Share/Dtos/Snapshot/IndustryFileDto.cs
  40. 1 1
      src/Hotline.Share/Dtos/Snapshot/OrderDto.cs
  41. 5 0
      src/Hotline.Share/Dtos/Snapshot/VolunteerReportDto.cs
  42. 30 5
      src/Hotline.Share/Dtos/Trains/TrainPracticeDto.cs
  43. 12 0
      src/Hotline.Share/Dtos/Trains/TrainRecordDto.cs
  44. 2 2
      src/Hotline.Share/Enums/Exams/EExamMode.cs
  45. 7 1
      src/Hotline.Share/Enums/User/EThirdType.cs
  46. 6 0
      src/Hotline.Share/Requests/Exam/GenerateTestPaperRequest.cs
  47. 12 0
      src/Hotline.Share/Tools/StringExtensions.cs
  48. 23 0
      src/Hotline.WeChat/TestService.cs
  49. 2 1
      src/Hotline.WeChat/WeChatService.cs
  50. 7 0
      src/Hotline/Caching/Interfaces/ISysDicDataCacheManager.cs
  51. 4 0
      src/Hotline/Caching/Services/SysDicDataCacheManager.cs
  52. 1 1
      src/Hotline/Exams/Trains/TrainPlanTemplate.cs
  53. 24 2
      src/Hotline/Orders/Citizen.cs
  54. 2 2
      src/Hotline/Orders/ICitizenRepository.cs
  55. 1 0
      src/Hotline/SeedData/SystemDicDataSeedData.cs
  56. 6 0
      src/Hotline/Snapshot/CommunityInfo.cs
  57. 0 18
      src/Hotline/Snapshot/Interfaces/ISnapshotUserInfoRepository.cs
  58. 0 49
      src/Hotline/Snapshot/SnapshotUserInfo.cs
  59. 1 1
      src/Hotline/ThirdAccountDomainServices/Interfaces/IThirdAccountSupplier.cs
  60. 1 1
      src/Hotline/ThirdAccountDomainServices/ThirdAccounSupplierFactory.cs
  61. 9 0
      test/Hotline.Tests/Application/IndustryApplicationTest.cs
  62. 5 4
      test/Hotline.Tests/Application/InviteCodeApplicationTest.cs
  63. 18 6
      test/Hotline.Tests/Application/OrderSnapshotApplicationTest.cs
  64. 4 4
      test/Hotline.Tests/Application/OrderVisitApplicationTest.cs
  65. 5 5
      test/Hotline.Tests/Application/SnapshotApplicationTest.cs
  66. 3 3
      test/Hotline.Tests/TestBase.cs

+ 1 - 12
src/Hotline.Api/Controllers/Exam/TrainRecordController.cs

@@ -33,17 +33,6 @@ namespace Hotline.Api.Controllers.Exam
             return trainRecordPageViewResponse as TrainRecordPageViewResponse;
         }
 
-        /// <summary>
-        /// 获取培训试题
-        /// </summary>
-        /// <param name="examQuestionRequest"></param>
-        /// <returns></returns>
-        [HttpGet(TrainRecordApiRoute.GetTrainQuestionDto)]
-        public async Task<TrainQuestionDto> GetTrainQuestionDto([FromQuery] TrainQuestionRequest trainQuestionRequest)
-        {
-            return await _trainRecordService.GetTrainQuestionDto(trainQuestionRequest);
-        }
-
         /// <summary>
         /// 初次培训
         /// </summary>
@@ -51,7 +40,7 @@ namespace Hotline.Api.Controllers.Exam
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
         [HttpPost(TrainRecordApiRoute.Train)]
-        public async Task<List<TrainPracticeOptionsDto>> Train([FromBody] AddTrainDto addTrainDto, CancellationToken cancellationToken)
+        public async Task<TrainPracticeDto> Train([FromBody] AddTrainDto addTrainDto, CancellationToken cancellationToken)
         {
             return await _trainRecordService.TrainAsync(addTrainDto, cancellationToken);
         }

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

@@ -81,5 +81,18 @@ namespace Hotline.Api.Controllers.Exam
 
             return trainTemplateDto;
         }
+
+        /// <summary>
+        /// 获取培训模版
+        /// </summary>
+        /// <param name="trainTemplatePagedRequest"></param>
+        /// <returns></returns>
+        [HttpGet(TrainTemplateApiRoute.GetList)]
+        public async Task<List<TrainTemplateViewResponse>> GetList([FromQuery] TrainTemplatePagedRequest trainTemplatePagedRequest)
+        {
+            var result = await _trainTemplateService.GetListAsync(trainTemplatePagedRequest);
+
+            return result.Item2;
+        }
     }
 }

+ 10 - 0
src/Hotline.Api/Controllers/Snapshot/RedPackController.cs

@@ -62,6 +62,16 @@ public class RedPackController : BaseController
     {
         return await _redPackApplication.GetRedPackSpecialAuditBaseDataAsync(id);
     }
+
+    /// <summary>
+    /// 撤销特殊红包审批
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpPut("audit/special/revocation")]
+    public async Task RevocationRedPackSpecialAuditAsync([FromBody] IList<string> ids)
+        => await _redPackApplication.RevocationRedPackSpecialAuditAsync(ids);
+
     #endregion
 
     #region 红包审核

+ 1 - 1
src/Hotline.Api/Controllers/Snapshot/SnapshotController.cs

@@ -316,7 +316,7 @@ public class SnapshotController : BaseController
     /// <param name="dto"></param>
     /// <returns></returns>
     [HttpPost("report")]
-    [LogFilter("志愿者上报")]
+    [LogFilterAlpha("志愿者上报")]
     public async Task<AddVolunteerReportOutDto> AddVolunteerReportAsync([FromBody] AddVolunteerReportInDto dto)
         => await _snapshotApplication.AddVolunteerReportAsync(dto, HttpContext.RequestAborted);
 

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

@@ -276,7 +276,7 @@ public class SnapshotOrderController : BaseController
     [HttpPut("guider/timeout/{id}")]
     public async Task HandleFromWanggeyuanToMaskAsync(string id)
     {
-        await _orderApplication.HandleFromWanggeyuanToMaskAsync(id, HttpContext.RequestAborted);
+        await _orderApplication.HandleFromWanggeyuanToMaskAsync(id, string.Empty, HttpContext.RequestAborted);
     }
 
     /// <summary>

+ 0 - 2
src/Hotline.Api/StartupExtensions.cs

@@ -215,8 +215,6 @@ internal static class StartupExtensions
         services.AddScoped<IExpireTimeSupplier, WorkDaySupplier>();
         services.AddScoped<IExpireTimeSupplier, HourSupplier>();
 
-        services.AddScoped<IThirdIdentiyService, WeChatService>();
-
         services.AddWeChatService();
 
         services.AddScoped<IGuiderSystemService, TiqnQueService>();

+ 1 - 1
src/Hotline.Api/config/appsettings.Development.json

@@ -111,7 +111,7 @@
     }
   },
   "DatabaseConfiguration": {
-    "ApplyDbMigrations": false,
+    "ApplyDbMigrations": true,
     "ApplySeed": false
   },
   "MqConfiguration": {

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

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

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

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

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

@@ -18,19 +18,13 @@ namespace Train.Application.Interface.Train
         /// <returns></returns>
         Task CompleteTrainRecordAsync(CompleteTrainRecordDto completeTrainRecordDto,CancellationToken cancellationToken);
 
-        /// <summary>
-        /// 获取培训试题
-        /// </summary>
-        /// <param name="examQuestionRequest"></param>
-        /// <returns></returns>
-        Task<TrainQuestionDto> GetTrainQuestionDto(TrainQuestionRequest trainQuestionRequest);
 
         /// <summary>
         /// 初次培训
         /// </summary>
         /// <param name="addUserTrainItemDto"></param>
         /// <returns></returns>
-        Task<List<TrainPracticeOptionsDto>> TrainAsync(AddTrainDto addTrainDto, CancellationToken cancellationToken);
+        Task<TrainPracticeDto> TrainAsync(AddTrainDto addTrainDto, CancellationToken cancellationToken);
 
         /// <summary>
         /// 获取培训习题

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

@@ -29,6 +29,7 @@ namespace Hotline.Application.Exam.QueryExtensions.ExamManages
             expression = ExpressionableUtility.CreateExpression<ExamManage>()
                          .AndIF(generateExamTestPaperRequest.ExamManageId.IsNotNullOrEmpty(), x => x.Id == generateExamTestPaperRequest.ExamManageId)
                          .AndIF(generateExamTestPaperRequest.TestPaperId.IsNotNullOrEmpty(), x => x.TestPaperId == generateExamTestPaperRequest.TestPaperId)
+                         .AndIF(generateExamTestPaperRequest.ExtractRuleId.IsNotNullOrEmpty(),x=>x.ExtractRuleId == generateExamTestPaperRequest.ExtractRuleId)
                          .ToExpression();
 
             return expression;

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

@@ -30,7 +30,7 @@ namespace Hotline.Application.Exam.QueryExtensions.Trains
             Expression<Func<TrainPlanTemplate, bool>> expression = m => m.Id != null;
 
             expression = ExpressionableUtility.CreateExpression<TrainPlanTemplate>()
-                .AndIF(trainPlanPagedRequest.TrainTemplateIds.IsNotNullOrEmpty(), x => trainPlanPagedRequest.TrainTemplateIds.Contains(x.TraimTemplateId))
+                .AndIF(trainPlanPagedRequest.TrainTemplateIds.IsNotNullOrEmpty(), x => trainPlanPagedRequest.TrainTemplateIds.Contains(x.TrainTemplateId))
                 .ToExpression();
 
             return expression;

+ 4 - 3
src/Hotline.Application/Exam/QueryExtensions/Trains/TrainTemplateQueryExtensions.cs

@@ -1,4 +1,5 @@
 using DocumentFormat.OpenXml.Presentation;
+using Exam.Infrastructure.Extensions;
 using Exam.Infrastructure.Web.Utilities;
 using Exam.Trains;
 using Hotline.Share.Requests.Train;
@@ -14,13 +15,13 @@ namespace Hotline.Application.Exam.QueryExtensions.Trains
             Expression<Func<TrainTemplate, bool>> expression = m => m.Id!=null;
 
             expression = ExpressionableUtility.CreateExpression<TrainTemplate>()
-                .AndIF(trainTemplatePagedRequest.Code.IsNotEmpty(), x => x.Code.Contains(trainTemplatePagedRequest.Code))
-                .AndIF(trainTemplatePagedRequest.Name.IsNotEmpty(), x => x.Name.Contains(trainTemplatePagedRequest.Name))
+                .AndIF(trainTemplatePagedRequest.Code.IsNotNullOrEmpty(), x => x.Code.Contains(trainTemplatePagedRequest.Code))
+                .AndIF(trainTemplatePagedRequest.Name.IsNotNullOrEmpty(), x => x.Name.Contains(trainTemplatePagedRequest.Name))
                 .AndIF(trainTemplatePagedRequest.IsContainsPractice.IsNotNull(), x => x.IsContainsPractice == trainTemplatePagedRequest.IsContainsPractice)
                 .AndIF(trainTemplatePagedRequest.Status.IsNotNull(),x=>x.Status==trainTemplatePagedRequest.Status)
                 .AndIF(trainTemplatePagedRequest.MinCreationTime.IsNotNull(), x => x.CreationTime >= trainTemplatePagedRequest.MinCreationTime)
                 .AndIF(trainTemplatePagedRequest.MaxCreationTime.IsNotNull(),x=>x.CreationTime<= trainTemplatePagedRequest.MaxCreationTime)
-                .AndIF(trainTemplatePagedRequest.CreatorName.IsNotNull(), x => x.CreatorName == trainTemplatePagedRequest.CreatorName)
+                .AndIF(trainTemplatePagedRequest.CreatorName.IsNotNullOrEmpty(), x => x.CreatorName == trainTemplatePagedRequest.CreatorName)
                 .ToExpression();
 
             return expression;

+ 70 - 8
src/Hotline.Application/Exam/Service/ExamManages/ExamManageService.cs

@@ -111,11 +111,20 @@ namespace Hotline.Application.Exam.Service.ExamManages
         {
             var entity = await _repository.GetAsync(entityQueryRequest.Id);
 
+            if (entity == null) return null;
+
             var questionDto = _mapper.Map<ExamManageDto>(entity);
 
+          
+
             if (questionDto != null)
             {
-                questionDto.ExamQuestionScoreDtos = await GetExamQuestionScores(entityQueryRequest);
+                questionDto.ExamQuestionScoreDtos = await GetExamQuestionScores(entityQueryRequest, entity);
+                if (entity.Mode == Share.Enums.Exams.EExamMode.Random)
+                {
+                    questionDto.TestPaperId = entity.ExtractRuleId;
+
+                }
 
                 questionDto.UserExamDtos = await GetUserExams(entityQueryRequest);
             }
@@ -160,20 +169,33 @@ namespace Hotline.Application.Exam.Service.ExamManages
 
             ResolveQuestionId(actionRequest, id);
 
+            ResolveRandomExtractRuleId(actionRequest);
+
             base.Entity.ExamQuestionScores = await AddExamQuestionScores(actionRequest, cancellationToken);
 
             base.Entity.UserExams = await AddUserExam(actionRequest, cancellationToken);
 
             await base.Complete(base.Entity, OperationConstant.Create);
 
-            await GenerateExamQuestion(new GenerateExamTestPaperRequest { 
-                TestPaperId = actionRequest.TestPaperId ,
+            await GenerateExamQuestion(new GenerateExamTestPaperRequest
+            {
+                TestPaperId = actionRequest.Mode == Share.Enums.Exams.EExamMode.Manual ? actionRequest.TestPaperId : null,
+                ExtractRuleId = actionRequest.Mode == Share.Enums.Exams.EExamMode.Random ? actionRequest.TestPaperId : null,
                 ExamManageId = id
             }, cancellationToken);
 
             return id;
         }
 
+        private void ResolveRandomExtractRuleId(AddExamManageDto actionRequest)
+        {
+            if (actionRequest.Mode == Share.Enums.Exams.EExamMode.Random)
+            {
+                base.Entity.ExtractRuleId = actionRequest.TestPaperId;
+                base.Entity.TestPaperId = string.Empty;
+            }
+        }
+
         public override async Task UpdateAsync(UpdateExamManageDto actionRequest, CancellationToken cancellationToken)
         {
             CalcuteTotalScore(actionRequest);
@@ -184,6 +206,8 @@ namespace Hotline.Application.Exam.Service.ExamManages
 
             ResolveQuestionId(actionRequest, actionRequest.Id);
 
+            ResolveRandomExtractRuleId(actionRequest);
+
             MapAddUpdateExamManage(actionRequest);
 
             base.Entity.ExamQuestionScores = await ModifyExamQuestionScores(actionRequest, cancellationToken);
@@ -193,8 +217,9 @@ namespace Hotline.Application.Exam.Service.ExamManages
             await base.Complete(base.Entity, OperationConstant.Update);
 
             await GenerateExamQuestion(new GenerateExamTestPaperRequest { 
-                TestPaperId = actionRequest.TestPaperId, 
-               ExamManageId = actionRequest.Id
+                TestPaperId = actionRequest.Mode == Share.Enums.Exams.EExamMode.Manual? actionRequest.TestPaperId:null,
+                ExtractRuleId = actionRequest.Mode == Share.Enums.Exams.EExamMode.Random ? actionRequest.TestPaperId : null,
+                ExamManageId = actionRequest.Id
             }, cancellationToken);
 
         }
@@ -543,19 +568,56 @@ namespace Hotline.Application.Exam.Service.ExamManages
             return await userExamDtos.ToListAsync();
         }
 
-        private async Task<List<ExamQuestionScoreDto>> GetExamQuestionScores(EntityQueryRequest entityQueryRequest)
+        private async Task<List<ExamQuestionScoreDto>> GetExamQuestionScores(EntityQueryRequest entityQueryRequest,ExamManage examManage)
         {
             var examQuestionScores = _examQuestionScoreRepository.Queryable().Where(x => x.ExamManageId == entityQueryRequest.Id);
 
-
             var examQuestionScoreDtos = examQuestionScores.Select(q => new ExamQuestionScoreDto
             {
                 Id = q.Id,
+                QuestionType = q.QuestionType,
                 ExamManageId = q.ExamManageId,
                 Score = q.Score
             });
 
-            return await examQuestionScoreDtos.ToListAsync();
+            if(examManage.Mode == Share.Enums.Exams.EExamMode.Random)
+            {
+                var tagQuestionRepository = new ExamRepository<TagQuestion>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider);
+                var tagQuestionTable = tagQuestionRepository.Queryable().Where(x => x.RuleId == examManage.ExtractRuleId);
+
+                var result = examQuestionScoreDtos.InnerJoin(tagQuestionTable, (e, t) => e.QuestionType == t.QuestionType).Select((e, t) => new ExamQuestionScoreDto
+                {
+                    Id = e.Id,
+                    QuestionType = e.QuestionType,
+                    ExamManageId = e.ExamManageId,
+                    Score = e.Score,
+                    Count = t.Count
+                });
+                return await result.ToListAsync();
+            }
+            else
+            {
+                var testPaperItemRepository = new ExamRepository<TestPaperItem>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider);
+                var testPaperItemTable = testPaperItemRepository.Queryable().Where(x => x.TestPaperId == examManage.TestPaperId);
+
+                var result = examQuestionScoreDtos.InnerJoin(testPaperItemTable, (e, t) => e.QuestionType == t.QuestionType)
+                    .GroupBy((e, t) => new
+                    {
+                        Id = e.Id,
+                        QuestionType = e.QuestionType,
+                        ExamManageId = e.ExamManageId,
+                        Score = e.Score,
+                    })
+                    .Select((e, t) => new ExamQuestionScoreDto
+                {
+                    Id = e.Id,
+                    QuestionType = e.QuestionType,
+                    ExamManageId = e.ExamManageId,
+                    Score = e.Score,
+                    Count = SqlFunc.AggregateCount(t.Id)
+                });
+                return await result.ToListAsync();
+            }           
         }
         private void CalcuteTotalScore(AddExamManageDto actionRequest)
         {

+ 65 - 15
src/Hotline.Application/Exam/Service/Trains/TrainPlanService.cs

@@ -1,4 +1,5 @@
-using Exam.Application.Interface.Train;
+using DocumentFormat.OpenXml.Office2010.Excel;
+using Exam.Application.Interface.Train;
 using Exam.Infrastructure.Data.Entity;
 using Exam.Infrastructure.Enums;
 using Exam.Infrastructure.Extensions;
@@ -9,6 +10,8 @@ using Exam.Repository.Sqlsugar.Repositories;
 using Exam.Share;
 using Exam.Share.ViewResponses.Train;
 using Exam.Trains;
+using Hotline.Application.Exam.Core.Constants;
+using Hotline.Application.Exam.Core.Utilities;
 using Hotline.Application.Exam.Extensions;
 using Hotline.Application.Exam.QueryExtensions.Trains;
 using Hotline.Exams.Trains;
@@ -92,10 +95,14 @@ namespace Hotline.Application.Exam.Service.Trains
         {
             base.StartTran();
 
+            actionRequest.Code = await GenerateCode(BusiConstants.TrainCode,3);
+
             var id = await base.AddAsync(actionRequest, cancellationToken);
 
             ResolveTemplateId(actionRequest, id);
 
+            _addTrainPlanDto = _mapper.Map<AddTrainPlanDto>(actionRequest);
+
             base.Entity.TrainPlanTemplates=  await AddTrainPlanTemplatesAsync(actionRequest, cancellationToken);
 
             base.Entity.TrainRecords = await AddTrainRecordsAsync(actionRequest, cancellationToken);
@@ -114,6 +121,8 @@ namespace Hotline.Application.Exam.Service.Trains
 
             _addTrainPlanDto = _mapper.Map<AddTrainPlanDto>(actionRequest);
 
+            ResolveTemplateId(actionRequest);
+
             base.Entity.TrainPlanTemplates = await ModifyTrainPlanTemplatesAsync(actionRequest, cancellationToken);
 
             base.Entity.TrainRecords = await ModifyTrainRecordsAsync(actionRequest, cancellationToken);
@@ -152,7 +161,7 @@ namespace Hotline.Application.Exam.Service.Trains
             var queryResult = trainPlanTemplateTable.Select(t => new TrainPlanTemplateDto
             {
                 TrainPlanId = t.TrainPlanId,
-                TrainTemplateId = t.TraimTemplateId,
+                TrainTemplateId = t.TrainTemplateId,
             }); 
 
             return await queryResult.ToListAsync();
@@ -172,16 +181,26 @@ namespace Hotline.Application.Exam.Service.Trains
             }
         }
 
-        private void ResolveTemplateId(UpdateTrainPlanDto actionRequest, string id)
+        private void ResolveTemplateId(UpdateTrainPlanDto actionRequest)
         {
             if (actionRequest.TrainPlanTemplateDtos != null)
             {
-                actionRequest.TrainPlanTemplateDtos.ForEach(x => x.TrainPlanId = id);
+                actionRequest.TrainPlanTemplateDtos.ForEach(x => x.TrainPlanId = actionRequest.Id);
             }
 
             if (actionRequest.TrainRecordDtos != null)
             {
-                actionRequest.TrainRecordDtos.ForEach(x => x.TrainPlanId = id);
+                actionRequest.TrainRecordDtos.ForEach(x => x.TrainPlanId = actionRequest.Id);
+            }
+
+            if (_addTrainPlanDto.TrainPlanTemplateDtos != null)
+            {
+                _addTrainPlanDto.TrainPlanTemplateDtos.ForEach(x => x.TrainPlanId = actionRequest.Id);
+            }
+
+            if (_addTrainPlanDto.TrainRecordDtos != null)
+            {
+                _addTrainPlanDto.TrainRecordDtos.ForEach(x => x.TrainPlanId = actionRequest.Id);
             }
         }
 
@@ -229,9 +248,16 @@ namespace Hotline.Application.Exam.Service.Trains
         {
             if (actionRequest.TrainPlanTemplateDtos != null)
             {
+                actionRequest.TrainPlanTemplateDtos.ResolveOperationStatus();
+
                 var trainPlanTemplateDtos = actionRequest.TrainPlanTemplateDtos.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();
 
-                var trainPlanTemplates = _mapper.Map<List<TrainPlanTemplate>>(trainPlanTemplateDtos);
+                var trainPlanTemplates = new List<TrainPlanTemplate>();
+
+                trainPlanTemplateDtos.ForEach(item =>
+                {
+                    trainPlanTemplates.Add(_mapper.Map<TrainPlanTemplate>(item));
+                });
 
                 trainPlanTemplates.ToInsert(_sessionContext);
 
@@ -247,9 +273,15 @@ namespace Hotline.Application.Exam.Service.Trains
         {
             if (actionRequest.TrainRecordDtos != null)
             {
+                actionRequest.TrainRecordDtos.ResolveOperationStatus();
+
                 var trainPracticeDtos = actionRequest.TrainRecordDtos.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();
+                var trainPractices = new List<TrainRecord>();
 
-                var trainPractices = _mapper.Map<List<TrainRecord>>(trainPracticeDtos);
+                trainPracticeDtos.ForEach(item =>
+                {
+                    trainPractices.Add(_mapper.Map<TrainRecord>(item));
+                });
 
                 trainPractices.ToInsert(_sessionContext);
 
@@ -323,14 +355,18 @@ namespace Hotline.Application.Exam.Service.Trains
             {
                 var ids = actionRequest.TrainRecordDtos.Where(x => x.OperationStatus == EEOperationStatus.Delete).Select(x => x.Id).ToList();
 
-                var entityQueryRequest = new EntityQueryRequest
+                if (ids.Any())
                 {
-                    Expression = ExpressionableUtility.CreateExpression<TrainRecord>()
-                    .AndIF(ids.IsNotEmpty(), x => ids.Contains(x.Id))
-                    .ToExpression()
-                };
-
-                await DeleteTrainRecordsAsync(entityQueryRequest, cancellationToken);
+                    var entityQueryRequest = new EntityQueryRequest
+                    {
+                        Expression = ExpressionableUtility.CreateExpression<TrainRecord>()
+                  .AndIF(ids.IsNotEmpty(), x => ids.Contains(x.Id))
+                  .ToExpression()
+                    };
+
+                    await DeleteTrainRecordsAsync(entityQueryRequest, cancellationToken);
+                }
+              
             }
 
             return trainRecords;
@@ -338,7 +374,7 @@ namespace Hotline.Application.Exam.Service.Trains
 
         private async Task DeleteTrainRecordsAsync(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
         {
-            await _trainPlanTemplateRepository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
+            await _trainRecordRepository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
         }
 
         private async Task<List<TrainRecord>> UpdateTrainRecordsAsync(UpdateTrainPlanDto actionRequest, CancellationToken cancellationToken)
@@ -360,6 +396,20 @@ namespace Hotline.Application.Exam.Service.Trains
 
             return trainRecords;
         }
+        
+        private async Task<string> GenerateCode(string codePrefix, int length)
+        {
+            var trainPlan = await _repository.Queryable().Where(x => x.CreationTime.Date == DateTime.Now.Date).OrderByDescending(x => x.CreationTime).FirstAsync();
+
+
+            var code = string.Empty;
+            if (trainPlan != null)
+            {
+                code = trainPlan.Code;
+            }
+            code = CodeUtility.GenerateCode(codePrefix, length, code);
+            return code;
+        }
         #endregion
     }
 }

+ 18 - 45
src/Hotline.Application/Exam/Service/Trains/TrainRecordService.cs

@@ -117,7 +117,7 @@ namespace Hotline.Application.Exam.Service.Trains
 
         }
 
-        public async Task<List<TrainPracticeOptionsDto>> TrainAsync(AddTrainDto addTrainDto, CancellationToken cancellationToken)
+        public async Task<TrainPracticeDto> TrainAsync(AddTrainDto addTrainDto, CancellationToken cancellationToken)
         {
             await AddTrainRecordItem(addTrainDto,cancellationToken);
 
@@ -166,7 +166,7 @@ namespace Hotline.Application.Exam.Service.Trains
 
             var queryResult = trainRecordTable.InnerJoin(trainPlanTable, (r, p) => r.TrainPlanId == p.Id)
                 .InnerJoin(trainPlanTemplateTable, (r, p, pt) => p.Id == pt.TrainPlanId)
-                .InnerJoin(trainTemplateTable, (r, p, pt, t) => pt.TraimTemplateId == t.Id)
+                .InnerJoin(trainTemplateTable, (r, p, pt, t) => pt.TrainTemplateId == t.Id)
                 .InnerJoin(userTable,(r,p,pt,t,u)=>r.UserId == u.Id)
                 .Select((r, p, pt, t, u) => new TrainResultViewResponse
                 {
@@ -206,7 +206,7 @@ namespace Hotline.Application.Exam.Service.Trains
 
             var queryResult = trainRecordTable.InnerJoin(trainPlanTable, (r, p) => r.TrainPlanId == p.Id)
                 .InnerJoin(trainPlanTemplateTable, (r, p, pt) => p.Id == pt.TrainPlanId)
-                .InnerJoin(trainTemplateTable, (r, p, pt, t) => pt.TraimTemplateId == t.Id)
+                .InnerJoin(trainTemplateTable, (r, p, pt, t) => pt.TrainTemplateId == t.Id)
                 .Select((r, p, pt, t) => new TrainResultViewResponse
                 {
                     TrainName = t.Name,
@@ -309,41 +309,6 @@ namespace Hotline.Application.Exam.Service.Trains
 
             return await queryable.ToListAsync();
         }
-
-        public async  Task<TrainQuestionDto> GetTrainQuestionDto(TrainQuestionRequest trainQuestionRequest)
-        {
-            var expression = trainQuestionRequest.GetExpression();
-            var quesetion = await new ExamRepository<TrainPractice>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(expression).FirstAsync();
-
-            if (quesetion != null)
-            {
-                var examQuestionDto = _mapper.Map<TrainQuestionDto>(quesetion);
-
-                if (examQuestionDto.QuestionType.CheckSelectType())
-                {
-                    var questionOptions = await new ExamRepository<TrainPracticeOptions>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(x => x.QuestionId == quesetion.Id).ToListAsync();
-
-                    if (questionOptions != null)
-                    {
-                        examQuestionDto.QuestionOptionsDtos = new List<TrainQuestionOptionDto>();
-
-                        questionOptions.ForEach(item =>
-                        {
-                            examQuestionDto.QuestionOptionsDtos.Add(_mapper.Map<TrainQuestionOptionDto>(item));
-                        });
-                    }
-
-                }
-
-
-                return examQuestionDto;
-            }
-            else
-            {
-                throw new UserFriendlyException(ErrorMessage.ServiceError, string.Format(ErrorMessage.IsNotExists, nameof(Question)));
-            }
-        }
-
        
 
         private async Task AddTrainRecordAnswer(AddTrainDto addTrainRecordDto, CancellationToken cancellationToken)
@@ -362,20 +327,28 @@ namespace Hotline.Application.Exam.Service.Trains
             await _trainRecordAnswerRepository.ValidateAddAsync(trainRecordAnswers,cancellationToken);
         }
 
-        private async Task<List<TrainPracticeOptionsDto>> GetTrainQuestionOptions(AddTrainDto addTrainDto)
+        private async Task<TrainPracticeDto> GetTrainQuestionOptions(AddTrainDto addTrainDto)
         {
-            var trainPracticeRepository = new ExamRepository<TrainPracticeOptions>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider);
-            // TODO: 获取未阅卷的第一道题
-            var trainPracticeOptions = trainPracticeRepository.Queryable().Where(x => x.TrainPracticeId == addTrainDto.Id);
+            var trainPracticeOptionsRepository = new ExamRepository<TrainPracticeOptions>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider);
+            var trainPracticeRepository = new ExamRepository<TrainPractice>(_repository.UOW, _dataPermissionFilterBuilder, _serviceProvider);
+
+            // TODO: 获取未答题的第一道题
+            var trainPractice = await trainPracticeRepository.Queryable()
+                .Where(x => x.TrainTemplateId == addTrainDto.TrainTemplateId && x.Id!= addTrainDto.TrainPracticeId).OrderBy(x=>x.SortIndex).FirstAsync();
+
+            var trainPracticeOptions = await trainPracticeOptionsRepository.Queryable().Where(x => x.TrainPracticeId == trainPractice.Id).ToListAsync();
 
-            var trainPracticeOptionsDtos = new List<TrainPracticeOptionsDto>();
+            var trainPracticeOptionsDtos = new List<SimpleTrainPracticeOptionsDto>();
 
             trainPracticeOptions.ForEach(x =>
             {
-                trainPracticeOptionsDtos.Add(_mapper.Map<TrainPracticeOptionsDto>(x));
+                trainPracticeOptionsDtos.Add(_mapper.Map<SimpleTrainPracticeOptionsDto>(x));
             });
 
-            return trainPracticeOptionsDtos;
+            var trainPracticeDto = _mapper.Map<TrainPracticeDto>(trainPractice);
+            trainPracticeDto.TrainPracticeOptionsDtos = trainPracticeOptionsDtos;
+
+            return trainPracticeDto;
         }
 
         private async Task<List<SimpleTrainPracticeOptionsDto>> GetTrainQuestionOptions(TrainPracticeRequest trainPracticeRequest)

+ 33 - 1
src/Hotline.Application/Exam/Service/Trains/TrainTemplateService.cs

@@ -12,7 +12,9 @@ using Exam.Repository.Sqlsugar.Repositories;
 using Exam.Share.ViewResponses.Train;
 using Exam.TestPapers;
 using Exam.Trains;
+using Hotline.Application.Exam.Core.Constants;
 using Hotline.Application.Exam.Core.Extensions;
+using Hotline.Application.Exam.Core.Utilities;
 using Hotline.Application.Exam.Extensions;
 using Hotline.Application.Exam.QueryExtensions.Trains;
 using Hotline.Exams.TestPapers;
@@ -115,6 +117,8 @@ namespace Hotline.Application.Exam.Service.Trains
         {
             base.StartTran();
 
+            actionRequest.Code = await GenerateCode(BusiConstants.TrainTemplateCode,3);
+
             var id = await base.AddAsync(actionRequest, cancellationToken);
 
             ResolveTemplateId(actionRequest, id);
@@ -148,12 +152,15 @@ namespace Hotline.Application.Exam.Service.Trains
             return id;
 
         }
+
         public override async Task UpdateAsync(UpdateTrainTemplateDto actionRequest, CancellationToken cancellationToken)
         {
             base.StartTran();
 
             await base.UpdateAsync(actionRequest, cancellationToken);
 
+            _addTrainTemplateDto = _mapper.Map<AddTrainTemplateDto>(actionRequest);
+
             base.Entity.TrainKnowladges = await ModifyTrainKnowladgeAsync(actionRequest, cancellationToken);
 
             base.Entity.TrainPractices = await ModifyTrainPracticesAsync(actionRequest, cancellationToken);
@@ -171,7 +178,7 @@ namespace Hotline.Application.Exam.Service.Trains
                 item.TrainPracticeSourcewares = trainPracticeSourcewares.Where(x => item.Id == x.TrainPracticeId).ToList();
             });
 
-            await base.Complete(base.Entity, OperationConstant.Create);
+            await base.Complete(base.Entity, OperationConstant.Update);
         }
 
 
@@ -411,6 +418,8 @@ namespace Hotline.Application.Exam.Service.Trains
             {
                 Id = t.Id,
                 QuestionId = t.QuestionId,
+                QuestionType = q.QuestionType,
+                DifficultyLevel = q.DifficultyLevel,                
                 Title = q.Title
             });
 
@@ -441,6 +450,8 @@ namespace Hotline.Application.Exam.Service.Trains
         {
             if (actionRequest.TrainPracticeDtos != null)
             {
+                actionRequest.TrainPracticeDtos.ResolveOperationStatus();
+
                 var trainPracticeDtos = actionRequest.TrainPracticeDtos.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();
 
                 var trainPractices = _mapper.Map<List<TrainPractice>>(trainPracticeDtos);
@@ -466,6 +477,8 @@ namespace Hotline.Application.Exam.Service.Trains
         {
             if (actionRequest.TrainKnowladges != null)
             {
+                actionRequest.TrainKnowladges.ResolveOperationStatus();
+
                 var trainKnowladgeDtos = actionRequest.TrainKnowladges.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();
 
                 var trainKnowladges = _mapper.Map<List<TrainKnowladge>>(trainKnowladgeDtos);
@@ -513,6 +526,8 @@ namespace Hotline.Application.Exam.Service.Trains
         {
             if (actionRequest.TrainPracticeDtos == null) return null;
 
+            actionRequest.TrainPracticeDtos.ResolveOperationStatus();
+
             var trainPracticeDtos = actionRequest.TrainPracticeDtos.Where(x => x.OperationStatus == EEOperationStatus.Update).ToList();
 
             var ids = trainPracticeDtos.Select(x => x.Id);
@@ -562,6 +577,8 @@ namespace Hotline.Application.Exam.Service.Trains
         {
             if (actionRequest.TrainKnowladges == null) return null;
 
+            actionRequest.TrainKnowladges.ResolveOperationStatus();
+
             var trainKnowladgeDtos = actionRequest.TrainKnowladges.Where(x => x.OperationStatus == EEOperationStatus.Update).ToList();
 
             var ids = trainKnowladgeDtos.Select(x => x.Id);
@@ -576,6 +593,21 @@ namespace Hotline.Application.Exam.Service.Trains
 
             return trainKnowladges;
         }
+
+        private async Task<string> GenerateCode(string codePrefix, int length)
+        {
+            var trainTemplate = await _repository.Queryable().Where(x => x.CreationTime.Date == DateTime.Now.Date).OrderByDescending(x=>x.CreationTime).FirstAsync();
+
+            var code = string.Empty;
+            if (trainTemplate != null)
+            {
+                code = trainTemplate.Code;
+            }
+
+            code = CodeUtility.GenerateCode(codePrefix,length,code);
+
+            return code;
+        }
         #endregion
 
         #region protected method

+ 7 - 7
src/Hotline.Application/Identity/IdentityAppService.cs

@@ -323,6 +323,7 @@ public class IdentityAppService : IIdentityAppService, IScopeDependency
             thirdAccount.PhoneNumber = phone.PhoneNumber;
             thirdAccount.Id = await _thirdAccountRepository.AddAsync(thirdAccount);
             await _thirdAccountDomainFactory.RegisterAsync(thirdAccount, token);
+            thirdAccount = await _thirdAccountRepository.GetAsync(thirdAccount.Id, token);
         }
 
         return await GetJwtToken(thirdAccount, token);
@@ -341,23 +342,22 @@ public class IdentityAppService : IIdentityAppService, IScopeDependency
         var jwtOptions = _identityOptionsAccessor.Value.Jwt;
         var claims = new List<Claim>
         {
-            new(JwtClaimTypes.Subject, thirdAccount.Id),
             new(JwtClaimTypes.PhoneNumber, thirdAccount.PhoneNumber ?? string.Empty),
             new(JwtClaimTypes.Scope, jwtOptions.Scope),
             new(AppClaimTypes.OpenId, thirdAccount.OpenId),
         };
-        claims = await _thirdAccountDomainFactory.GetClaimAsync(thirdAccount, claims, cancel);
-        var audience = new AudienceTicket(thirdAccount.Id);
+        var userId = await _thirdAccountDomainFactory.GetClaimAsync(thirdAccount, claims, cancel);
+        var audience = new AudienceTicket(userId);
         var expiredSeconds = jwtOptions.Expired <= 0 ? 3600 : jwtOptions.Expired;
         await _cacheAudience.SetAsync(audience.Id, audience, TimeSpan.FromSeconds(expiredSeconds));
         var token = _jwtSecurity.EncodeJwtToken(claims, audience.Ticket);
         
         var dicOutData = new Dictionary<string, object>()
         {
-            { "Token", token },
-            { "OpenId", thirdAccount.OpenId },
-            { "PhoneNumber", thirdAccount.PhoneNumber },
-            { "UserName", thirdAccount.UserName }
+            { "token", token },
+            { "openId", thirdAccount.OpenId },
+            { "phoneNumber", thirdAccount.PhoneNumber },
+            { "userName", thirdAccount.UserName }
         };
         dicOutData = await _thirdAccountDomainFactory.GetLoginOutDataAsync(thirdAccount, dicOutData, cancel);
         return dicOutData;

+ 1 - 1
src/Hotline.Application/OrderApp/Handlers/SnapshotHandler/GuiderSystemTimeoutHandler.cs

@@ -35,7 +35,7 @@ namespace Hotline.Application.OrderApp.Handlers.SnapshotHandler
             {
                 if (_systemSettingCacheManager.Snapshot)
                 {
-                    await _orderApplication.HandleFromWanggeyuanToMaskAsync(notification.OrderId, cancellationToken);
+                    await _orderApplication.HandleFromWanggeyuanToMaskAsync(notification.OrderId, "超时4小时12345自动退回。", cancellationToken);
                 }
             }
             catch (Exception e)

+ 1 - 1
src/Hotline.Application/OrderApp/IOrderApplication.cs

@@ -425,7 +425,7 @@ namespace Hotline.Application.OrderApp
         /// 将工单从网格员节点办理至工单标记节点
         /// </summary>
         /// <returns></returns>
-        Task HandleFromWanggeyuanToMaskAsync(string orderId, CancellationToken cancellation);
+        Task HandleFromWanggeyuanToMaskAsync(string orderId,string opinion, CancellationToken cancellation);
 
         /// <summary>
         /// 查询退回操作目标节点的指派对象

+ 2 - 1
src/Hotline.Application/OrderApp/OrderApplication.cs

@@ -3756,7 +3756,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     /// 将工单从网格员节点办理至工单标记节点(按照老系统实现方案:办理人统一处理为坐席)
     /// </summary>
     /// <returns></returns>
-    public async Task HandleFromWanggeyuanToMaskAsync(string orderId, CancellationToken cancellationToken)
+    public async Task HandleFromWanggeyuanToMaskAsync(string orderId, string opinion, CancellationToken cancellationToken)
     {
         var order = await _orderRepository.GetAsync(orderId, cancellationToken);
         if (order is null)
@@ -3787,6 +3787,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             NextHandlers = new(),
             IsStartCountersign = false,
             BusinessType = markStepDefine.BusinessType,
+            Opinion = opinion
         };
 
         var startStep = workflow.Steps.FirstOrDefault(d => d.StepType == EStepType.Start);

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
src/Hotline.Application/Snapshot/DefaultSnapshotApplication.cs


+ 7 - 0
src/Hotline.Application/Snapshot/IRedPackApplication.cs

@@ -109,6 +109,13 @@ public interface IRedPackApplication
     /// <returns></returns>
     ISugarQueryable<SnapshotOrderAuditItemsOutDto> GetRedPackSpecialAuditItems(SnapshotOrderAuditItemsInDto dto);
 
+    /// <summary>
+    /// 撤销特殊红包审核
+    /// </summary>
+    /// <param name="ids"></param>
+    /// <returns></returns>
+    Task RevocationRedPackSpecialAuditAsync(IList<string> ids);
+
     /// <summary>
     /// 审核添加备注
     /// </summary>

+ 29 - 6
src/Hotline.Application/Snapshot/IndustryApplication.cs

@@ -1,4 +1,5 @@
 using DocumentFormat.OpenXml.Office2010.Excel;
+using DocumentFormat.OpenXml.Wordprocessing;
 using Hotline.Caching.Interfaces;
 using Hotline.File;
 using Hotline.Repository.SqlSugar.Extensions;
@@ -231,7 +232,7 @@ public class IndustryApplication : IIndustryApplication, IScopeDependency
             .LeftJoin<Industry>((s, i) => s.IndustryId == i.Id)
             .Where((s, i) => s.Id == id)
             .Select((s, i) => new SnapshotSMSTemplateItemsOutDto
-            { 
+            {
                 Status = s.Status
             }, true)
             .FirstAsync();
@@ -385,10 +386,31 @@ public class IndustryApplication : IIndustryApplication, IScopeDependency
     public ISugarQueryable<VolunteerReportItemsOutDto> GetVolunteerReportItems(VolunteerReportItemsInDto dto)
     {
         var query = _volunteerReportRepository.Queryable()
-            .WhereIF(dto.Name.NotNullOrEmpty(), m => m.Name.Contains(dto.Name))
-            .WhereIF(dto.PhoneNumber.NotNullOrEmpty(), m => m.PhoneNumber.Contains(dto.PhoneNumber))
-            .OrderByDescending(m => m.CreationTime)
-            .Select<VolunteerReportItemsOutDto>();
+            .LeftJoin<SystemDicData>((volunteer, dic) => volunteer.JobType == dic.Id)
+            .WhereIF(dto.Name.NotNullOrEmpty(), (volunteer, dic) => volunteer.Name.Contains(dto.Name))
+            .WhereIF(dto.PhoneNumber.NotNullOrEmpty(), (volunteer, dic) => volunteer.PhoneNumber.Contains(dto.PhoneNumber))
+            .OrderByDescending((volunteer, dic) => volunteer.CreationTime)
+            .Select((volunteer, dic) => new VolunteerReportItemsOutDto
+            {
+                JobType = dic.DicDataName,
+                PhoneNumber = volunteer.DeclarePhoneNumber,
+                FullAddress = volunteer.Address + volunteer.FullAddress,
+            }, true)
+            .Mapper((item, cache)=> 
+            {
+                item.Files = _fileRepository.Queryable()
+                .Where(file => file.Key == item.Id)
+                .Select(file => new IndustryFileDto
+                {
+                    Id = file.Id,
+                    Path = file.Path,
+                    FileName = file.FileName,
+                    Additions = file.Additions
+                }).ToList();
+            });
+#if DEBUG
+        var sql = query.ToSqlString();
+#endif
         return query;
     }
 
@@ -419,7 +441,8 @@ public class IndustryApplication : IIndustryApplication, IScopeDependency
         await _snapshotSMSTemplateRepository.Queryable()
             .Where(m => ids.Contains(m.Id))
             .ToListAsync()
-            .Then(async sms => {
+            .Then(async sms =>
+            {
                 for (int i = 0;i < sms.Count;i++)
                 {
                     sms[i].IsDeleted = true;

+ 1 - 1
src/Hotline.Application/Snapshot/Notifications/SnapshotHandler.cs

@@ -115,7 +115,7 @@ public class GuiderSystemFieldNotificationHandler : INotificationHandler<GuiderS
         }
 
         //流程流转至派单组
-        await _orderApplication.HandleFromWanggeyuanToMaskAsync(notification.OrderSnapshot.Id, cancellationToken);
+        await _orderApplication.HandleFromWanggeyuanToMaskAsync(notification.OrderSnapshot.Id, string.Empty, cancellationToken);
     }
 }
 

+ 25 - 7
src/Hotline.Application/Snapshot/RedPackApplication.cs

@@ -15,6 +15,7 @@ using Hotline.Snapshot.Interfaces;
 using Hotline.ThirdAccountDomainServices.Interfaces;
 using Mapster;
 using Microsoft.AspNetCore.Http;
+using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Logging;
 using SqlSugar;
 using SqlSugar.Extensions;
@@ -48,9 +49,8 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
     private readonly IPushDomainService _pushDomainService;
     private readonly ILogger<RedPackApplication> _logger;
     private readonly ISystemDicDataCacheManager _systemDic;
-    private readonly ISnapshotUserInfoRepository _snapshotUserInfoRepository;
 
-    public RedPackApplication(IOrderSnapshotRepository orderSnapshotRepository, ISnapshotSMSTemplateRepository snapshotSMSTemplateRepository, IOrderRepository orderRepository, IIndustryRepository industryRepository, IRedPackAuditRepository redPackAuditRepository, IRedPackRecordRepository redPackRecordRepository, IRepository<OrderSpecial> orderSpecialRepository, ISessionContext sessionContext, IRedPackGuiderAuditRepository redPackGuiderAuditRepository, IThirdAccountRepository thirdAccountRepository, ISupplementRecordRepository supplementRecordRepository, IPushDomainService pushDomainService, ILogger<RedPackApplication> logger, ISpecialRedPackAuditRepository specialRedPackAuditRepository, ISystemDicDataCacheManager systemDic, ISnapshotUserInfoRepository snapshotUserInfoRepository)
+    public RedPackApplication(IOrderSnapshotRepository orderSnapshotRepository, ISnapshotSMSTemplateRepository snapshotSMSTemplateRepository, IOrderRepository orderRepository, IIndustryRepository industryRepository, IRedPackAuditRepository redPackAuditRepository, IRedPackRecordRepository redPackRecordRepository, IRepository<OrderSpecial> orderSpecialRepository, ISessionContext sessionContext, IRedPackGuiderAuditRepository redPackGuiderAuditRepository, IThirdAccountRepository thirdAccountRepository, ISupplementRecordRepository supplementRecordRepository, IPushDomainService pushDomainService, ILogger<RedPackApplication> logger, ISpecialRedPackAuditRepository specialRedPackAuditRepository, ISystemDicDataCacheManager systemDic)
     {
         _orderSnapshotRepository = orderSnapshotRepository;
         _snapshotSMSTemplateRepository = snapshotSMSTemplateRepository;
@@ -67,7 +67,6 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
         _logger = logger;
         _specialRedPackAuditRepository = specialRedPackAuditRepository;
         _systemDic = systemDic;
-        _snapshotUserInfoRepository = snapshotUserInfoRepository;
     }
 
     /// <summary>
@@ -95,7 +94,7 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
         var order = await _orderRepository.Queryable()
             .LeftJoin<OrderSnapshot>((order, snapshot) => order.Id == snapshot.Id)
             .Where((order, snapshot) => order.Id == redPackAudit.OrderId)
-            .Select((order, snapshot) => new { order.Id, order.No, order.FromName, order.FromPhone , snapshot.SnapshotUserId})
+            .Select((order, snapshot) => new { order.Id, order.No, order.FromName, order.FromPhone, snapshot.SnapshotUserId })
             .FirstAsync(token) ?? throw UserFriendlyException.SameMessage("工单不存在");
         if (status == ERedPackAuditStatus.Agree)
         {
@@ -267,7 +266,7 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
             .LeftJoin<OrderSnapshot>((redPackAudit, snapshot) => redPackAudit.OrderId == snapshot.Id)
             .LeftJoin<Order>((redPackAudit, snapshot, order) => redPackAudit.OrderId == order.Id)
             .LeftJoin<SpecialRedPackAudit>((redPackAudit, snapshot, order, special) => redPackAudit.OrderId == special.OrderId)
-            .Where((redPackAudit, snapshot, order, special) => (redPackAudit.Status == ERedPackAuditStatus.Agree && snapshot.IndustryName == "安全隐患" && order.OrderTagCode!.Contains("DHZY")) || (redPackAudit.Status == ERedPackAuditStatus.Agree && order.CreationTime >= beginTime && order.CreationTime <= endTime && redPackAudit.ApprovedAmount == 20 && order.AcceptTypeCode != "30" && snapshot.IndustryName != "电气焊作业申报"))
+            .Where((redPackAudit, snapshot, order, special) => (redPackAudit.Status == ERedPackAuditStatus.Agree && snapshot.IndustryName == "安全隐患" && order.OrderTagCode!.Contains("DHZY")) || snapshot.IsSafetyDepartment == true || (redPackAudit.Status == ERedPackAuditStatus.Agree && order.CreationTime >= beginTime && order.CreationTime <= endTime && redPackAudit.ApprovedAmount == 20 && order.AcceptTypeCode != "30" && snapshot.IndustryName != "电气焊作业申报"))
             .WhereIF(dto.Status == 0, (redPackAudit, snapshot, order, special) => special.Id == null)
             .WhereIF(dto.Status == 1, (redPackAudit, snapshot, order, special) => special.Status == ERedPackAuditStatus.Agree)
             .WhereIF(dto.Status == 2, (redPackAudit, snapshot, order, special) => special.Status == ERedPackAuditStatus.Refuse)
@@ -329,6 +328,21 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
         return query;
     }
 
+    public async Task RevocationRedPackSpecialAuditAsync(IList<string> ids)
+    {
+        var has = await _specialRedPackAuditRepository.Queryable()
+            .Where(m => m.IsSend == true && ids.Contains(m.Id))
+            .AnyAsync();
+        if (has) throw new UserFriendlyException("该工单已发放红包,不能撤销审批");
+        await _specialRedPackAuditRepository.Updateable()
+            .SetColumns(m => m.Status, ERedPackAuditStatus.Pending)
+            .SetColumns(m => m.AuditType, null)
+            .SetColumns(m => m.AuditTypeCode, null)
+            .SetColumns(m => m.AuditRemark, null)
+            .Where(m => ids.Contains(m.Id) && m.IsSend == false)
+            .ExecuteCommandAsync();
+    }
+
     /// <summary>
     /// 红包审核集合
     /// </summary>
@@ -348,6 +362,7 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
             .LeftJoin<OrderSnapshot>((redPackAudit, order, snapshot) => redPackAudit.OrderId == snapshot.Id)
             .LeftJoin<RedPackRecord>((redPackAudit, order, snapshot, record) => redPackAudit.Id == record.OrderId)
             .LeftJoin<Industry>((redPackAudit, order, snapshot, record, industry) => snapshot.IndustryId == industry.Id)
+            .LeftJoin<IndustryCase>((redPackAudit, order, snapshot, record, industry, industryCase) => snapshot.IndustryCase == industryCase.Id)
             .Where((redPackAudit, order, snapshot, record, industry) => order.Status >= EOrderStatus.Filed)
             .WhereIF(dto.No.NotNullOrEmpty(), (redPackAudit, order, snapshot, record, industry) => order.No.Contains(dto.No))
             .WhereIF(dto.Title.NotNullOrEmpty(), (redPackAudit, order, snapshot, record, industry) => order.Title.Contains(dto.Title))
@@ -366,7 +381,7 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
             .WhereIF(dto.IsDanger.HasValue, (redPackAudit, order, snapshot, record, industry) => snapshot.IsDanger == dto.IsDanger)
             .WhereIF(status.HasValue, (redPackAudit, order, snapshot, record, industry) => redPackAudit.Status == status)
             .OrderByDescending((redPackAudit, order, snapshot, record, industry) => redPackAudit.CreationTime)
-            .Select((redPackAudit, order, snapshot, record, industry) => new SnapshotOrderAuditItemsOutDto
+            .Select((redPackAudit, order, snapshot, record, industry, industryCase) => new SnapshotOrderAuditItemsOutDto
             {
                 Id = redPackAudit.Id,
                 RedPackAuditId = redPackAudit.Id,
@@ -374,6 +389,8 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
                 No = order.No,
                 Title = order.Title,
                 IndustryName = industry.Name,
+                IndustryCaseId = snapshot.IndustryCase,
+                IndustryCase = industryCase.Name,
                 IndustryId = industry.Id,
                 SourceChannel = order.SourceChannel,
                 SourceChannelCode = order.SourceChannelCode,
@@ -405,7 +422,7 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
                 AwardName = record.Name,
                 OpenBank = record.OpenBank,
                 AuditStatus = redPackAudit.Status,
-            });
+            }, true);
         return query;
     }
 
@@ -803,5 +820,6 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
     }
 
 
+
     #endregion
 }

+ 16 - 13
src/Hotline.Application/Snapshot/SnapshotApplicationBase.cs

@@ -77,9 +77,9 @@ public abstract class SnapshotApplicationBase
     private readonly IOrderVisitDetailRepository _orderVisitDetailRepository;
     private readonly IInviteCodeRecordRepository _inviteCodeRecordRepository;
     private readonly IInviteCodeRepository _inviteCodeRepository;
-    private readonly ISnapshotUserInfoRepository _snapshotUserInfoRepository;
+    private readonly ICitizenRepository _citizenRepository;
 
-    public SnapshotApplicationBase(IThirdIdentiyService thirdLoginService, IIndustryRepository industryRepository, ISnapshotBulletinRepository bulletinRepository, ISessionContext sessionContext, IRepository<RedPackRecord> redPackRecordRepository, IRepository<Order> orderRepository, IThirdAccountRepository thirdAccountRepository, IOrderSnapshotRepository orderSnapshotRepository, ISystemSettingCacheManager systemSettingCacheManager, ISystemAreaDomainService systemAreaDomainService, IFileRepository fileRepository, ISystemDicDataCacheManager systemDicDataCacheManager, ISnapshotOrderPublishRepository snapshotOrderPublishRepository, IRepository<WorkflowTrace> workflowTraceRepository, IPractitionerRepository practitionerRepository, IRepository<SystemArea> systemAreaRepository, IVolunteerRepository volunteerRepository, IVolunteerReportRepository volunteerReportRepository, ISystemLogRepository systemLog, IGuiderSystemService guiderSystemService, ICapPublisher capPublisher, Publisher publisher, IFileDomainService fileDomainService, ICommunityInfoRepository communityInfoRepository, IRedPackAuditRepository redPackAuditRepository, IOrderVisitRepository orderVisitRepository, IOrderVisitDetailRepository orderVisitDetailRepository, IRedPackGuiderAuditRepository redPackGuiderAuditRepository, IInviteCodeRecordRepository inviteCodeRecordRepository, IInviteCodeRepository inviteCodeRepository, ISnapshotUserInfoRepository snapshotUserInfoRepository)
+    public SnapshotApplicationBase(IThirdIdentiyService thirdLoginService, IIndustryRepository industryRepository, ISnapshotBulletinRepository bulletinRepository, ISessionContext sessionContext, IRepository<RedPackRecord> redPackRecordRepository, IRepository<Order> orderRepository, IThirdAccountRepository thirdAccountRepository, IOrderSnapshotRepository orderSnapshotRepository, ISystemSettingCacheManager systemSettingCacheManager, ISystemAreaDomainService systemAreaDomainService, IFileRepository fileRepository, ISystemDicDataCacheManager systemDicDataCacheManager, ISnapshotOrderPublishRepository snapshotOrderPublishRepository, IRepository<WorkflowTrace> workflowTraceRepository, IPractitionerRepository practitionerRepository, IRepository<SystemArea> systemAreaRepository, IVolunteerRepository volunteerRepository, IVolunteerReportRepository volunteerReportRepository, ISystemLogRepository systemLog, IGuiderSystemService guiderSystemService, ICapPublisher capPublisher, Publisher publisher, IFileDomainService fileDomainService, ICommunityInfoRepository communityInfoRepository, IRedPackAuditRepository redPackAuditRepository, IOrderVisitRepository orderVisitRepository, IOrderVisitDetailRepository orderVisitDetailRepository, IRedPackGuiderAuditRepository redPackGuiderAuditRepository, IInviteCodeRecordRepository inviteCodeRecordRepository, IInviteCodeRepository inviteCodeRepository, ICitizenRepository citizenRepository)
     {
         _thirdLoginService = thirdLoginService;
         _industryRepository = industryRepository;
@@ -111,7 +111,7 @@ public abstract class SnapshotApplicationBase
         _redPackGuiderAuditRepository = redPackGuiderAuditRepository;
         _inviteCodeRecordRepository = inviteCodeRecordRepository;
         _inviteCodeRepository = inviteCodeRepository;
-        _snapshotUserInfoRepository = snapshotUserInfoRepository;
+        _citizenRepository = citizenRepository;
     }
 
     #region 小程序
@@ -138,10 +138,13 @@ public abstract class SnapshotApplicationBase
             if (m.CellImgUrl.NotNullOrEmpty())
                 m.CellImgUrl = fileServiceUrl + m.CellImgUrl;
         });
+        var banners = (await _bulletinRepository.Queryable()
+            .Where(m => m.SnapshotBulletinTypeId == "SSPBanner" && m.BulletinState == EBulletinState.ReviewPass && m.IsArrive == true)
+            .Select(m => m.Content).ToListAsync()).Select(m => m.GetHtmlImgSrc()).ToList();
 
         return new HomePageOutDto
         {
-            Banners = _sysSetting.AppBanner.Split('|').Select(m => fileDownloadApi + m).ToList(),
+            Banners = banners,
             Industrys = items
         };
     }
@@ -295,7 +298,7 @@ public abstract class SnapshotApplicationBase
     /// <returns></returns>
     public async Task<SnapshotUserInfoOutDto> GetSnapshotUserInfoAsync()
     {
-        var userInfo = await _snapshotUserInfoRepository.GetAsync(_sessionContext.UserId);
+        var userInfo = await _citizenRepository.GetAsync(_sessionContext.UserId);
 
         var dayTime = DateTime.Now;
         var readPack = await _redPackRecordRepository.Queryable()
@@ -314,7 +317,7 @@ public abstract class SnapshotApplicationBase
             }).FirstAsync();
 
         outDto.DayAmount = readPack;
-        outDto.TotalAmount = userInfo.TotalAmount;
+        outDto.TotalAmount = userInfo.TotalAmount ?? 0;
         outDto.PhoneNumber = userInfo.PhoneNumber;
         return outDto;
     }
@@ -465,9 +468,9 @@ public abstract class SnapshotApplicationBase
     /// <returns></returns>
     public async Task<string> GetRedPackReceivedTotalAsync(CancellationToken cancellationToken)
     {
-        var member = await _snapshotUserInfoRepository.GetAsync(m => m.Id == _sessionContext.UserId, cancellationToken)
+        var member = await _citizenRepository.GetAsync(m => m.Id == _sessionContext.UserId, cancellationToken)
             ?? throw UserFriendlyException.SameMessage("用户不存在");
-        return member.TotalAmount.ToYuanFinance();
+        return (member.TotalAmount ?? 0).ToYuanFinance();
     }
 
     /// <summary>
@@ -526,7 +529,7 @@ public abstract class SnapshotApplicationBase
         { 
             throw new UserFriendlyException(200, "邀请码格式错误");
         }
-        var userInfo = await _snapshotUserInfoRepository.GetAsync(_sessionContext.UserId)
+        var userInfo = await _citizenRepository.GetAsync(_sessionContext.UserId)
             ?? throw UserFriendlyException.SameMessage("用户不存在");
         if (userInfo.InvitationCode.NotNullOrEmpty())
         {
@@ -549,7 +552,7 @@ public abstract class SnapshotApplicationBase
             PhoneNumber = userInfo.PhoneNumber,
             Name = userInfo.Name,
         };
-        await _snapshotUserInfoRepository.UpdateAsync(userInfo);
+        await _citizenRepository.UpdateAsync(userInfo);
         await _inviteCodeRecordRepository.AddAsync(entity);
     }
 
@@ -664,16 +667,16 @@ public abstract class SnapshotApplicationBase
             .Where(m => m.Id == orderId)
             .Select(m => new { m.MemberName, m.MemberMobile })
             .FirstAsync(cancellationToken);
-        var guider = await _snapshotUserInfoRepository.GetByPhoneNumberAsync(guiderInfo.MemberMobile);
+        var guider = await _citizenRepository.GetByPhoneNumberAsync(guiderInfo.MemberMobile);
         if (guider != null) return;
 
-        var entity = new SnapshotUserInfo
+        var entity = new Citizen
         {
             Name = guiderInfo.MemberName,
             PhoneNumber = guiderInfo.MemberMobile,
             CitizenType = EReadPackUserType.Guider
         };
-        entity.Id = await _snapshotUserInfoRepository.AddAsync(entity, cancellationToken);
+        entity.Id = await _citizenRepository.AddAsync(entity, cancellationToken);
         var third = (await _thirdAccountRepository.GetByPhoneNumberAsync(guiderInfo.MemberMobile, cancellationToken))
             .Where(m => m.ExternalId == null)
             .ToList();

+ 19 - 23
src/Hotline.Application/Snapshot/SnapshotThirdAccountSupplier.cs

@@ -25,51 +25,47 @@ public class SnapshotThirdAccountSupplier : IThirdAccountSupplier, IScopeDepende
     private readonly IThirdAccountRepository _thirdAccountRepository;
     private readonly IThirdAccountDomainService _thirdAccountDomainService;
     private readonly IVolunteerRepository _volunteerRepository;
-    private readonly ISnapshotUserInfoRepository _snapshotUserInfoRepository;
     private readonly ISystemSettingCacheManager _systemSettingCacheManager;
     private readonly ISystemLogRepository _systemLog;
+    private readonly ICitizenRepository _citizenRepository;
 
-    public SnapshotThirdAccountSupplier(IThirdAccountRepository thirdAccountRepository, IVolunteerRepository volunteerRepository, ISnapshotUserInfoRepository snapshotUserInfoRepository, ISystemSettingCacheManager systemSettingCacheManager, ISystemLogRepository systemLog, IThirdAccountDomainService thirdAccountDomainService)
+    public SnapshotThirdAccountSupplier(IThirdAccountRepository thirdAccountRepository, IVolunteerRepository volunteerRepository, ISystemSettingCacheManager systemSettingCacheManager, ISystemLogRepository systemLog, IThirdAccountDomainService thirdAccountDomainService, ICitizenRepository citizenRepository)
     {
         _thirdAccountRepository = thirdAccountRepository;
         _volunteerRepository = volunteerRepository;
-        _snapshotUserInfoRepository = snapshotUserInfoRepository;
         _systemSettingCacheManager = systemSettingCacheManager;
         _systemLog = systemLog;
         _thirdAccountDomainService = thirdAccountDomainService;
+        _citizenRepository = citizenRepository;
     }
 
-    public async Task<List<Claim>> GetClaimAsync(ThirdAccount account, List<Claim> claims, CancellationToken token)
+    public async Task<string> GetClaimAsync(ThirdAccount account, List<Claim> claims, CancellationToken token)
     {
-        var userInfo = await _snapshotUserInfoRepository.GetAsync(account.ExternalId);
-        if (userInfo != null)
-        {
-            var subject = claims.Find(m => m.Type == JwtClaimTypes.Subject);
-            if (subject != null)
-                claims.Remove(subject);
-            claims.Add(new(JwtClaimTypes.Subject, userInfo.Id));
-        }
-        return claims;
+        var userInfo = await _citizenRepository.GetAsync(account.ExternalId);
+        if (userInfo == null)
+            return string.Empty;
+        claims.Add(new(JwtClaimTypes.Subject, userInfo.Id));
+        return userInfo.Id;
     }
 
     public async Task<Dictionary<string, object>> GetLoginOutDataAsync(ThirdAccount thirdAccount, Dictionary<string, object> dicOutData, CancellationToken cancel)
     {
         var isVolunteer = await _volunteerRepository.IsVolunteerAsync(thirdAccount.PhoneNumber);
-        var user = await _snapshotUserInfoRepository.GetAsync(thirdAccount.ExternalId);
+        var user = await _citizenRepository.GetAsync(thirdAccount.ExternalId);
         if (user == null || thirdAccount.ExternalId.IsNullOrEmpty())
         {
-            user = new SnapshotUserInfo
+            user = new Citizen
             {
                 CitizenType = EReadPackUserType.Citizen,
                 Name = thirdAccount.UserName,
                 PhoneNumber = thirdAccount.PhoneNumber,
             };
-            user.Id = await _snapshotUserInfoRepository.AddAsync(user);
+            user.Id = await _citizenRepository.AddAsync(user);
             await _thirdAccountDomainService.UpdateExternalIdAsync(thirdAccount.Id, thirdAccount.ExternalId, user.Id, cancel);
         }
-        dicOutData.Add("UserType", user.CitizenType);
-        dicOutData.Add("IsVolunteer", isVolunteer);
-        dicOutData.Add("InvitationCode", user.InvitationCode);
+        dicOutData.Add("userType", user.CitizenType);
+        dicOutData.Add("isVolunteer", isVolunteer);
+        dicOutData.Add("invitationCode", user.InvitationCode);
         return dicOutData;
     }
 
@@ -85,21 +81,21 @@ public class SnapshotThirdAccountSupplier : IThirdAccountSupplier, IScopeDepende
 
     public async Task RegisterAsync(ThirdAccount thirdAccount, CancellationToken token)
     {
-        var userInfo = await _snapshotUserInfoRepository.GetByPhoneNumberAsync(thirdAccount.PhoneNumber);
+        var userInfo = await _citizenRepository.GetByPhoneNumberAsync(thirdAccount.PhoneNumber);
         if (userInfo != null)
         {
             userInfo.CitizenType = EReadPackUserType.Guider;
-            await _snapshotUserInfoRepository.UpdateAsync(userInfo);
+            await _citizenRepository.UpdateAsync(userInfo);
             await _thirdAccountDomainService.UpdateExternalIdAsync(thirdAccount.Id, thirdAccount.ExternalId, userInfo.Id, token);
         }
         else
         {
-            userInfo = new SnapshotUserInfo
+            userInfo = new Citizen
             {
                 PhoneNumber = thirdAccount.PhoneNumber,
                 CitizenType = EReadPackUserType.Citizen,
             };
-            userInfo.Id = await _snapshotUserInfoRepository.AddAsync(userInfo);
+            userInfo.Id = await _citizenRepository.AddAsync(userInfo);
             await _thirdAccountDomainService.UpdateExternalIdAsync(thirdAccount.Id, thirdAccount.ExternalId, userInfo.Id, token);
         }
     }

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
src/Hotline.Application/Snapshot/ZiGongSnapshotApplication.cs


+ 1 - 1
src/Hotline.Repository.SqlSugar/Exam/Repositories/Trains/TrainPlanRepository.cs

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

+ 1 - 1
src/Hotline.Repository.SqlSugar/Exam/Repositories/Trains/TrainTemplateRepository.cs

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

+ 3 - 0
src/Hotline.Repository.SqlSugar/Exam/Validators/Trains/TrainPlanTemplateValidator.cs

@@ -34,6 +34,9 @@ namespace Hotline.Repository.SqlSugar.Exam.Validators.Trains
         protected override void BaseValidateRule()
         {
             base.BaseValidateRule();
+
+            RuleFor(m => m.TrainTemplateId).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(TrainPlanTemplate.TrainTemplateId))));
+            RuleFor(m => m.TrainPlanId).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(TrainPlanTemplate.TrainPlanId))));
         }
 
         protected override void ValidateRuleWithAdd()

+ 11 - 1
src/Hotline.Repository.SqlSugar/Exam/Validators/Trains/TrainPlanValidator.cs

@@ -2,13 +2,16 @@ using Exam.Infrastructure.Extensions;
 using Exam.Infrastructure.Validation.Validation;
 using Exam.Trains;
 using FluentValidation;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.Trains;
 using Hotline.Repository.SqlSugar.Validate;
 
 namespace Exam.Repository.Sqlsugar.Validators.Trains
 {
     public class TrainPlanValidator:BaseValidator<TrainPlan>
     {
-        public TrainPlanValidator()
+        private readonly ITrainPlanRepository _trainPlanRepository;
+
+        public TrainPlanValidator(ITrainPlanRepository trainPlanRepository)
         {
             RuleSet(ValidatorTypeConstants.Create, () =>
             {
@@ -23,6 +26,7 @@ namespace Exam.Repository.Sqlsugar.Validators.Trains
 
                 ValidateRuleWithModify();
             });
+            this._trainPlanRepository = trainPlanRepository;
         }
 
         protected override void BaseValidateRule()
@@ -34,12 +38,18 @@ namespace Exam.Repository.Sqlsugar.Validators.Trains
         {
             base.ValidateRuleWithAdd();
             RuleFor(m => m.CreationTime).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(TrainPlan.CreationTime))));
+            RuleFor(m => m.Name).Must((t, v) => !_trainPlanRepository.Queryable().Any(x => x.Name == v && x.Id != t.Id)).WithMessage(x => string.Format(ErrorMessage.IsRepeat, x.GetType().GetDescription(nameof(TrainPlan.Name))));
+
+            RuleFor(m=>m.Code).Must(v =>!_trainPlanRepository.Queryable().Any(x=>x.Code == v)).WithMessage(x => string.Format(ErrorMessage.IsRepeat, x.GetType().GetDescription(nameof(TrainPlan.Code))));
         }
 
         protected override void ValidateRuleWithModify()
         {
             base.ValidateRuleWithModify();
             RuleFor(m => m.LastModificationTime).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(TrainPlan.LastModificationTime))));
+            RuleFor(m => m.Name).Must((t, v) => !_trainPlanRepository.Queryable().Any(x => x.Name == v && x.Id != t.Id)).WithMessage(x => string.Format(ErrorMessage.IsRepeat, x.GetType().GetDescription(nameof(TrainPlan.Name))));
+
+            RuleFor(m => m.Code).Must((t,v) => !_trainPlanRepository.Queryable().Any(x => x.Code == v && x.Id!=t.Id)).WithMessage(x => string.Format(ErrorMessage.IsRepeat, x.GetType().GetDescription(nameof(TrainPlan.Code))));
 
         }
 

+ 3 - 0
src/Hotline.Repository.SqlSugar/Exam/Validators/Trains/TrainRecordItemValidator.cs

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

+ 3 - 0
src/Hotline.Repository.SqlSugar/Exam/Validators/Trains/TrainRecordValidator.cs

@@ -3,6 +3,7 @@ using Exam.Infrastructure.Validation.Validation;
 using Exam.Trains;
 using FluentValidation;
 using Hotline.Repository.SqlSugar.Validate;
+using Hotline.Users;
 
 namespace Exam.Repository.Sqlsugar.Validators.Trains
 {
@@ -28,6 +29,8 @@ namespace Exam.Repository.Sqlsugar.Validators.Trains
         protected override void BaseValidateRule()
         {
             base.BaseValidateRule();
+
+            RuleFor(m=>m.UserId).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, typeof(User).GetDescription()));
         }
 
         protected override void ValidateRuleWithAdd()

+ 10 - 1
src/Hotline.Repository.SqlSugar/Exam/Validators/Trains/TrainTemplateValidator.cs

@@ -1,14 +1,18 @@
 using Exam.Infrastructure.Extensions;
 using Exam.Infrastructure.Validation.Validation;
+using Exam.Repository.Sqlsugar.Repositories.Trains;
 using Exam.Trains;
 using FluentValidation;
+using Hotline.Repository.SqlSugar.Exam.Interfaces.Trains;
 using Hotline.Repository.SqlSugar.Validate;
 
 namespace Exam.Repository.Sqlsugar.Validators.Trains
 {
     public class TrainTemplateValidator:BaseValidator<TrainTemplate>
     {
-        public TrainTemplateValidator()
+        private readonly ITrainTemplateRepository _trainTemplateRepository;
+
+        public TrainTemplateValidator(ITrainTemplateRepository trainTemplateRepository)
         {
             RuleSet(ValidatorTypeConstants.Create, () =>
             {
@@ -23,6 +27,7 @@ namespace Exam.Repository.Sqlsugar.Validators.Trains
 
                 ValidateRuleWithModify();
             });
+            this._trainTemplateRepository = trainTemplateRepository;
         }
 
         protected override void BaseValidateRule()
@@ -34,12 +39,16 @@ namespace Exam.Repository.Sqlsugar.Validators.Trains
         {
             base.ValidateRuleWithAdd();
             RuleFor(m => m.CreationTime).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(TrainTemplate.CreationTime))));
+            RuleFor(m => m.Name).Must(v => !_trainTemplateRepository.Queryable().Any(x => x.Name == v)).WithMessage(x => string.Format(ErrorMessage.IsRepeat, x.GetType().GetDescription(nameof(TrainPlan.Name))));
+            RuleFor(m => m.Code).Must(v => !_trainTemplateRepository.Queryable().Any(x => x.Code == v)).WithMessage(x => string.Format(ErrorMessage.IsRepeat, x.GetType().GetDescription(nameof(TrainPlan.Code))));
         }
 
         protected override void ValidateRuleWithModify()
         {
             base.ValidateRuleWithModify();
             RuleFor(m => m.LastModificationTime).NotEmpty().WithMessage(x => string.Format(ErrorMessage.IsRequired, x.GetType().GetDescription(nameof(TrainTemplate.LastModificationTime))));
+            RuleFor(m => m.Name).Must((t, v) => !_trainTemplateRepository.Queryable().Any(x => x.Name == v && x.Id != t.Id)).WithMessage(x => string.Format(ErrorMessage.IsRepeat, x.GetType().GetDescription(nameof(TrainPlan.Name))));
+            RuleFor(m => m.Code).Must((t, v) => !_trainTemplateRepository.Queryable().Any(x => x.Code == v && x.Id != t.Id)).WithMessage(x => string.Format(ErrorMessage.IsRepeat, x.GetType().GetDescription(nameof(TrainPlan.Code))));
 
         }
 

+ 5 - 2
src/Hotline.Repository.SqlSugar/Orders/CitizenRepository.cs

@@ -17,7 +17,7 @@ using XF.Domain.Repository;
 
 namespace Hotline.Repository.SqlSugar.Orders
 {
-	public class CitizenRepository : BaseRepository<Citizen>, ICitizenRepository, IScopeDependency
+    public class CitizenRepository : BaseRepository<Citizen>, ICitizenRepository, IScopeDependency
 	{
 		private readonly ISessionContext _sessionContext;
 		private readonly IMapper _mapper;
@@ -48,7 +48,10 @@ namespace Hotline.Repository.SqlSugar.Orders
 			await AddAsync(citizen, cancellationToken);
 		}
 
-		public async Task LabelAddAsync(LabelDetailAddDto  model , CancellationToken cancellationToken) 
+        public async Task<Citizen> GetByPhoneNumberAsync(string phoneNumber)
+            => await Queryable().Where(m => m.PhoneNumber == phoneNumber).FirstAsync();
+
+        public async Task LabelAddAsync(LabelDetailAddDto  model , CancellationToken cancellationToken) 
 		{
 			var label = _mapper.Map<CitizenLabelDetail>(model);
 			var id = await _labelDetailRepository.AddAsync(label, cancellationToken);

+ 0 - 28
src/Hotline.Repository.SqlSugar/Snapshot/SnapshotUserInfoRepository.cs

@@ -1,28 +0,0 @@
-using Hotline.Repository.SqlSugar.DataPermissions;
-using Hotline.Share.Tools;
-using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
-using Hotline.Snapshot;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using XF.Domain.Dependency;
-using SqlSugar;
-using Hotline.Repository.SqlSugar.DataPermissions;
-
-namespace Hotline.Repository.SqlSugar.Snapshot;
-
-public class SnapshotUserInfoRepository : BaseRepository<SnapshotUserInfo>, ISnapshotUserInfoRepository, IScopeDependency
-{
-    public SnapshotUserInfoRepository(ISugarUnitOfWork<HotlineDbContext> uow, IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider) : base(uow, dataPermissionFilterBuilder, serviceProvider)
-    {
-    }
-
-    public async Task<SnapshotUserInfo?> GetByPhoneNumberAsync(string? phoneNumber)
-    {
-        if (phoneNumber.IsNullOrEmpty()) return null;
-        return await Queryable().Where(m => m.PhoneNumber == phoneNumber).FirstAsync();
-    }
-}

+ 1 - 0
src/Hotline.Share/Dtos/Snapshot/IndustryFileDto.cs

@@ -30,6 +30,7 @@ public class IndustryFileDto
     /// 附件系统中附件的Id
     /// </summary>
     public string AdditionId { get; set; }
+    public string Additions { get; set; }
 
     /// <summary>
     /// Url

+ 1 - 1
src/Hotline.Share/Dtos/Snapshot/OrderDto.cs

@@ -741,7 +741,7 @@ public class SystemDicDataAmountOutDto : SystemDicDataOutDto
     /// <summary>
     /// 价格
     /// </summary>
-    public int Amount => int.Parse(_dicDataName.Split('|')[1]);
+    public int Amount => int.TryParse(_dicDataName.Split('|').ElementAtOrDefault(1), out var result) ? result : 100;
 
     private string _dicDataName;
     new public string DicDataName

+ 5 - 0
src/Hotline.Share/Dtos/Snapshot/VolunteerReportDto.cs

@@ -170,4 +170,9 @@ public class VolunteerReportItemsOutDto
     /// 上报时间
     /// </summary>
     public DateTime CreationTime { get; set; }
+
+    /// <summary>
+    /// 附件信息
+    /// </summary>
+    public List<IndustryFileDto> Files { get; set; }
 }

+ 30 - 5
src/Hotline.Share/Dtos/Trains/TrainPracticeDto.cs

@@ -1,6 +1,7 @@
 using Exam.Infrastructure.Data.Entity;
 using Exam.Infrastructure.Data.Interface;
 using Exam.Infrastructure.Enums;
+using Exam.Infrastructure.Extensions;
 using Hotline.Share.Dtos.Questions;
 using Hotline.Share.Enums.Exams;
 using Hotline.Share.Exams.Interface;
@@ -13,7 +14,7 @@ namespace Hotline.Share.Dtos.Trains
     /// 培训习题
     /// </summary>
     [Description("培训习题")]
-    public class TrainPracticeDto:UpdateTrainPracticeDto
+    public class TrainPracticeDto : UpdateTrainPracticeDto
     {
         // <summary>
         /// 关联课件
@@ -32,9 +33,33 @@ namespace Hotline.Share.Dtos.Trains
         /// </summary>
         [Description("培训习题选项")]
         public List<SimpleTrainPracticeOptionsDto> TrainPracticeOptionsDtos { get; set; }
+
+        /// <summary>
+        /// 题型
+        /// </summary>
+        [Description("题型")]
+        public string QuestionTypeDesc
+        {
+            get
+            {
+                return QuestionType.GetDescription();
+            }
+        }
+
+        /// <summary>
+        /// 难度
+        /// </summary>
+        [Description("难度")]
+        public string DifficultyLevelDesc
+        {
+            get
+            {
+                return DifficultyLevel.GetDescription();
+            }
+        }
     }
 
-    public class AddTrainPracticeDto : IAddRequest,IOperationStatus
+    public class AddTrainPracticeDto : IAddRequest, IOperationStatus
     {
         /// <summary>
         /// 培训模版Id
@@ -71,9 +96,9 @@ namespace Hotline.Share.Dtos.Trains
         /// 操作状态
         /// </summary>
         [Description("操作状态")]
-        public EEOperationStatus OperationStatus { get ; set ; }
+        public EEOperationStatus OperationStatus { get; set; }
+
 
-       
 
         // <summary>
         /// 关联课件
@@ -94,7 +119,7 @@ namespace Hotline.Share.Dtos.Trains
         /// 主键
         /// </summary>
         [Description("主键")]
-        public string Id { get ; set ; }
+        public string Id { get; set; }
 
         // <summary>
         /// 关联课件

+ 12 - 0
src/Hotline.Share/Dtos/Trains/TrainRecordDto.cs

@@ -103,6 +103,18 @@ namespace Hotline.Share.Dtos.Trains
         [Description("培训模版Id")]
         public string TrainTemplateId { get; set; }
 
+        /// <summary>
+        /// 培训记录Id
+        /// </summary>
+        [Description("培训记录Id")]
+        public string TrainRecordId { get; set; }
+
+        /// <summary>
+        /// 培训习题Id
+        /// </summary>
+        [Description("培训习题Id")]
+        public string TrainPracticeId { get; set; }
+
         /// <summary>
         /// 操作状态
         /// </summary>

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

@@ -8,12 +8,12 @@ namespace Hotline.Share.Enums.Exams
         /// 随机试题
         /// </summary>
         [Description("随机试题")]
-        Random = 0,
+        Random = 1,
         /// <summary>
         /// 自选试题
         /// </summary>
         [Description("统一试卷")]
-        Manual = 1,
+        Manual = 0,
         /// <summary>
         /// 导入
         /// </summary>

+ 7 - 1
src/Hotline.Share/Enums/User/EThirdType.cs

@@ -17,5 +17,11 @@ public enum EThirdType
     /// 微信
     /// </summary>
     [Description("微信")]
-    WeChat = 0
+    WeChat = 0,
+
+    /// <summary>
+    /// 测试
+    /// </summary>
+    [Description("测试")]
+    Test = 100
 }

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

@@ -18,5 +18,11 @@ namespace Hotline.Share.Requests.Exam
         /// </summary>
         [Description("考试Id")]
         public string ExamManageId { get; set; }
+
+        /// <summary>
+        /// 抽题规则Id
+        /// </summary>
+        [Description("抽题规则Id")]
+        public string ExtractRuleId { get; set; }
     }
 }

+ 12 - 0
src/Hotline.Share/Tools/StringExtensions.cs

@@ -131,4 +131,16 @@ public static class StringExtensions
             return memoryStream.ToArray();
         }
     }
+
+    public static string GetHtmlImgSrc(this string value)
+    {
+        string pattern = @"<img\s[^>]*?src\s*=\s*[""'](?<src>[^""']+)[""']";
+        Match match = Regex.Match(value, pattern, RegexOptions.IgnoreCase);
+
+        if (match.Success)
+        {
+            return match.Groups["src"].Value;
+        }
+        return string.Empty;
+    }
 }

+ 23 - 0
src/Hotline.WeChat/TestService.cs

@@ -0,0 +1,23 @@
+using Hotline.Share.Dtos.Snapshot;
+using Hotline.ThirdAccountDomainServices.Interfaces;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Dependency;
+
+namespace Hotline.WeChat;
+
+public class TestService : IThirdIdentiyService, IScopeDependency
+{
+    public Task<ThirdPhoneOutDto> GetPhoneNumberAsync(ThirdTokenDto dto, CancellationToken token)
+    {
+        return Task.FromResult(new ThirdPhoneOutDto { PhoneNumber = dto.TelCode});
+    }
+
+    public Task<ThirdTokenOutDto> GetTokenAsync(ThirdTokenDto dto, CancellationToken token)
+    {
+        return Task.FromResult(new ThirdTokenOutDto { OpenId = dto.LoginCode, UnIonId = dto.LoginCode, SessionKey = dto.LoginCode});
+    }
+}

+ 2 - 1
src/Hotline.WeChat/WeChatService.cs

@@ -7,11 +7,12 @@ using Senparc.Weixin;
 using Senparc.Weixin.WxOpen.AdvancedAPIs.Sns;
 using Senparc.Weixin.WxOpen.AdvancedAPIs.WxApp;
 using Senparc.Weixin.WxOpen.Containers;
+using XF.Domain.Dependency;
 using XF.Domain.Exceptions;
 
 namespace Hotline.WeChat;
 
-public class WeChatService : IThirdIdentiyService
+public class WeChatService : IThirdIdentiyService, IScopeDependency
 {
     private readonly ILogger<WeChatService> _logger;
     private readonly ISystemLogRepository _systemLog;

+ 7 - 0
src/Hotline/Caching/Interfaces/ISysDicDataCacheManager.cs

@@ -56,6 +56,13 @@ namespace Hotline.Caching.Interfaces
         /// </summary>
         IReadOnlyCollection<SystemDicDataOutDto> SnapshotBulletinSource { get; }
 
+        /// <summary>
+        /// 随手拍公告来源
+        /// </summary>
+        /// <param name="dicDataValue"></param>
+        /// <returns></returns>
+        SystemDicDataOutDto GetSnapshotBulletinType(string dicDataValue);
+
         /// <summary>
         /// 红包补充发放类型
         /// </summary>

+ 4 - 0
src/Hotline/Caching/Services/SysDicDataCacheManager.cs

@@ -103,6 +103,9 @@ namespace Hotline.Caching.Services
         /// 随手拍公告类型
         /// </summary>
         public IReadOnlyList<SystemDicDataOutDto> SnapshotBulletinType => GetOrAdd(SysDicTypeConsts.SnapshotBulletinType);
+        public SystemDicDataOutDto GetSnapshotBulletinType(string dicDataValue)
+            => this.SnapshotBulletinType.FirstOrDefault(x => x.DicDataValue == dicDataValue) ?? new SystemDicDataOutDto();
+
 
         /// <summary>
         /// 作业区域
@@ -151,5 +154,6 @@ namespace Hotline.Caching.Services
         {
             _cacheSysDicData.Remove(code);
         }
+
     }
 }

+ 1 - 1
src/Hotline/Exams/Trains/TrainPlanTemplate.cs

@@ -22,6 +22,6 @@ namespace Hotline.Exams.Trains
         /// </summary>
         [Description("培训模版Id")]
         [SugarColumn(ColumnDescription ="培训模版Id")]
-        public string TraimTemplateId { get; set; }
+        public string TrainTemplateId { get; set; }
     }
 }

+ 24 - 2
src/Hotline/Orders/Citizen.cs

@@ -7,6 +7,7 @@ using System.Threading.Tasks;
 using Hotline.Orders;
 using Hotline.Quality;
 using Hotline.Share.Enums.Order;
+using Hotline.Share.Enums.Snapshot;
 using SqlSugar;
 using XF.Domain.Repository;
 
@@ -88,7 +89,28 @@ namespace Hotline.Orders
 		/// 标签记录
 		/// </summary>
 		[Navigate(NavigateType.OneToMany, nameof(CitizenLabelDetail.CitizenId))]
-		public List<CitizenLabelDetail> labelDetails { get; set; }	
+		public List<CitizenLabelDetail> labelDetails { get; set; }
 
-	}
+        #region 随手拍
+        /// <summary>
+        /// 用户自己填的邀请码
+        /// </summary>
+        [SugarColumn(ColumnDescription = "用户自己填的邀请码")]
+        public string? InvitationCode { get; set; }
+
+        /// <summary>
+        /// 历史已经领取金额总和(单位:元)
+        /// </summary>
+        [SugarColumn(ColumnDescription = "历史已经领取金额总和(单位:元)")]
+        public double? TotalAmount { get; set; }
+
+        /// <summary>
+        /// 用户类型
+        /// 注册时根据手机号码判断是否是 网格员
+        /// </summary>
+        [SugarColumn(ColumnDescription = "用户类型")]
+        public EReadPackUserType? CitizenType { get; set; }
+        #endregion
+
+    }
 }

+ 2 - 2
src/Hotline/Orders/ICitizenRepository.cs

@@ -6,8 +6,8 @@ namespace Hotline.Orders
 	public interface ICitizenRepository : IRepository<Citizen>
 	{
 		Task AddAsync(CitizenAddDto citizen, CancellationToken cancellationToken);
-
-		Task LabelAddAsync(LabelDetailAddDto model, CancellationToken cancellationToken);
+        Task<Citizen> GetByPhoneNumberAsync(string phoneNumber);
+        Task LabelAddAsync(LabelDetailAddDto model, CancellationToken cancellationToken);
 
 		Task LabelDeleteAsync(CitizenLabelDetail model, CancellationToken cancellationToken);
 

+ 1 - 0
src/Hotline/SeedData/SystemDicDataSeedData.cs

@@ -172,6 +172,7 @@ public class SystemDicDataSeedData : ISeedData<SystemDicData>
                 new() { Id = "08dc0681-e9e1-493b-889d-3a238c2dde39", DicDataValue = "15", DicDataName = "城市管理操作指引", Sort = 15 },
                 new() { Id = "08dc0681-ed2e-40bb-829b-c8ee518b3229", DicDataValue = "16", DicDataName = "宣传学习安全隐患", Sort = 16 },
                 new() { Id = "08dc0681-ee29-4624-865c-58d5d9667c92", DicDataValue = "17", DicDataName = "安全隐患操作指引", Sort = 17 },
+                new() { Id = "08dd6a95-9d0d-4ffd-82f6-0037ced40215", DicDataValue = "SSPBanner", DicDataName = "随手拍Banner", Sort = 18 },
                 ];
         }
 

+ 6 - 0
src/Hotline/Snapshot/CommunityInfo.cs

@@ -29,6 +29,12 @@ public class CommunityInfo : CreationSoftDeleteEntity
     [SugarColumn(ColumnDescription = "社区全称")]
     public string FullName { get; set; }
 
+    /// <summary>
+    /// 部门编号
+    /// </summary>
+    [SugarColumn(ColumnDescription = "部门编号")]
+    public string? DepartmentNo { get; set; }
+
     /// <summary>
     /// 父社区Code
     /// </summary>

+ 0 - 18
src/Hotline/Snapshot/Interfaces/ISnapshotUserInfoRepository.cs

@@ -1,18 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using XF.Domain.Repository;
-
-namespace Hotline.Snapshot.Interfaces;
-public interface ISnapshotUserInfoRepository : IRepository<SnapshotUserInfo>
-{
-    /// <summary>
-    /// 根据电话号码获取用户信息
-    /// </summary>
-    /// <param name="phoneNumber"></param>
-    /// <returns></returns>
-    Task<SnapshotUserInfo?> GetByPhoneNumberAsync(string? phoneNumber);
-}
-

+ 0 - 49
src/Hotline/Snapshot/SnapshotUserInfo.cs

@@ -1,49 +0,0 @@
-using Hotline.Share.Enums.Snapshot;
-using SqlSugar;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using XF.Domain.Repository;
-
-namespace Hotline.Snapshot;
-
-/// <summary>
-/// 网格员和普通随手拍人员信息
-/// </summary>
-[Description("随手拍用户信息")]
-public class SnapshotUserInfo : CreationSoftDeleteEntity
-{
-    /// <summary>
-    /// 用户自己填的邀请码
-    /// </summary>
-    [SugarColumn(ColumnDescription = "用户自己填的邀请码")]
-    public string? InvitationCode { get; set; }
-
-    /// <summary>
-    /// 历史已经领取金额总和(单位:元)
-    /// </summary>
-    [SugarColumn(ColumnDescription = "历史已经领取金额总和(单位:元)")]
-    public double TotalAmount { get; set; }
-
-    /// <summary>
-    /// 用户类型
-    /// 注册时根据手机号码判断是否是 网格员
-    /// </summary>
-    [SugarColumn(ColumnDescription = "用户类型")]
-    public EReadPackUserType CitizenType { get; set; }
-
-    /// <summary>
-    /// 姓名
-    /// </summary>
-    [SugarColumn(ColumnDescription = "姓名")]
-    public string? Name { get; set; }
-
-    /// <summary>
-    /// 电话号码
-    /// </summary>
-    [SugarColumn(ColumnDescription = "电话号码")]
-    public string? PhoneNumber { get; set; }
-}

+ 1 - 1
src/Hotline/ThirdAccountDomainServices/Interfaces/IThirdAccountSupplier.cs

@@ -25,7 +25,7 @@ public interface IThirdAccountSupplier
     /// <param name="claims"></param>
     /// <param name="token"></param>
     /// <returns></returns>
-    Task<List<Claim>> GetClaimAsync(ThirdAccount account, List<Claim> claims, CancellationToken token);
+    Task<string> GetClaimAsync(ThirdAccount account, List<Claim> claims, CancellationToken token);
 
     /// <summary>
     /// 登录成功后返回给前端的参数

+ 1 - 1
src/Hotline/ThirdAccountDomainServices/ThirdAccounSupplierFactory.cs

@@ -26,7 +26,7 @@ public class ThirdAccounSupplierFactory : IThirdAccountSupplier, ISelfDependency
         .FirstOrDefault(supplier => supplier.GetType().Name.ToLower().StartsWith(enumType.ToString().ToLower()))
         ?? _serviceItems.First();
 
-    public async Task<List<Claim>> GetClaimAsync(ThirdAccount account, List<Claim> claims, CancellationToken token)
+    public async Task<string> GetClaimAsync(ThirdAccount account, List<Claim> claims, CancellationToken token)
         => await GetSupplier(account.AppType).GetClaimAsync(account, claims, token);
 
     public async Task<Dictionary<string, object>> GetLoginOutDataAsync(ThirdAccount thirdAccount, Dictionary<string, object> dicOutData, CancellationToken cancel)

+ 9 - 0
test/Hotline.Tests/Application/IndustryApplicationTest.cs

@@ -17,6 +17,7 @@ using Hotline.Settings;
 using XF.Domain.Cache;
 using Hotline.ThirdAccountDomainServices.Interfaces;
 using Hotline.ThirdAccountDomainServices;
+using Hotline.Repository.SqlSugar.Extensions;
 
 namespace Hotline.Tests.Application;
 public class IndustryApplicationTest : TestBase
@@ -32,6 +33,14 @@ public class IndustryApplicationTest : TestBase
         _systemOrganizeRepository = systemOrganizeRepository;
     }
 
+    [Fact]
+    public async Task GetVolunteerReportItems_Test()
+    {
+        var inDto = new VolunteerReportItemsInDto(null, null);
+        var items = await _industryApplication.GetVolunteerReportItems(inDto).ToListAsync();
+        items.ShouldNotBeNull();
+    }
+
     [Fact]
     public async Task UpdateIndustry_Test()
     {

+ 5 - 4
test/Hotline.Tests/Application/InviteCodeApplicationTest.cs

@@ -2,6 +2,7 @@
 using Hotline.Application.Snapshot;
 using Hotline.Identity.Accounts;
 using Hotline.Identity.Roles;
+using Hotline.Orders;
 using Hotline.Repository.SqlSugar.Snapshot;
 using Hotline.Settings;
 using Hotline.Share.Dtos.Snapshot;
@@ -29,16 +30,16 @@ public class InviteCodeApplicationTest : TestBase
     private readonly IInviteCodeRepository _inviteCodeRepository;
     private readonly ISessionContext _sessionContext;
     private readonly IInviteCodeRecordRepository _inviteCodeRecordRepository;
-    private readonly ISnapshotUserInfoRepository _snapshotUserInfoRepository;
+    private readonly ICitizenRepository _citizenRepository;
 
-    public InviteCodeApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, IThirdIdentiyService thirdIdentiyService, IThirdAccountRepository thirdAccountRepository, IInviteCodeApplication inviteCodeApplication, ISnapshotApplication snapshotApplication, IInviteCodeRepository inviteCodeRepository, ISessionContext sessionContext, IInviteCodeRecordRepository inviteCodeRecordRepository, ITypedCache<SystemSetting> cacheSettingData, ISnapshotUserInfoRepository snapshotUserInfoRepository, ThirdAccounSupplierFactory thirdAccountDomainFactory) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdIdentiyService, thirdAccountRepository, cacheSettingData, thirdAccountDomainFactory)
+    public InviteCodeApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, IThirdIdentiyService thirdIdentiyService, IThirdAccountRepository thirdAccountRepository, IInviteCodeApplication inviteCodeApplication, ISnapshotApplication snapshotApplication, IInviteCodeRepository inviteCodeRepository, ISessionContext sessionContext, IInviteCodeRecordRepository inviteCodeRecordRepository, ITypedCache<SystemSetting> cacheSettingData, ThirdAccounSupplierFactory thirdAccountDomainFactory, ICitizenRepository citizenRepository) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdIdentiyService, thirdAccountRepository, cacheSettingData, thirdAccountDomainFactory)
     {
         _inviteCodeApplication = inviteCodeApplication;
         _snapshotApplication = snapshotApplication;
         this._inviteCodeRepository = inviteCodeRepository;
         _sessionContext = sessionContext;
         _inviteCodeRecordRepository = inviteCodeRecordRepository;
-        _snapshotUserInfoRepository = snapshotUserInfoRepository;
+        _citizenRepository = citizenRepository;
     }
 
     [Fact]
@@ -59,7 +60,7 @@ public class InviteCodeApplicationTest : TestBase
 
         SetWeiXin();
 
-        await _snapshotUserInfoRepository.Updateable()
+        await _citizenRepository.Updateable()
             .SetColumns(m => m.InvitationCode, null)
             .Where(m => m.Id == _sessionContext.UserId)
             .ExecuteCommandAsync();

+ 18 - 6
test/Hotline.Tests/Application/OrderSnapshotApplicationTest.cs

@@ -44,8 +44,9 @@ public class OrderSnapshotApplicationTest : TestBase
     private readonly ISnapshotLabelLogRepository _snapshotLabelLogRepository;
     private readonly IRedPackApplication _redPackApplication;
     private readonly IOrderRepository _orderRepository;
+    private readonly IIndustryCaseRepository _industryCaseRepository;
 
-    public OrderSnapshotApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, IThirdIdentiyService thirdIdentiyService, IThirdAccountRepository thirdAccountRepository, OrderServiceMock orderServiceMock, ISystemDicDataCacheManager systemDicDataCacheManager, IOrderSnapshotRepository orderSnapshotRepository, IOrderSnapshotApplication orderSnapshotApplication, ISnapshotApplication snapshotApplication, IIndustryLogRepository industryLogRepository, ICommunityInfoRepository communityInfoRepository, IRedPackAuditRepository redPackAuditRepository, IRedPackRecordRepository redPackRecordRepository, ISnapshotLabelLogRepository snapshotLabelLogRepository, ITypedCache<SystemSetting> cacheSettingData, IRedPackApplication redPackApplication, IOrderRepository orderRepository, ThirdAccounSupplierFactory thirdAccountDomainFactory) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdIdentiyService, thirdAccountRepository, cacheSettingData, thirdAccountDomainFactory)
+    public OrderSnapshotApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, IThirdIdentiyService thirdIdentiyService, IThirdAccountRepository thirdAccountRepository, OrderServiceMock orderServiceMock, ISystemDicDataCacheManager systemDicDataCacheManager, IOrderSnapshotRepository orderSnapshotRepository, IOrderSnapshotApplication orderSnapshotApplication, ISnapshotApplication snapshotApplication, IIndustryLogRepository industryLogRepository, ICommunityInfoRepository communityInfoRepository, IRedPackAuditRepository redPackAuditRepository, IRedPackRecordRepository redPackRecordRepository, ISnapshotLabelLogRepository snapshotLabelLogRepository, ITypedCache<SystemSetting> cacheSettingData, IRedPackApplication redPackApplication, IOrderRepository orderRepository, ThirdAccounSupplierFactory thirdAccountDomainFactory, IIndustryCaseRepository industryCaseRepository) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdIdentiyService, thirdAccountRepository, cacheSettingData, thirdAccountDomainFactory)
     {
         _orderServiceMock = orderServiceMock;
         _systemDicDataCacheManager = systemDicDataCacheManager;
@@ -59,6 +60,7 @@ public class OrderSnapshotApplicationTest : TestBase
         _snapshotLabelLogRepository = snapshotLabelLogRepository;
         _redPackApplication = redPackApplication;
         _orderRepository = orderRepository;
+        _industryCaseRepository = industryCaseRepository;
     }
 
     /// <summary>
@@ -99,6 +101,7 @@ public class OrderSnapshotApplicationTest : TestBase
         SetSettingCache(SettingConstants.OvertimeBack, "4");
         var snapshotLabels = _systemDicDataCacheManager.SnapshotOrderLabel;
         var inputLable = snapshotLabels.Where(m => m.DicDataValue == "bss").ToList();
+        var industryCase = await _industryCaseRepository.Queryable().Where(m => m.IsEnable ==  true).FirstAsync();
         var order = _orderServiceMock.CreateSnapshotOrder(SetWeiXin)
             .办理到网格员(SetZuoXi)
             .StepHandle(async (order, mock) =>
@@ -121,7 +124,7 @@ public class OrderSnapshotApplicationTest : TestBase
                     .Where(m => m.Id == order.Id)
                     .ExecuteCommandAsync();
                 orderSnapshot = await _orderSnapshotRepository.GetAsync(order.Id);
-                var replyDto =  mock.GetGuiderSystemInDto(orderSnapshot.NetworkENumber);
+                var replyDto = mock.GetGuiderSystemInDto(orderSnapshot.NetworkENumber);
                 await _snapshotApplication.SaveGuiderSystemReplyAsync(replyDto, CancellationToken.None);
                 orderSnapshot = await _orderSnapshotRepository.GetAsync(order.Id);
                 orderSnapshot.CommunityName.ShouldNotBeNull();
@@ -140,13 +143,22 @@ public class OrderSnapshotApplicationTest : TestBase
             })
             .办理到派单员(Set班长)
             .办理到一级部门(SetPaiDanYuan)
-            .办理到归档(Set一级部门)
+            .办理到归档(Set一级部门, data =>
+            {
+                data.IsDangerDepartment = true;
+                data.IndustryCase = industryCase.Id;
+                data.IsRectifyDepartment = true;
+            })
+            .StepHandle(async order => {
+                var snapshot = await _orderSnapshotRepository.GetAsync(order.Id);
+                snapshot.IndustryCase.ShouldBe(industryCase.Id);
+            })
             .发布工单(SetZuoXi, inputLable.Select(m => new Kv(m.DicDataName, m.DicDataName)).ToList())
             .StepHandle(async order =>
             {
                 var log = _snapshotLabelLogRepository.Queryable().Where(m => m.OrderId == order.Id).First();
                 log.ShouldNotBeNull();
-                await _orderSnapshotApplication.UpdateIsEmphasisAsync(new UpdateIsEmphasisInDto { Ids = [order.Id]});
+                await _orderSnapshotApplication.UpdateIsEmphasisAsync(new UpdateIsEmphasisInDto { Ids = [order.Id] });
                 var snapshot = _orderSnapshotRepository.Get(order.Id);
                 snapshot.LabelName.ShouldBe(string.Join(',', inputLable.Select(m => m.DicDataName)), "label异常");
                 snapshot.IsEmphasis.ShouldBe(true);
@@ -169,9 +181,9 @@ public class OrderSnapshotApplicationTest : TestBase
                 redPackRecord.ShouldNotBeNull();
             })
             .部门审核特殊红包(Set应急管理局)
-            .StepHandle(async order => 
+            .StepHandle(async order =>
             {
-                 var baseData = await _redPackApplication.GetRedPackRecordBaseDataAsync(order.Id);
+                var baseData = await _redPackApplication.GetRedPackRecordBaseDataAsync(order.Id);
                 baseData.AuditType.ShouldNotBeNull();
                 baseData.AuditTypeCode.ShouldNotBeNull();
                 baseData.Amount.ShouldNotBeNull();

+ 4 - 4
test/Hotline.Tests/Application/OrderVisitApplicationTest.cs

@@ -30,9 +30,9 @@ public class OrderVisitApplicationTest
         var items = await _orderVisitApplication.QueryOrderVisitQuantityAsync(inDto);
         items.ShouldNotBeNull();
 
-        if (name.NotNullOrEmpty())
-        {
-            items.Any().ShouldBeTrue();
-        }
+        //if (name.NotNullOrEmpty())
+        //{
+        //    items.Any().ShouldBeTrue();
+        //}
     }
 }

+ 5 - 5
test/Hotline.Tests/Application/SnapshotApplicationTest.cs

@@ -50,9 +50,9 @@ public class SnapshotApplicationTest : TestBase
     private readonly IIndustryLogRepository _industryLogRepository;
     private readonly IRedPackApplication _redPackApplication;
     private readonly IOrderSnapshotApplication _orderSnapshotApplication;
-    private readonly ISnapshotUserInfoRepository _snapshotUserInfoRepository;
+    private readonly ICitizenRepository _citizenRepository;
 
-    public SnapshotApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, ISnapshotApplication snapshotApplication, IIdentityAppService identityAppService, IRepository<RedPackRecord> redPackRecordRepository, IIndustryApplication industryApplication, IIndustryRepository industryRepository, IFileRepository fileRepository, OrderServiceMock orderServiceMock, IOrderRepository orderRepository, IOrderSnapshotRepository orderSnapshotRepository, IThirdIdentiyService thirdService, IThirdAccountRepository thirdAccount, ISessionContext sessionContext, IGuiderSystemService guiderSystemService, ISystemSettingCacheManager systemSettingCacheManager, ICommunityInfoRepository communityInfoRepository, IIndustryLogRepository industryLogRepository, IRedPackApplication redPackApplication, IOrderSnapshotApplication orderSnapshotApplication, ITypedCache<SystemSetting> cacheSettingData, ThirdAccounSupplierFactory thirdAccountDomainFactory, ISnapshotUserInfoRepository snapshotUserInfoRepository) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdService, thirdAccount, cacheSettingData, thirdAccountDomainFactory)
+    public SnapshotApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, ISnapshotApplication snapshotApplication, IIdentityAppService identityAppService, IRepository<RedPackRecord> redPackRecordRepository, IIndustryApplication industryApplication, IIndustryRepository industryRepository, IFileRepository fileRepository, OrderServiceMock orderServiceMock, IOrderRepository orderRepository, IOrderSnapshotRepository orderSnapshotRepository, IThirdIdentiyService thirdService, IThirdAccountRepository thirdAccount, ISessionContext sessionContext, IGuiderSystemService guiderSystemService, ISystemSettingCacheManager systemSettingCacheManager, ICommunityInfoRepository communityInfoRepository, IIndustryLogRepository industryLogRepository, IRedPackApplication redPackApplication, IOrderSnapshotApplication orderSnapshotApplication, ITypedCache<SystemSetting> cacheSettingData, ThirdAccounSupplierFactory thirdAccountDomainFactory, ICitizenRepository citizenRepository) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdService, thirdAccount, cacheSettingData, thirdAccountDomainFactory)
     {
         _snapshotApplication = snapshotApplication;
         _identityAppService = identityAppService;
@@ -71,7 +71,7 @@ public class SnapshotApplicationTest : TestBase
         _industryLogRepository = industryLogRepository;
         _redPackApplication = redPackApplication;
         _orderSnapshotApplication = orderSnapshotApplication;
-        _snapshotUserInfoRepository = snapshotUserInfoRepository;
+        _citizenRepository = citizenRepository;
     }
 
     /// <summary>
@@ -367,12 +367,12 @@ public class SnapshotApplicationTest : TestBase
         var code = new Random().Next(100, 200).ToString();
         try
         {
-            await _snapshotUserInfoRepository.Updateable()
+            await _citizenRepository.Updateable()
                 .SetColumns(m => m.InvitationCode, null)
                 .Where(m => m.Id == _sessionContext.UserId)
                 .ExecuteCommandAsync();
             await _snapshotApplication.SaveInvitationCodeAsync(new SaveInvitationCodeInDto { InvitationCode = code });
-            var userInfo = await _snapshotUserInfoRepository.GetAsync(_sessionContext.UserId);
+            var userInfo = await _citizenRepository.GetAsync(_sessionContext.UserId);
             userInfo.ShouldNotBeNull();
             userInfo!.InvitationCode.ShouldBe(code);
         }

+ 3 - 3
test/Hotline.Tests/TestBase.cs

@@ -207,9 +207,9 @@ public class TestBase
         ];
         if (appType != null && thirdType != null)
         {
-            var claims = _thirdAccountDomainFactory.GetClaimAsync(new ThirdAccount { Id = thirdAccount?.Id, PhoneNumber = phoneNo, AccountType = thirdType.Value, AppType = appType.Value, ExternalId = thirdAccount?.ExternalId }, new List<Claim>(), CancellationToken.None).GetAwaiter().GetResult();
-            if (claims.NotNullOrEmpty())
-                userClaims.Add(new(ClaimTypes.NameIdentifier, claims.First().Value));
+            var claims = new List<Claim>();
+            var userId = _thirdAccountDomainFactory.GetClaimAsync(new ThirdAccount { Id = thirdAccount?.Id, PhoneNumber = phoneNo, AccountType = thirdType.Value, AppType = appType.Value, ExternalId = thirdAccount?.ExternalId }, claims, CancellationToken.None).GetAwaiter().GetResult();
+            userClaims.Add(new(ClaimTypes.NameIdentifier, userId));
         }
         else
         {

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů