Przeglądaj źródła

merge test -> release

xf 3 miesięcy temu
rodzic
commit
036db144a2
100 zmienionych plików z 3978 dodań i 1403 usunięć
  1. 9 2
      Hotline.sln
  2. 75 3
      src/Hotline.Api/Controllers/Bi/BiQualityController.cs
  3. 13 2
      src/Hotline.Api/Controllers/ExportData/ExportDataController.cs
  4. 7 1
      src/Hotline.Api/Controllers/ExportWordController.cs
  5. 105 77
      src/Hotline.Api/Controllers/KnowledgeCommonController.cs
  6. 494 66
      src/Hotline.Api/Controllers/KnowledgeController.cs
  7. 117 109
      src/Hotline.Api/Controllers/OrderController.cs
  8. 54 40
      src/Hotline.Api/Controllers/QualityController.cs
  9. 2 14
      src/Hotline.Api/Controllers/Snapshot/SnapshotBulletinController.cs
  10. 12 2
      src/Hotline.Api/Controllers/Snapshot/SnapshotController.cs
  11. 26 26
      src/Hotline.Api/Controllers/SysController.cs
  12. 8 7
      src/Hotline.Api/Controllers/TestController.cs
  13. 515 4
      src/Hotline.Api/Controllers/WebPortalController.cs
  14. 4 1
      src/Hotline.Api/StartupExtensions.cs
  15. 5 4
      src/Hotline.Api/config/appsettings.Development.json
  16. 18 14
      src/Hotline.Application.Tests/Application/KnowApplicationTest.cs
  17. 2 1
      src/Hotline.Application.Tests/Controller/SnapshotControllerTest.cs
  18. 1 1
      src/Hotline.Application.Tests/Startup.cs
  19. 3 1
      src/Hotline.Application.Tests/TestBase.cs
  20. 17 2
      src/Hotline.Application/ExportExcel/ExportApplication.cs
  21. 1 1
      src/Hotline.Application/ExportWord/WordHelper.cs
  22. 13 3
      src/Hotline.Application/Handlers/FlowEngine/WorkflowEndHandler.cs
  23. 1 0
      src/Hotline.Application/Hotline.Application.csproj
  24. 83 40
      src/Hotline.Application/Jobs/XingTangCallsSyncJob.cs
  25. 44 21
      src/Hotline.Application/Jobs/XingTangTelOperationSyncJob.cs
  26. 148 61
      src/Hotline.Application/Knowledge/KnowApplication.cs
  27. 39 2
      src/Hotline.Application/Mappers/KnowledgeMapperConfigs.cs
  28. 6 6
      src/Hotline.Application/Orders/Handles/OrderScreenHandler/OrderScreenNextWorkflowHandler.cs
  29. 0 7
      src/Hotline.Application/Orders/IOrderApplication.cs
  30. 47 21
      src/Hotline.Application/Orders/OrderApplication.cs
  31. 3 0
      src/Hotline.Application/Quality/IQualityApplication.cs
  32. 13 4
      src/Hotline.Application/Quality/QualityApplication.cs
  33. 4 1
      src/Hotline.Application/Snapshot/ISnapshotBulletinApplication.cs
  34. 7 4
      src/Hotline.Application/Snapshot/IndustryApplication.cs
  35. 2 0
      src/Hotline.Application/Snapshot/InviteCodeApplication.cs
  36. 7 6
      src/Hotline.Application/Snapshot/RedPackApplication.cs
  37. 23 1
      src/Hotline.Application/Snapshot/SnapshotBulletinApplication.cs
  38. 7 0
      src/Hotline.Application/Snapshot/SnapshotOrderApplication.cs
  39. 48 3
      src/Hotline.Application/Tools/StringExtensions.cs
  40. 17 0
      src/Hotline.Pdf/Hotline.Pdf.csproj
  41. 52 0
      src/Hotline.Pdf/QuestPdfManager.cs
  42. 16 0
      src/Hotline.Pdf/QuestPdfManagerStartupExtensions.cs
  43. 1 1
      src/Hotline.Repository.SqlSugar/CallCenter/TrCallRecordRepository.cs
  44. 5 3
      src/Hotline.Repository.SqlSugar/Orders/OrderRepository.cs
  45. 19 0
      src/Hotline.Share/Attributes/ExportExcelAttribute.cs
  46. 19 0
      src/Hotline.Share/Dtos/Knowledge/ApproveBatchRequest.cs
  47. 14 0
      src/Hotline.Share/Dtos/Knowledge/DeleteKnowledgeDto.cs
  48. 15 0
      src/Hotline.Share/Dtos/Knowledge/ExportKnowledgeRequest.cs
  49. 98 0
      src/Hotline.Share/Dtos/Knowledge/KnowPagedListDto.cs
  50. 74 0
      src/Hotline.Share/Dtos/Knowledge/KnowledgeApproveDto.cs
  51. 29 28
      src/Hotline.Share/Dtos/Knowledge/KnowledgeDataDto.cs
  52. 107 125
      src/Hotline.Share/Dtos/Knowledge/KnowledgeDto.cs
  53. 32 0
      src/Hotline.Share/Dtos/Knowledge/KnowledgeInfoDto.cs
  54. 2 111
      src/Hotline.Share/Dtos/Knowledge/KnowledgePagedDto.cs
  55. 14 0
      src/Hotline.Share/Dtos/Knowledge/OffShelfKnowledgeDto.cs
  56. 11 0
      src/Hotline.Share/Dtos/Knowledge/PublishBatchRequest.cs
  57. 28 0
      src/Hotline.Share/Dtos/Knowledge/QueryKnowledgeApprovePagedRequest.cs
  58. 7 0
      src/Hotline.Share/Dtos/Order/OrderBiDto.cs
  59. 19 8
      src/Hotline.Share/Dtos/Order/OrderDto.cs
  60. 69 0
      src/Hotline.Share/Dtos/Order/OrderFlowDto.cs
  61. 16 0
      src/Hotline.Share/Dtos/Order/OrderRelationTagDto.cs
  62. 10 0
      src/Hotline.Share/Dtos/Order/OrderVisitDto.cs
  63. 0 5
      src/Hotline.Share/Dtos/Order/PublishedDto.cs
  64. 6 1
      src/Hotline.Share/Dtos/Order/QueryOrderDto.cs
  65. 19 0
      src/Hotline.Share/Dtos/Quality/BiQualityDto.cs
  66. 228 210
      src/Hotline.Share/Dtos/Quality/QualityDto.cs
  67. 1 1
      src/Hotline.Share/Dtos/QualityExportWord/QualityCertificate.cs
  68. 16 0
      src/Hotline.Share/Dtos/Snapshot/IndustryDto.cs
  69. 45 2
      src/Hotline.Share/Dtos/Snapshot/OrderDto.cs
  70. 2 0
      src/Hotline.Share/Dtos/Snapshot/RedPackRecordDto.cs
  71. 28 1
      src/Hotline.Share/Dtos/WebPortal/ArticleDetailsDto.cs
  72. 14 0
      src/Hotline.Share/Dtos/WebPortal/WaitVisitListDataDto.cs
  73. 33 0
      src/Hotline.Share/Enums/FlowEngine/EBusinessType.cs
  74. 18 0
      src/Hotline.Share/Enums/KnowledgeBase/EKnowledgeApproveStatus.cs
  75. 21 0
      src/Hotline.Share/Enums/KnowledgeBase/EKnowledgeApproveType.cs
  76. 88 25
      src/Hotline.Share/Enums/KnowledgeBase/EKnowledgeStatus.cs
  77. 2 2
      src/Hotline.Share/Enums/Quality/EQuality.cs
  78. 0 29
      src/Hotline.Share/Enums/Snapshot/EJobType.cs
  79. 1 1
      src/Hotline.Share/Hotline.Share.csproj
  80. 82 6
      src/Hotline.Share/Requests/PagedKeywordRequest.cs
  81. 20 0
      src/Hotline.Share/Tools/MethodInfoExtensions.cs
  82. 1 1
      src/Hotline/FlowEngine/WorkflowModules/WorkflowModuleConsts.cs
  83. 1 1
      src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs
  84. 26 7
      src/Hotline/KnowledgeBase/IKnowledgeDomainService.cs
  85. 198 149
      src/Hotline/KnowledgeBase/Knowledge.cs
  86. 80 0
      src/Hotline/KnowledgeBase/KnowledgeApprove.cs
  87. 175 52
      src/Hotline/KnowledgeBase/KnowledgeDomainService.cs
  88. 46 0
      src/Hotline/KnowledgeBase/KnowledgeImportTemplate.cs
  89. 6 0
      src/Hotline/KnowledgeBase/KnowledgePV.cs
  90. 15 12
      src/Hotline/KnowledgeBase/KnowledgeRelationType.cs
  91. 8 1
      src/Hotline/KnowledgeBase/KnowledgeType.cs
  92. 63 43
      src/Hotline/KnowledgeBase/KnowledgeTypeDomainService.cs
  93. 3 2
      src/Hotline/KnowledgeBase/KnowledgeTypeOrg.cs
  94. 24 12
      src/Hotline/Orders/Order.cs
  95. 24 0
      src/Hotline/Orders/OrderRelationTag.cs
  96. 14 0
      src/Hotline/Pdf/IPdfManager.cs
  97. 2 2
      src/Hotline/SeedData/SystemDicDataSeedData.cs
  98. 4 2
      src/Hotline/Settings/TimeLimitDomain/ExpireTimeSupplier/WorkDaySupplier.cs
  99. 1 1
      src/Hotline/Settings/TimeLimits/TimeLimit.cs
  100. 6 1
      src/Hotline/Snapshot/OrderSnapshot.cs

+ 9 - 2
Hotline.sln

@@ -59,9 +59,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.Logger", "src\Hotli
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.WeChat", "src\Hotline.WeChat\Hotline.WeChat.csproj", "{75215667-65AF-4B7B-85E7-3140239B30CC}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TianQue.Sdk", "src\TianQue.Sdk\TianQue.Sdk.csproj", "{6CF27647-D0E0-4D17-80FB-3EE57864A2B4}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TianQue.Sdk", "src\TianQue.Sdk\TianQue.Sdk.csproj", "{6CF27647-D0E0-4D17-80FB-3EE57864A2B4}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hotline.Ai.XingTang", "src\Hotline.Ai.XingTang\Hotline.Ai.XingTang.csproj", "{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.Ai.XingTang", "src\Hotline.Ai.XingTang\Hotline.Ai.XingTang.csproj", "{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.Pdf", "src\Hotline.Pdf\Hotline.Pdf.csproj", "{3AB75B51-A69D-4145-A564-1D9D1695992E}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -165,6 +167,10 @@ Global
 		{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3AB75B51-A69D-4145-A564-1D9D1695992E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3AB75B51-A69D-4145-A564-1D9D1695992E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3AB75B51-A69D-4145-A564-1D9D1695992E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3AB75B51-A69D-4145-A564-1D9D1695992E}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -197,6 +203,7 @@ Global
 		{75215667-65AF-4B7B-85E7-3140239B30CC} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{6CF27647-D0E0-4D17-80FB-3EE57864A2B4} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
+		{3AB75B51-A69D-4145-A564-1D9D1695992E} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {4B8EA790-BD13-4422-8D63-D6DBB77B823F}

+ 75 - 3
src/Hotline.Api/Controllers/Bi/BiQualityController.cs

@@ -9,6 +9,14 @@ using Hotline.Share.Dtos.Quality;
 using Hotline.Tools;
 using Hotline.Quality;
 using Hotline.Share.Enums.Quality;
+using Hotline.Caching.Services;
+using Hotline.Settings;
+using Hotline.Share.Enums.FlowEngine;
+using Hotline.Share.Enums.Order;
+using XF.Utility.EnumExtensions;
+using Hotline.Caching.Interfaces;
+using Hotline.Users;
+using XF.Domain.Repository;
 
 namespace Hotline.Api.Controllers.Bi
 {
@@ -17,17 +25,22 @@ namespace Hotline.Api.Controllers.Bi
 		private readonly IMapper _mapper;
 		private readonly IQualityApplication _qualityApplication;
 		private readonly IQualityRepository _qualityRepository;
-
+		private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+		private readonly IRepository<User> _userRepository;
 
 		public BiQualityController(
 			IMapper mapper,
 			IQualityApplication qualityApplication,
-			IQualityRepository qualityRepository
+			IQualityRepository qualityRepository,
+			ISystemSettingCacheManager systemSettingCacheManager,
+			IRepository<User> userRepository
 			)
 		{
 			_mapper = mapper;
 			_qualityApplication = qualityApplication;
 			_qualityRepository = qualityRepository;
+			_systemSettingCacheManager = systemSettingCacheManager;
+			_userRepository = userRepository;
 		}
 
 
@@ -115,7 +128,7 @@ namespace Hotline.Api.Controllers.Bi
 			dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
 
 			var dtos = listDtos
-				.Select(stu => _mapper.Map(stu, typeof(SeatsQualityAnalyseDto), dynamicClass))
+				.Select(stu => _mapper.Map(stu, typeof(QualityOrderOverviewDto), dynamicClass))
 				.Cast<object>()
 				.ToList();
 
@@ -125,6 +138,65 @@ namespace Hotline.Api.Controllers.Bi
 
 		}
 
+		/// <summary>
+		/// 坐席质检分值分析
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[HttpGet("seats_quality_grade_analyse")]
+		public async Task<SeatsQualityGradeAnalyseDto> SeatsQualityGradeAnalyse([FromQuery] QualityWorkAnalysisRequest dto)
+		{
+			var temps = await _qualityApplication.SeatsQualityGradeAnalyse(dto, HttpContext.RequestAborted).ToListAsync(HttpContext.RequestAborted);
+			var temp = new SeatsQualityGradeAnalyseDto()
+			{
+				NinetyGrade = temps.Sum(x => x.NinetyGrade),
+			    EightyGrade = temps.Sum(x => x.EightyGrade),
+				SeventyGrade = temps.Sum(x => x.SeventyGrade),
+				SixtyGrade = temps.Sum(x => x.SixtyGrade),
+				FiftyGrade = temps.Sum(x => x.FiftyGrade)
+			};
+			return temp;
+		}
+
+
+		/// <summary>
+		/// 质检分值分析/坐席质检工单分析
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[HttpGet("month_quality_grade")]
+		public async Task<List<MonthQualityGradeDto>> MonthQualityGrade([FromQuery] QualityWorkAnalysisRequest dto)
+		{
+			var items = await _qualityApplication.MonthQualityGrade(dto, HttpContext.RequestAborted);
+			return items;
+		}
+
+
+		/// <summary>
+		/// 列表页面基础数据
+		/// </summary>
+		/// <returns></returns>
+		[HttpGet("quality_work/base")]
+		public async Task<object> QualityWorkBaseData()
+		{
+			var setting = _systemSettingCacheManager.GetSetting(SettingConstants.RoleZuoXi);
+			var roles = setting?.SettingValue.ToList();
+			var users = await _userRepository.Queryable()
+				.Includes(d => d.Organization)
+				.Includes(d => d.Roles)
+				.Where(d => d.Roles.Any(x => roles.Contains(x.Name)))
+				.Select(x => new Kv {
+					Key = x.Id,
+					Value = x.Name
+				})
+				.ToListAsync(HttpContext.RequestAborted);
+
+			var rsp = new
+			{
+				Seats = users
+			};
+			return rsp;
+		}
 		#endregion
 	}
 }

+ 13 - 2
src/Hotline.Api/Controllers/ExportData/ExportDataController.cs

@@ -1,4 +1,5 @@
 using Hotline.Application.ExportExcel;
+using Hotline.Share.Attributes;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Tools;
 using Microsoft.AspNetCore.Mvc;
@@ -9,6 +10,16 @@ using XF.Domain.Exceptions;
 
 namespace Hotline.Api.Controllers.ExportData;
 
+/// <summary>
+/// 只要URL结尾是 export_excel 的请求都会进入这个控制器
+/// 逻辑:
+///     1. 通过URL找到对应的Controller和Action
+///     2. 通过Controller和Action找到对应的ApplicationService
+///        比如: RedPackController.GetRedPackList 就会去查找 RedPackApplication.GetRedPackList 方法
+///     3. 通过反射调用对应的方法
+///     4. ApplicationService 的方法返回的必须是 ISugarQueryable
+///     5. ApplicationService 中方法的ExportExcel属性会作为导出的文件名
+/// </summary>
 [ApiController]
 [Route("{*path:regex(.*export_excel$)}")]
 public class ExportDataController : BaseController
@@ -86,7 +97,7 @@ public class ExportDataController : BaseController
         var result = method.Invoke(serviceInstance, [queryDto]);
 
         var returnType = method.ReturnType.GetGenericArguments()[0];
-        var description = method.GetCustomAttribute<DescriptionAttribute>()?.Description;
+        var fileName = method.GetFileName() + "_";
         if (pageIndex == null || pageSize == null)
         {
             isExportAll = true;
@@ -94,7 +105,7 @@ public class ExportDataController : BaseController
             pageSize = 20;
         }
 
-        return _exportApplication.GetExcelFile(returnType, genericType, exportData, ConvertToList(result, (bool)isExportAll, (int)pageIndex, (int)pageSize), description);
+        return _exportApplication.GetExcelFile(returnType, genericType, exportData, ConvertToList(result, (bool)isExportAll, (int)pageIndex, (int)pageSize), fileName);
     }
 
     public static List<object>? ConvertToList(object? result, bool isExportAll, int pageIndex, int pageSize)

+ 7 - 1
src/Hotline.Api/Controllers/ExportWordController.cs

@@ -11,7 +11,9 @@ using MapsterMapper;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Options;
 using MongoDB.Driver.Linq;
+using Novacode.NETCorePort;
 using XF.Domain.Repository;
+using XF.Utility.EnumExtensions;
 
 namespace Hotline.Api.Controllers
 {
@@ -158,10 +160,14 @@ namespace Hotline.Api.Controllers
 					exportTest.CityName = "泸州市";
 
                 //质检信息
-                exportTest.Grade = quality.Grade;
+                exportTest.Grade = quality.Grade.ToString();
                 exportTest.QualityContent = quality.Content;
 				exportTest.UserName = quality.UserName;
 				exportTest.QualityTime = quality.QualityTime.HasValue? quality.QualityTime.Value.ToString("yyyy-MM-dd HH:mm:ss") :string.Empty;
+				exportTest.CenterToOrgTime = order.CenterToOrgTime.HasValue ? order.CenterToOrgTime.Value.ToString("yyyy-MM-dd HH:mm:ss") : string.Empty;
+                exportTest.CreationTime = order.CreationTime.ToString("yyyy-MM-dd HH:mm:ss");
+                exportTest.ExpiredTime = order.ExpiredTime.HasValue ? order.ExpiredTime.Value.ToString("yyyy-MM-dd HH:mm:ss") : string.Empty;
+				exportTest.FromGender = order.FromGender.GetDescription();
 
 				//查询回访信息
 				var visitData = await _orderVisitRepository.GetAsync(p => p.OrderId == order.Id && p.VisitState == EVisitState.Visited, HttpContext.RequestAborted);

+ 105 - 77
src/Hotline.Api/Controllers/KnowledgeCommonController.cs

@@ -3,6 +3,7 @@ using Hotline.Permissions;
 using Hotline.Settings;
 using Hotline.Settings.Hotspots;
 using Hotline.Share.Dtos.Knowledge;
+using Hotline.Share.Enums.KnowledgeBase;
 using MapsterMapper;
 using Microsoft.AspNetCore.Mvc;
 using SqlSugar;
@@ -24,19 +25,19 @@ namespace Hotline.Api.Controllers
         private readonly IRepository<KnowledgeStandard> _knowledgeStandardRepository;
         private readonly IMapper _mapper;
         private readonly ISessionContext _sessionContext;
-		private readonly ISystemOrganizeRepository _systemOrganizeRepository;
-		private readonly IRepository<Hotspot> _hotspotTypeRepository;
+        private readonly ISystemOrganizeRepository _systemOrganizeRepository;
+        private readonly IRepository<Hotspot> _hotspotTypeRepository;
 
-		/// <summary>
-		/// 
-		/// </summary>
-		/// <param name="knowledgeTypeRepository"></param>
-		/// <param name="knowledgeTypeDomainService"></param>
-		/// <param name="knowledgeStandardRepository"></param>
-		/// <param name="mapper"></param>
-		public KnowledgeCommonController(IRepository<KnowledgeType> knowledgeTypeRepository, IKnowledgeTypeDomainService knowledgeTypeDomainService,
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="knowledgeTypeRepository"></param>
+        /// <param name="knowledgeTypeDomainService"></param>
+        /// <param name="knowledgeStandardRepository"></param>
+        /// <param name="mapper"></param>
+        public KnowledgeCommonController(IRepository<KnowledgeType> knowledgeTypeRepository, IKnowledgeTypeDomainService knowledgeTypeDomainService,
             IRepository<KnowledgeStandard> knowledgeStandardRepository, ISessionContext sessionContext,
-			IMapper mapper, ISystemOrganizeRepository systemOrganizeRepository, IRepository<Hotspot> hotspotTypeRepository)
+            IMapper mapper, ISystemOrganizeRepository systemOrganizeRepository, IRepository<Hotspot> hotspotTypeRepository)
         {
             _knowledgeTypeRepository = knowledgeTypeRepository;
             _knowledgeTypeDomainService = knowledgeTypeDomainService;
@@ -45,7 +46,7 @@ namespace Hotline.Api.Controllers
             _sessionContext = sessionContext;
             _systemOrganizeRepository = systemOrganizeRepository;
             _hotspotTypeRepository = hotspotTypeRepository;
-		}
+        }
         #endregion
 
         #region 知识分类
@@ -93,8 +94,8 @@ namespace Hotline.Api.Controllers
         public async Task<KnowledgeType> GetType(string Id)
         {
             var types = await _knowledgeTypeRepository.Queryable()
-                .Includes(x=>x.KnowledgeTypeOrgs)
-                .Where(x=>x.Id  == Id)
+                .Includes(x => x.KnowledgeTypeOrgs)
+                .Where(x => x.Id == Id)
                 .FirstAsync(HttpContext.RequestAborted);
             if (types is null)
                 throw UserFriendlyException.SameMessage("查询失败!");
@@ -137,26 +138,53 @@ namespace Hotline.Api.Controllers
         /// <summary>
         /// 知识分类- 获取层级分类
         /// </summary>
-        /// <param name="IsEnable">不传查询所有</param>
-        /// <returns></returns>
         [HttpGet("treelist")]
-        public async Task<List<KnowledgeTypeDto>> GetTreeList(bool? IsEnable,string? Attribution)
+        public async Task<List<KnowledgeTypeDto>> GetTreeList(bool? isEnable, string? attribution, EKnowledgeStatus? status)
         {
-            return await _knowledgeTypeRepository.Queryable().WhereIF(IsEnable.HasValue, x =>x.IsEnable == IsEnable)
-				 //.Where(x => x.KnowledgeTypeOrgs.Any(to => to.OrgId == _sessionContext.RequiredOrgId) || x.KnowledgeTypeOrgs.Any() == false)
-				 .Where(x=> SqlFunc.Subqueryable<KnowledgeTypeOrg>().Where(to=>to.TypeId == x.Id && to.OrgId == _sessionContext.RequiredOrgId).Any()
-				 || SqlFunc.Subqueryable<KnowledgeTypeOrg>().Where(to => to.TypeId == x.Id).NotAny())
-				 .Select(x=> new KnowledgeTypeDto()
-				 {
+            var isCenter = _sessionContext.OrgIsCenter;
+            var query = _knowledgeTypeRepository.Queryable()
+                .WhereIF(isEnable.HasValue, x => x.IsEnable == isEnable);
+            if (!isCenter)
+                query.Where(x => x.Orgs.Any(s => s.Id == _sessionContext.RequiredOrgId));
+            return await query
+                 .Select(x => new KnowledgeTypeDto()
+                 {
                      Id = x.Id.SelectAll(),
-                     KnowledgeNum = SqlFunc.Subqueryable<KnowledgeRelationType>().LeftJoin<Knowledge>((kr,k)=> kr.KnowledgeId == k.Id)
-	                     .Where((kr, k)=>kr.KnowledgeTypeSpliceName.StartsWith(x.SpliceName))
-	                     .WhereIF(!string.IsNullOrEmpty(Attribution),(kr,k)=>k.Attribution == Attribution).DistinctCount(kr=>kr.KnowledgeId)
-
-				 })
-				 .OrderBy(x => x.Sort).ToTreeAsync(it => it.children, it => it.ParentId, null,it=> it.Id);
+                     //KnowledgeNum = SqlFunc.Subqueryable<KnowledgeRelationType>()
+                     //    .LeftJoin<Knowledge>((kr, k) => kr.KnowledgeId == k.Id)
+                     //    .Where((kr, k) => kr.KnowledgeTypeSpliceName.StartsWith(x.SpliceName))
+                     //    .WhereIF(!string.IsNullOrEmpty(attribution) && !isCenter, (kr, k) => k.Attribution == attribution)
+                     //    .DistinctCount(kr => kr.KnowledgeId),
+                     KnowledgeNum = SqlFunc.Subqueryable<Knowledge>()
+                         .InnerJoin<KnowledgeRelationType>((k, krt) => k.Id == krt.KnowledgeId)
+                         .InnerJoin<KnowledgeType>((k, krt, t) => krt.KnowledgeTypeId == t.Id)
+                         .Where((k, krt, t) => t.SpliceName.StartsWith(x.SpliceName))
+                         .WhereIF(!string.IsNullOrEmpty(attribution) && !isCenter, (k, krt, t) => k.Attribution == attribution)
+                         .WhereIF(status != null, k => k.Status == status)
+                         .DistinctCount(k => k.Id)
+                 })
+                 .OrderBy(x => x.Sort)
+                 .ToTreeAsync(it => it.children, it => it.ParentId, null, it => it.Id);
         }
 
+        //[HttpGet("treelist")]
+        //public async Task<List<KnowledgeTypeDto>> GetTreeList(bool? IsEnable, string? Attribution)
+        //{
+        //    return await _knowledgeTypeRepository.Queryable()
+        //        .WhereIF(IsEnable.HasValue, x => x.IsEnable == IsEnable)
+        //        .Where(x => SqlFunc.Subqueryable<KnowledgeTypeOrg>().Where(to => to.TypeId == x.Id && to.OrgId == _sessionContext.RequiredOrgId).Any()
+        //                    || SqlFunc.Subqueryable<KnowledgeTypeOrg>().Where(to => to.TypeId == x.Id).NotAny())
+        //        .Select(x => new KnowledgeTypeDto()
+        //        {
+        //            Id = x.Id.SelectAll(),
+        //            KnowledgeNum = SqlFunc.Subqueryable<KnowledgeRelationType>().LeftJoin<Knowledge>((kr, k) => kr.KnowledgeId == k.Id)
+        //                .Where((kr, k) => kr.KnowledgeTypeSpliceName.StartsWith(x.SpliceName))
+        //                .WhereIF(!string.IsNullOrEmpty(Attribution), (kr, k) => k.Attribution == Attribution).DistinctCount(kr => kr.KnowledgeId)
+
+        //        })
+        //        .OrderBy(x => x.Sort).ToTreeAsync(it => it.children, it => it.ParentId, null, it => it.Id);
+        //}
+
         /// <summary>
         /// 知识部门
         /// </summary>
@@ -166,25 +194,25 @@ namespace Hotline.Api.Controllers
         public async Task<List<KnowledgeOrgDto>> GetTreeList(string? Attribution)
         {
 
-	        //await Db.Queryable<SystemOrganize>()
-		       // .Where(it => it.Id.StartsWith(orgCode))
-		       // .OrderBy(d => d.Id)
-		       // .ToTreeAsync(it => it.Children, it => it.ParentId, orgCode.Length > 6 ? orgCode.Substring(0, orgCode.Length - 3) : null);
-	        var orgCode = _sessionContext.RequiredOrgId;
-			var query=  _systemOrganizeRepository.Queryable().WhereIF(!_sessionContext.OrgIsCenter, it => it.Id.StartsWith(_sessionContext.RequiredOrgId))
-					.Select(it=> new KnowledgeOrgDto()
-					{
-						Id = it.Id.SelectAll(),
-						KnowledgeNum = SqlFunc.Subqueryable<Knowledge>()
-							.Where(k => k.CreatorOrgId.StartsWith(it.Id))
-							.WhereIF(!string.IsNullOrEmpty(Attribution),  k => k.Attribution == Attribution).DistinctCount(k => k.Id)
+            //await Db.Queryable<SystemOrganize>()
+            // .Where(it => it.Id.StartsWith(orgCode))
+            // .OrderBy(d => d.Id)
+            // .ToTreeAsync(it => it.Children, it => it.ParentId, orgCode.Length > 6 ? orgCode.Substring(0, orgCode.Length - 3) : null);
+            var orgCode = _sessionContext.RequiredOrgId;
+            var query = _systemOrganizeRepository.Queryable().WhereIF(!_sessionContext.OrgIsCenter, it => it.Id.StartsWith(_sessionContext.RequiredOrgId))
+                    .Select(it => new KnowledgeOrgDto()
+                    {
+                        Id = it.Id.SelectAll(),
+                        KnowledgeNum = SqlFunc.Subqueryable<Knowledge>()
+                            .Where(k => k.CreatorOrgId.StartsWith(it.Id))
+                            .WhereIF(!string.IsNullOrEmpty(Attribution), k => k.Attribution == Attribution).DistinctCount(k => k.Id)
 
-					}).OrderBy(d => d.Id);
+                    }).OrderBy(d => d.Id);
 
-			if (_sessionContext.OrgIsCenter)
-			{
-				return await query.ToTreeAsync(x => x.Children, it => it.ParentId, null);
-			}
+            if (_sessionContext.OrgIsCenter)
+            {
+                return await query.ToTreeAsync(x => x.Children, it => it.ParentId, null);
+            }
             return await query.ToTreeAsync(it => it.Children, it => it.ParentId, orgCode.Length > 6 ? orgCode.Substring(0, orgCode.Length - 3) : null);
 
         }
@@ -195,20 +223,20 @@ namespace Hotline.Api.Controllers
         [HttpGet("treelist/hotspot")]
         public async Task<IReadOnlyList<KnowledgeHotSpotDto>> GetChildren([FromQuery] string? id, string? Attribution)
         {
-	        var list = await _hotspotTypeRepository.Queryable()
-		        .WhereIF(!string.IsNullOrEmpty(id), x => x.ParentId == id)
-		        .WhereIF(string.IsNullOrEmpty(id), x => x.ParentId == null || x.ParentId == "")
-		        .OrderBy(d => d.HotSpotName)
-		        .Select(x => new KnowledgeHotSpotDto
-				{
-					Id = x.Id.SelectAll(),
-					HasChild = SqlFunc.Subqueryable<Hotspot>().Where(d => d.ParentId == x.Id).NotAny(),
-					KnowledgeNum = SqlFunc.Subqueryable<Knowledge>().LeftJoin<Hotspot>((k,h)=>k.HotspotId == h.Id)
-						.Where((k,h) =>h.HotSpotFullName.StartsWith(x.HotSpotFullName))
-						.WhereIF(!string.IsNullOrEmpty(Attribution), (k,h) => k.Attribution == Attribution).DistinctCount(k => k.Id)
-				}).ToListAsync();
+            var list = await _hotspotTypeRepository.Queryable()
+                .WhereIF(!string.IsNullOrEmpty(id), x => x.ParentId == id)
+                .WhereIF(string.IsNullOrEmpty(id), x => x.ParentId == null || x.ParentId == "")
+                .OrderBy(d => d.HotSpotName)
+                .Select(x => new KnowledgeHotSpotDto
+                {
+                    Id = x.Id.SelectAll(),
+                    HasChild = SqlFunc.Subqueryable<Hotspot>().Where(d => d.ParentId == x.Id).NotAny(),
+                    KnowledgeNum = SqlFunc.Subqueryable<Knowledge>().LeftJoin<Hotspot>((k, h) => k.HotspotId == h.Id)
+                        .Where((k, h) => h.HotSpotFullName.StartsWith(x.HotSpotFullName))
+                        .WhereIF(!string.IsNullOrEmpty(Attribution), (k, h) => k.Attribution == Attribution).DistinctCount(k => k.Id)
+                }).ToListAsync();
 
-	        return list;
+            return list;
         }
 
 
@@ -220,27 +248,27 @@ namespace Hotline.Api.Controllers
         [HttpGet("treelist/children-hasname")]
         public async Task<IReadOnlyList<KnowledgeHotSpotDto>> GetChildrenHasName([FromQuery] string Name, string? Attribution)
         {
-	        var arr = _hotspotTypeRepository.Queryable()
-		        .WhereIF(!string.IsNullOrEmpty(Name), x => x.HotSpotFullName.Contains(Name)).Select(x => x.Id).ToList().Cast<object>().ToArray();
-	        //.ToTreeAsync(x => x.Children, it => it.ParentId, null);
-	        return await _hotspotTypeRepository.Queryable().Select(x => new KnowledgeHotSpotDto
-			{
-				Id = x.Id.SelectAll(),
-				HasChild = SqlFunc.Subqueryable<Hotspot>().Where(d => d.ParentId == x.Id).NotAny(),
-				KnowledgeNum = SqlFunc.Subqueryable<Knowledge>().LeftJoin<Hotspot>((k, h) => k.HotspotId == h.Id)
-					.Where((k, h) => h.HotSpotFullName.StartsWith(x.HotSpotFullName))
-					.WhereIF(!string.IsNullOrEmpty(Attribution), (k, h) => k.Attribution == Attribution).DistinctCount(k => k.Id)
-			}).ToTreeAsync(x => x.Children, it => it.ParentId, "", arr);
+            var arr = _hotspotTypeRepository.Queryable()
+                .WhereIF(!string.IsNullOrEmpty(Name), x => x.HotSpotFullName.Contains(Name)).Select(x => x.Id).ToList().Cast<object>().ToArray();
+            //.ToTreeAsync(x => x.Children, it => it.ParentId, null);
+            return await _hotspotTypeRepository.Queryable().Select(x => new KnowledgeHotSpotDto
+            {
+                Id = x.Id.SelectAll(),
+                HasChild = SqlFunc.Subqueryable<Hotspot>().Where(d => d.ParentId == x.Id).NotAny(),
+                KnowledgeNum = SqlFunc.Subqueryable<Knowledge>().LeftJoin<Hotspot>((k, h) => k.HotspotId == h.Id)
+                    .Where((k, h) => h.HotSpotFullName.StartsWith(x.HotSpotFullName))
+                    .WhereIF(!string.IsNullOrEmpty(Attribution), (k, h) => k.Attribution == Attribution).DistinctCount(k => k.Id)
+            }).ToTreeAsync(x => x.Children, it => it.ParentId, "", arr);
         }
 
 
-		/// <summary>
-		/// 知识分类- 只获取一级
-		/// </summary>
-		/// <param name="ParentId">不传默认查询第一级</param>
-		/// <param name="IsEnable">不传查询所有,包含已经禁用的</param>
-		/// <returns></returns>
-		[HttpGet("typelist")]
+        /// <summary>
+        /// 知识分类- 只获取一级
+        /// </summary>
+        /// <param name="ParentId">不传默认查询第一级</param>
+        /// <param name="IsEnable">不传查询所有,包含已经禁用的</param>
+        /// <returns></returns>
+        [HttpGet("typelist")]
         public async Task<List<KnowledgeType>> GetTypeList(string? ParentId, bool? IsEnable)
         {
             return await _knowledgeTypeRepository

+ 494 - 66
src/Hotline.Api/Controllers/KnowledgeController.cs

@@ -1,5 +1,4 @@
-using DocumentFormat.OpenXml.Wordprocessing;
-using DotNetCore.CAP;
+using DotNetCore.CAP;
 using Hotline.Api.Filter;
 using Hotline.Application.Bulletin;
 using Hotline.Application.ExportExcel;
@@ -13,14 +12,11 @@ using Hotline.FlowEngine.WorkflowModules;
 using Hotline.FlowEngine.Workflows;
 using Hotline.KnowledgeBase;
 using Hotline.KnowledgeBase.Notifies;
-using Hotline.Permissions;
 using Hotline.Repository.SqlSugar.Extensions;
-using Hotline.Repository.SqlSugar.Knowledge;
 using Hotline.Repository.SqlSugar.Ts;
 using Hotline.Settings;
 using Hotline.Settings.Hotspots;
 using Hotline.Share.Dtos;
-using Hotline.Share.Dtos.File;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.Knowledge;
 using Hotline.Share.Dtos.Order;
@@ -30,24 +26,21 @@ using Hotline.Share.Enums.KnowledgeBase;
 using Hotline.Share.Mq;
 using Hotline.Share.Tools;
 using Hotline.Users;
-using J2N.Text;
 using Mapster;
 using MapsterMapper;
 using MediatR;
 using Microsoft.AspNetCore.Mvc;
-using Microsoft.EntityFrameworkCore.Metadata.Internal;
-using Org.BouncyCastle.Utilities.IO;
 using SqlSugar;
 using System.Text;
-using System.Threading;
 using Hotline.Configurations;
 using Microsoft.Extensions.Options;
 using XF.Domain.Authentications;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
-using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
-using Hotline.Share.Dtos.Schedulings;
+using Hotline.Caching.Interfaces;
+using MiniExcelLibs;
+using Hotline.Pdf;
 
 namespace Hotline.Api.Controllers
 {
@@ -57,6 +50,9 @@ namespace Hotline.Api.Controllers
         #region 注入
         private readonly IExportApplication _exportApplication;
         private readonly IRepository<KnowledgeHotWord> _knowledgeHotWordRepository;
+        private readonly IRepository<KnowledgeApprove> _knowledgeApproRepository;
+        private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+        private readonly IPdfManager _pdfManager;
         private readonly IRepository<KnowledgeWord> _knowledgeWordRepository;
         private readonly IWordHelperService _wordHelperService;
         private readonly BaseDataApplication _baseDataApplication;
@@ -82,7 +78,6 @@ namespace Hotline.Api.Controllers
         private readonly ISystemOrganizeRepository _systemOrganizeRepository;
         private readonly IFileRepository _fileRepository;
         private readonly ICapPublisher _capPublisher;
-        private readonly IRepository<KnowledgeRelationType> _knowledgeRelationTypeRepository;
         private readonly IBulletinApplication _bulletinApplication;
         private readonly IRepository<KnowledgeCollectGroup> _knowledgeCollectGroupRepository;
         private readonly IRepository<KnowledgePv> _knowledgePvepository;
@@ -90,29 +85,28 @@ namespace Hotline.Api.Controllers
 
 
         public KnowledgeController(
-           IKnowledgeRepository knowledgeRepository,
-           ISessionContext sessionContext,
-           IKnowledgeDomainService knowledgeDomainService,
-           IMapper mapper,
-           IKnowApplication knowApplication,
-           IMediator mediator,
-           IWorkflowApplication workflowApplication,
-           IWorkflowDomainService workflowDomainService,
-           IKnowledgeWorkFlowRepository knowledgeWorkFlowRepository,
-           IRepository<User> userRepository,
-           IRepository<KnowledgeType> knowledgeTypeRepository,
-           IRepository<Hotspot> hotspotTypeRepository,
-           IRepositoryTextSearch<KnowledgeTs> repositoryts,
-           IRepository<KnowledgeWord> knowledgeWrodRepository,
-           IRepository<KnowledgeQuestions> knowledgeQuestionsRepository,
-           IRepository<KnowledgeCorrection> knowledgeCorrectionRepository,
-           IRepository<KnowledgeCollect> knowledgeCollectRepository,
-           ISystemDomainService systemDomainService,
-           IRepository<KnowledgeComment> knowledgeCommentRepository,
-           ISystemOrganizeRepository systemOrganizeRepository,
-           IFileRepository fileRepository,
-           ICapPublisher capPublisher,
-           IRepository<KnowledgeRelationType> knowledgeRelationTypeRepository,
+            IKnowledgeRepository knowledgeRepository,
+            ISessionContext sessionContext,
+            IKnowledgeDomainService knowledgeDomainService,
+            IMapper mapper,
+            IKnowApplication knowApplication,
+            IMediator mediator,
+            IWorkflowApplication workflowApplication,
+            IWorkflowDomainService workflowDomainService,
+            IKnowledgeWorkFlowRepository knowledgeWorkFlowRepository,
+            IRepository<User> userRepository,
+            IRepository<KnowledgeType> knowledgeTypeRepository,
+            IRepository<Hotspot> hotspotTypeRepository,
+            IRepositoryTextSearch<KnowledgeTs> repositoryts,
+            IRepository<KnowledgeWord> knowledgeWrodRepository,
+            IRepository<KnowledgeQuestions> knowledgeQuestionsRepository,
+            IRepository<KnowledgeCorrection> knowledgeCorrectionRepository,
+            IRepository<KnowledgeCollect> knowledgeCollectRepository,
+            ISystemDomainService systemDomainService,
+            IRepository<KnowledgeComment> knowledgeCommentRepository,
+            ISystemOrganizeRepository systemOrganizeRepository,
+            IFileRepository fileRepository,
+            ICapPublisher capPublisher,
             IBulletinApplication bulletinApplication,
             IRepository<KnowledgeCollectGroup> knowledgeCollectGroupRepository,
             IExportApplication exportApplication,
@@ -121,8 +115,11 @@ namespace Hotline.Api.Controllers
             IRepository<KnowledgePv> knowledgePvepository,
             IRepository<KnowledgeWord> knowledgeWordRepository,
             IRepository<KnowledgeHotWord> knowledgeWordHotRepository,
-           IOptionsSnapshot<AppConfiguration> appOptions,
-            IRepository<KnowledgeHotWord> knowledgeHotWordRepository)
+            IOptionsSnapshot<AppConfiguration> appOptions,
+            IRepository<KnowledgeHotWord> knowledgeHotWordRepository,
+            IRepository<KnowledgeApprove> knowledgeApproRepository,
+            ISystemSettingCacheManager _systemSettingCacheManager,
+            IPdfManager pdfManager)
         {
             _knowledgeRepository = knowledgeRepository;
             _sessionContext = sessionContext;
@@ -146,7 +143,6 @@ namespace Hotline.Api.Controllers
             _systemOrganizeRepository = systemOrganizeRepository;
             _fileRepository = fileRepository;
             _capPublisher = capPublisher;
-            _knowledgeRelationTypeRepository = knowledgeRelationTypeRepository;
             _bulletinApplication = bulletinApplication;
             _knowledgeCollectGroupRepository = knowledgeCollectGroupRepository;
             _exportApplication = exportApplication;
@@ -155,6 +151,9 @@ namespace Hotline.Api.Controllers
             _knowledgePvepository = knowledgePvepository;
             _knowledgeWordRepository = knowledgeWordRepository;
             _knowledgeHotWordRepository = knowledgeHotWordRepository;
+            _knowledgeApproRepository = knowledgeApproRepository;
+            this._systemSettingCacheManager = _systemSettingCacheManager;
+            _pdfManager = pdfManager;
             _appOptions = appOptions;
         }
 
@@ -190,7 +189,7 @@ namespace Hotline.Api.Controllers
             {
                 List<KnowledgeRelationType> types = _mapper.Map<List<KnowledgeRelationType>>(dto.Data.KnowledgeType);
                 types.ForEach(x => x.KnowledgeId = kn.Id);
-                await _knowledgeRelationTypeRepository.AddRangeAsync(types, HttpContext.RequestAborted);
+                //await _knowledgeRelationTypeRepository.AddRangeAsync(types, HttpContext.RequestAborted);
             }
             if (dto.Workflow != null && !string.IsNullOrEmpty(kn.Id))
             {
@@ -208,6 +207,30 @@ namespace Hotline.Api.Controllers
             return kn.Id;
         }
 
+        /// <summary>
+        /// 知识库-新增(new)
+        /// </summary>
+        [HttpPost]
+        public async Task<string> Add([FromBody] AddKnowledgeDto dto)
+        {
+            var kn = await _knowledgeDomainService.AddKnowledgeAsync(dto, HttpContext.RequestAborted);
+            return kn.Id;
+        }
+
+        /// <summary>
+        /// 知识库-新增并发起审批(new)
+        /// </summary>
+        [HttpPost("add-and-approve")]
+        public async Task<string> AddAndApprove([FromBody] AddKnowledgeDto dto)
+        {
+            var kn = await _knowledgeDomainService.AddKnowledgeAsync(dto, HttpContext.RequestAborted);
+
+            var approve = await _knowledgeDomainService.AddKnowledgeApproveAsync(EKnowledgeApproveType.Add, kn, null,
+                HttpContext.RequestAborted);
+
+            return approve.Id;
+        }
+
         /// <summary>
         /// 知识库-知识下架
         /// </summary>
@@ -235,7 +258,7 @@ namespace Hotline.Api.Controllers
                 }
                 else
                 {
-                    know.Status = EKnowledgeStatus.OffShelfAudit;
+                    know.Status = EKnowledgeStatus.Auditing;
                     await _knowledgeRepository.UpdateAsync(know, HttpContext.RequestAborted);
 
                     var startDto = _mapper.Map<StartWorkflowDto>(dto.Workflow);
@@ -249,6 +272,27 @@ namespace Hotline.Api.Controllers
                 throw UserFriendlyException.SameMessage("知识下架失败");
         }
 
+        /// <summary>
+        /// 知识库-知识下架(new)
+        /// </summary>
+        [HttpPost("offshelf")]
+        [LogFilter("知识下架")]
+        public Task OffShelf([FromBody] OffShelfKnowledgeDto dto) =>
+            _knowledgeDomainService.OffShelfAsync(dto, HttpContext.RequestAborted);
+
+        /// <summary>
+        /// 知识库-知识下架并发起审批(new)
+        /// </summary>
+        [HttpPost("offshelf-and-approve")]
+        [LogFilter("知识下架并发起审批")]
+        public async Task OffShelfAndApprove([FromBody] OffShelfKnowledgeDto dto)
+        {
+            var kn = await _knowledgeDomainService.OffShelfAsync(dto, HttpContext.RequestAborted);
+
+            await _knowledgeDomainService.AddKnowledgeApproveAsync(EKnowledgeApproveType.OffShelf,
+                kn, dto.Opinion, HttpContext.RequestAborted);
+        }
+
         /// <summary>
         /// 知识库-知识上架
         /// </summary>
@@ -331,12 +375,12 @@ namespace Hotline.Api.Controllers
             await _knowledgeRepository.UpdateNullAsync(knowledge, HttpContext.RequestAborted);
             if (dto.Data.KnowledgeType.Any())
             {
-                var anyRelationTypes = await _knowledgeRelationTypeRepository.Queryable().Where(x => x.KnowledgeId == knowledge.Id).ToListAsync();
-                if (anyRelationTypes.Any())
-                    await _knowledgeRelationTypeRepository.RemoveRangeAsync(anyRelationTypes);
-                List<KnowledgeRelationType> types = _mapper.Map<List<KnowledgeRelationType>>(dto.Data.KnowledgeType);
-                types.ForEach(x => x.KnowledgeId = dto.Data.Id);
-                await _knowledgeRelationTypeRepository.AddRangeAsync(types, HttpContext.RequestAborted);
+                //var anyRelationTypes = await _knowledgeRelationTypeRepository.Queryable().Where(x => x.KnowledgeId == knowledge.Id).ToListAsync();
+                //if (anyRelationTypes.Any())
+                //    await _knowledgeRelationTypeRepository.RemoveRangeAsync(anyRelationTypes);
+                //List<KnowledgeRelationType> types = _mapper.Map<List<KnowledgeRelationType>>(dto.Data.KnowledgeType);
+                //types.ForEach(x => x.KnowledgeId = dto.Data.Id);
+                //await _knowledgeRelationTypeRepository.AddRangeAsync(types, HttpContext.RequestAborted);
             }
             if (dto.Workflow != null)
             {
@@ -360,6 +404,62 @@ namespace Hotline.Api.Controllers
 
         }
 
+        /// <summary>
+        /// 知识库-修改(new)
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPut]
+        [LogFilter("知识修改")]
+        public Task Update([FromBody] UpdateKnowledgeDto dto) =>
+            _knowledgeDomainService.UpdateKnowledgeAsync(dto, HttpContext.RequestAborted);
+
+        /// <summary>
+        /// 知识库-修改(new)
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPut("update-and-approve")]
+        [LogFilter("知识修改")]
+        public async Task UpdateAndApprove([FromBody] UpdateKnowledgeAndApproveDto dto)
+        {
+            var kn = await _knowledgeDomainService.UpdateKnowledgeAsync(dto, HttpContext.RequestAborted);
+
+            var exists = await _knowledgeApproRepository.Queryable()
+                .AnyAsync(d => d.KnowledgeId == dto.Id && d.KnowledgeApproveStatus == EKnowledgeApproveStatus.Unhandle,
+                    HttpContext.RequestAborted);
+            if (exists)
+                throw new UserFriendlyException($"该知识存在待审批申请, knId: {dto.Id}", "该知识存在待审批申请, 请先审批");
+
+            if (kn.Status == EKnowledgeStatus.Drafts)
+            {
+                await _knowledgeDomainService.AddKnowledgeApproveAsync(EKnowledgeApproveType.Add, kn, null,
+                    HttpContext.RequestAborted);
+            }
+            else
+            {
+                await _knowledgeDomainService.AddKnowledgeApproveAsync(EKnowledgeApproveType.Update, kn, dto.Opinion,
+                    HttpContext.RequestAborted);
+            }
+        }
+
+        /// <summary>
+        /// 批量更新公开状态(new)
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost("publish-batch")]
+        public async Task PublishBatch([FromBody] PublishBatchRequest request)
+        {
+            var kns = request.KnowledgeIds.Select(d => new Knowledge
+            {
+                Id = d,
+                IsPublic = request.IsPublic
+            }).ToList();
+            await _knowledgeRepository.Updateable(kns)
+                .UpdateColumns(d => d.IsPublic)
+                .ExecuteCommandAsync(HttpContext.RequestAborted);
+        }
+
         /// <summary>
         /// 批量审核
         /// </summary>
@@ -462,6 +562,36 @@ namespace Hotline.Api.Controllers
             }
         }
 
+        /// <summary>
+        /// 删除知识(new)
+        /// </summary>
+        [HttpDelete("remove")]
+        [LogFilter("删除知识")]
+        public async Task Remove([FromBody] DeleteKnowledgeDto dto)
+        {
+            var knowledge = await _knowledgeRepository.GetAsync(dto.Id, HttpContext.RequestAborted);
+            if (knowledge == null)
+                throw UserFriendlyException.SameMessage("无效知识库数据");
+            if (knowledge.Status == EKnowledgeStatus.OnShelf || knowledge.Status == EKnowledgeStatus.Auditing)
+                throw UserFriendlyException.SameMessage("知识库数据不可删除");
+
+            if (knowledge.Status == EKnowledgeStatus.Drafts || knowledge.Status == EKnowledgeStatus.Revert)
+            {
+                await _knowledgeRepository.RemoveAsync(knowledge, false, HttpContext.RequestAborted);
+            }
+            else
+            {
+                var exists = await _knowledgeApproRepository.Queryable()
+                    .AnyAsync(d => d.KnowledgeId == dto.Id && d.KnowledgeApproveStatus == EKnowledgeApproveStatus.Unhandle,
+                        HttpContext.RequestAborted);
+                if (exists)
+                    throw new UserFriendlyException($"该知识存在待审批申请, knId: {dto.Id}", "该知识存在待审批申请, 请先审批");
+
+                await _knowledgeDomainService.AddKnowledgeApproveAsync(EKnowledgeApproveType.Delete, knowledge, dto.Opinion,
+                    HttpContext.RequestAborted);
+            }
+        }
+
         /// <summary>
         /// 增加搜索量
         /// </summary>
@@ -487,17 +617,15 @@ namespace Hotline.Api.Controllers
         [HttpGet("search_num/list")]
         public async Task<PagedDto<KnowledgeDto>> SearchNumList([FromQuery] KnowledgeCollectListDto dto)
         {
-
-            var query = _knowledgeRepository.Queryable(false, true, false)
+            var query = _knowledgeRepository.Queryable()
                 .Where(x => x.Status == EKnowledgeStatus.OnShelf)
-                .Where(m => m.IsDeleted == false)
                 .Where(x => (x.ExpiredTime != null && x.ExpiredTime >= DateTime.Now) || x.ExpiredTime == null)
                 .OrderByDescending(x => x.SearchNum);
 
             if (_sessionContext.OrgIsCenter == false)
             {
-                query = query.Where(m => m.KnowledgeType.Any(a => a.IsDeleted == false && a.KnowledgeType.KnowledgeTypeOrgs
-                .Any(k => k.IsDeleted == false && k.OrgId.StartsWith(_sessionContext.OrgId))));
+                query = query.Where(m => m.KnowledgeTypes.Any(a => a.IsDeleted == false && a.Orgs
+                .Any(k => k.Id.StartsWith(_sessionContext.RequiredOrgId))));
                 query = query.Where(m => m.Attribution == "部门知识库");
             }
             var (total, items) = await query.ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
@@ -509,6 +637,7 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <param name="Id"></param>
         /// <returns></returns>
+        [Obsolete("请调用info/{Id}")]
         [HttpGet("updateinfo/{Id}")]
         public async Task<KnowledgeInfoDto> KnowledgeUpdateInfo(string Id)
         {
@@ -544,15 +673,18 @@ namespace Hotline.Api.Controllers
         /// <param name="IsAddPv">默认不增加,false不增加,true增加浏览量</param>
         /// <returns></returns>
         [HttpGet("info/{Id}")]
-        public async Task<KnowledgeInfoDto> KnowledgeInfo(string Id, bool? IsAddPv)
+        public async Task<KnowledgeDto> KnowledgeInfo(string Id, bool? IsAddPv)
         {
-            var knowledge = await _knowledgeDomainService.KnowledgeInfo(Id, HttpContext.RequestAborted);
+            var knowledge = await _knowledgeRepository.Queryable()
+                .Includes(x => x.SourceOrganize)
+                .Includes(x => x.KnowledgeTypes)
+                .Includes(x => x.HotspotType)
+                .FirstAsync(p => p.Id == Id, HttpContext.RequestAborted);
             if (knowledge is null)
                 throw UserFriendlyException.SameMessage("知识查询失败!");
-            if (knowledge.Workflow != null)
-                knowledge.IsCanHandle = knowledge.Workflow.IsCanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles);
+
             //转化
-            var knowledgeShowInfoDto = _mapper.Map<KnowledgeInfoDto>(knowledge);
+            var knowledgeShowInfoDto = _mapper.Map<KnowledgeDto>(knowledge);
 
             if (knowledgeShowInfoDto != null && !string.IsNullOrEmpty(knowledgeShowInfoDto.Content))
                 knowledgeShowInfoDto.Content = _bulletinApplication.GetSiteUrls(knowledgeShowInfoDto.Content);
@@ -563,9 +695,9 @@ namespace Hotline.Api.Controllers
             //	knowledgeShowInfoDto.KnowledgeTypeName = type.SpliceName;
             //	knowledgeShowInfoDto.KnowledgeType = _mapper.Map<KnowledgeTypeDto>(type);
             //}
-            var hot = await _hotspotTypeRepository.GetAsync(knowledge.HotspotId, HttpContext.RequestAborted);
-            if (hot != null)
-                knowledgeShowInfoDto.HotspotName = hot.HotSpotFullName;
+            //var hot = await _hotspotTypeRepository.GetAsync(knowledge.HotspotId, HttpContext.RequestAborted);
+            //if (hot != null)
+            //    knowledgeShowInfoDto.HotspotName = hot.HotSpotFullName;
             //收藏
             var collect = await _knowledgeCollectRepository.GetAsync(x => x.KnowledgeId == Id && x.CreatorId == _sessionContext.UserId);
             if (collect != null)
@@ -610,7 +742,7 @@ namespace Hotline.Api.Controllers
             }
 
             var info = await _knowledgeRepository.GetAsync(dto.Ids[0]) ?? throw UserFriendlyException.SameMessage("知识不存在");
-            return info.Content.HtmlToStream(dto.FileType).GetFileStreamResult(dto.FileType, info.Title, false);
+            return info.Content.HtmlToStream(dto.FileType, info.Title, _pdfManager).GetFileStreamResult(dto.FileType, info.Title, false);
         }
 
         /// <summary>
@@ -684,6 +816,7 @@ namespace Hotline.Api.Controllers
                 new KeyValuePair<int, string>(8, "草稿"),
                 new KeyValuePair<int, string>(-1, "全部")
             };
+            //var tabNames = EnumExts.GetDescriptions<EKnowledgeStatusRequest>();
 
             var tabNewDraftsNames = new List<KeyValuePair<int, string>>
             {
@@ -691,20 +824,22 @@ namespace Hotline.Api.Controllers
                 new KeyValuePair<int, string>(0, "待提交"),
                 new KeyValuePair<int, string>(5, "审核不通过"),
             };
+            //var tabNewDraftsNames = EnumExts.GetDescriptions<EKnowledgeDraftTypeRequest>();
+
 
             var tabAuditingNames = new List<KeyValuePair<string, string>>
             {
                 new KeyValuePair<string, string>("", "全部"),
-                new KeyValuePair<string, string>("KnowledgeAdd", "新增审核"),
-                new KeyValuePair<string, string>("KnowledgeUpdate", "修改审核"),
-                new KeyValuePair<string, string>("KnowledgeDelete", "删除审核"),
-                new KeyValuePair<string, string>("KnowledgeOffshelf", "下架审核"),
+                new KeyValuePair<string, string>("add", "新增审核"),
+                new KeyValuePair<string, string>("update", "修改审核"),
+                new KeyValuePair<string, string>("delete", "删除审核"),
+                new KeyValuePair<string, string>("offshelf", "下架审核"),
             };
 
             //return _baseDataApplication
             //    .FileType(EFileType.excel | EFileType.pdf)
             //    .Build();
-            var ignoreFileType = EFileType.excel | EFileType.pdf;
+            var ignoreFileType = EFileType.excel;//| EFileType.pdf;
             var items = EnumExts.GetDescriptions<EFileType>();
             var filteredDictionary = items
                  .Where(kvp => (ignoreFileType & (EFileType)kvp.Key) == 0)
@@ -821,6 +956,124 @@ namespace Hotline.Api.Controllers
             return any;
         }
 
+        /// <summary>
+        /// 下载知识导入模板(new)
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet("dl-template")]
+        public IActionResult DownLoadKnowledgeTemplate()
+        {
+            return _exportApplication.ExportData(new List<KnowledgeImportTemplate>
+            {
+                new ()
+            }, "知识模板.xlsx");
+        }
+
+        /// <summary>
+        /// 导入知识(new)
+        /// </summary>
+        /// <param name="file"></param>
+        /// <returns></returns>
+        [HttpPost("import-knowledge")]
+        public async Task<string> ImportKnowledge(IFormFile file)
+        {
+            using var stream = new MemoryStream();
+            await file.CopyToAsync(stream);
+            var items = stream.Query<KnowledgeImportTemplate>().ToList();
+            if (items == null || !items.Any()) throw new UserFriendlyException("未读取到数据");
+
+            if (items.Any(d => d.Attribution != "中心知识库" && d.Attribution != "部门知识库"))
+                throw UserFriendlyException.SameMessage("请正确输入知识归属");
+            if (items.Any(d => !string.IsNullOrEmpty(d.IsPublic) && d.IsPublic != "是" && d.IsPublic != "否"))
+                throw UserFriendlyException.SameMessage("请正确输入是否公开");
+            if (items.Any(d => string.IsNullOrEmpty(d.Title)))
+                throw UserFriendlyException.SameMessage("请输入标题");
+            if (items.Any(d => string.IsNullOrEmpty(d.Content)))
+                throw UserFriendlyException.SameMessage("请输入内容");
+
+            var titles = items.Select(d => d.Title).ToList();
+            var exists = await _knowledgeRepository.Queryable()
+                .Where(d => titles.Contains(d.Title))
+                .ToListAsync(HttpContext.RequestAborted);
+            if (exists.Any())
+                throw UserFriendlyException.SameMessage($"重复标题:{string.Join(',', exists.Select(d => d.Title))}");
+
+            var inputLevelOneTypes = items.Select(d => d.KnowledgeTypeLevelOne)
+                .Distinct().ToList();
+            var levelOneTypes = await _knowledgeTypeRepository.Queryable()
+                .Where(d => d.IsEnable && string.IsNullOrEmpty(d.ParentId))
+                .ToListAsync(HttpContext.RequestAborted);
+            var existLevelOneNames = levelOneTypes.Select(d => d.Name);
+            var notExists = inputLevelOneTypes.Except(existLevelOneNames).ToList();
+            if (notExists.Any())
+                throw UserFriendlyException.SameMessage($"不存在的一级分类名称:{string.Join(',', notExists)}");
+
+            var sb = new StringBuilder();
+            var kns = new List<Knowledge>();
+            foreach (var item in items)
+            {
+                var kn = _mapper.Map<Knowledge>(item);
+                kn.SourceOrganizeId = _sessionContext.RequiredOrgId;
+                var knType = levelOneTypes.FirstOrDefault(d => d.Name == item.KnowledgeTypeLevelOne);
+                if (knType is null)
+                {
+                    sb.Append($"错误一级分类:{item.KnowledgeTypeLevelOne}");
+                    continue;
+                }
+                kn.KnowledgeTypes = new List<KnowledgeType> { knType };
+                kn.IsPublic = item.IsPublic == "是";
+                kns.Add(kn);
+            }
+
+            await _knowledgeRepository.AddNav(kns)
+                .Include(d => d.KnowledgeTypes)
+                .ExecuteCommandAsync();
+
+            var err = sb.ToString();
+            return string.IsNullOrEmpty(err)
+                ? $"导入{items.Count}条,成功{kns.Count}条"
+                : $"导入{items.Count}条,成功{kns.Count}条,{err}";
+        }
+
+        /// <summary>
+        /// 导出知识(new)
+        /// </summary>
+        [HttpGet("export-knowledge")]
+        public async Task<IActionResult> ExportKnowledge([FromQuery] ExportKnowledgeRequest request)
+        {
+            if (string.IsNullOrEmpty(request.Id))
+                throw new UserFriendlyException("无效知识编号");
+            var kn = await _knowledgeRepository.GetAsync(request.Id, HttpContext.RequestAborted);
+            if (kn is null)
+                throw new UserFriendlyException("无效知识编号");
+            var content = kn.Content
+                  .Replace("&nbsp;", " ")
+                  .Replace("&amp;", "&")
+                  .Replace("&quot;", "\"")
+                  .Replace("&gt;", ">")
+                  .Replace("&lt;", "<")
+                  ;
+
+
+            switch (request.ExportType)
+            {
+                case "pdf":
+                    var fileName = $"{kn.Title}.pdf";
+                    var stream = new MemoryStream();
+                    _pdfManager.GeneratePdf(kn.Title, content, stream);
+                    stream.Seek(0, SeekOrigin.Begin);
+                    return new FileStreamResult(stream, "application/pdf")
+                    {
+                        FileDownloadName = fileName ?? DateTime.Now.ToString("yyyyMMddhhmmss") + ".pdf"
+                    };
+
+                    break;
+            }
+
+
+            return null;
+        }
+
         #endregion
 
         #region 我的知识删除列表
@@ -951,7 +1204,7 @@ namespace Hotline.Api.Controllers
         //[HttpPost("{id}/add-startflow")]
         //public async Task AddStartFlow(string id, [FromBody] StartWorkflowDto dto)
         //{
-        //	await StartFlow(id, WorkflowModuleConsts.KnowledgeAdd, EKnowledgeApplyType.Add, dto);
+        //	await StartFlow(id, WorkflowModuleConsts.AddKnowledgeAsync, EKnowledgeApplyType.Add, dto);
         //}
 
         ///// <summary>
@@ -1635,5 +1888,180 @@ namespace Hotline.Api.Controllers
         }
 
         #endregion
+
+        #region 知识审批
+
+        /// <summary>
+        /// 查询待审批列表(new)
+        /// </summary>
+        [HttpGet("approve-paged")]
+        public async Task<PagedDto<KnowledgeApproveDto>> QueryKnowledgeApprovePaged([FromQuery] QueryKnowledgeApprovePagedRequest request)
+        {
+            var query = _knowledgeApproRepository.Queryable()
+                .Includes(d => d.Knowledge, s => s.KnowledgeTypes)
+                .WhereIF(request.KnowledgeApproveType.HasValue, d => d.KnowledgeApproveType == request.KnowledgeApproveType)
+                .WhereIF(request.IsPublic.HasValue, d => d.Knowledge.IsPublic == request.IsPublic)
+                .WhereIF(!string.IsNullOrEmpty(request.Keyword), d => d.CreatorName == request.Keyword
+                || d.CreatorOrgName == request.Keyword
+                || d.Knowledge.Title == request.Keyword
+                || d.Knowledge.SourceOrganize.Name == request.Keyword)
+                ;
+            if (request.HasApproved)
+            {
+                //已审批
+                query//.Includes(d => d.Approver)
+                    .Where(d =>
+                    d.ApproverId == _sessionContext.RequiredUserId
+                    && d.KnowledgeApproveStatus != EKnowledgeApproveStatus.Unhandle);
+            }
+            else
+            {
+                //待审批
+                query.Where(d => d.KnowledgeApproveStatus == EKnowledgeApproveStatus.Unhandle);
+            }
+
+            var (total, items) = await query.ToPagedListAsync(request, HttpContext.RequestAborted);
+
+            return new PagedDto<KnowledgeApproveDto>(total, _mapper.Map<IReadOnlyList<KnowledgeApproveDto>>(items));
+        }
+
+        /// <summary>
+        /// 查询审核历史(new)
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet("approve-history/{knowledgeId}")]
+        public async Task<IReadOnlyList<KnowledgeApproveDto>> QueryKnowledgeApproveByKnId(string knowledgeId)
+        {
+            var approves = await _knowledgeApproRepository.Queryable()
+                .Includes(d => d.Approver)
+                .Where(d => d.KnowledgeId == knowledgeId)
+                .OrderByDescending(d => d.CreationTime)
+                .ToListAsync(HttpContext.RequestAborted);
+
+            return _mapper.Map<IReadOnlyList<KnowledgeApproveDto>>(approves);
+        }
+
+        /// <summary>
+        /// 批量审核(new)
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [HttpPost("approve-batch")]
+        public async Task ApproveBatch([FromBody] ApproveBatchRequest request)
+        {
+            #region approve
+
+            var approves = await _knowledgeApproRepository.Queryable()
+                .Includes(d => d.Knowledge, x => x.KnowledgeTypes)
+                .Where(d => request.ApproveIds.Contains(d.Id) && d.KnowledgeApproveStatus == EKnowledgeApproveStatus.Unhandle)
+                .ToListAsync(HttpContext.RequestAborted);
+
+            foreach (var approve in approves)
+            {
+                var kn = approve.Knowledge;
+                switch (approve.KnowledgeApproveType)
+                {
+                    case EKnowledgeApproveType.Add:
+                    case EKnowledgeApproveType.Update:
+                        // t: 上架 f: 驳回
+                        if (request.IsSuccess)
+                        {
+                            kn.OnShelf();
+                        }
+                        else
+                        {
+                            kn.Status = EKnowledgeStatus.Revert;
+                        }
+                        break;
+                    case EKnowledgeApproveType.Delete:
+                        // t: 先下架,再删除 f: 驳回
+                        if (request.IsSuccess)
+                        {
+                            kn.OffShelf();
+                            kn.SoftDelete();
+                        }
+                        else
+                        {
+                            kn.Status = EKnowledgeStatus.Revert;
+                        }
+                        break;
+                    case EKnowledgeApproveType.OffShelf:
+                        // t: 下架 f: 上架
+                        if (request.IsSuccess)
+                        {
+                            kn.OffShelf();
+                        }
+                        else
+                        {
+                            kn.Status = EKnowledgeStatus.OnShelf;
+                        }
+                        break;
+                    default:
+                        throw new ArgumentOutOfRangeException();
+                }
+                approve.Approve(_sessionContext.RequiredUserId, request.Opinion,
+                    request.IsSuccess
+                        ? EKnowledgeApproveStatus.Successed
+                        : EKnowledgeApproveStatus.Failed);
+            }
+
+            //var kns = approves.Select(d => d.Knowledge).ToList();
+            //await _knowledgeRepository.UpdateRangeAsync(kns, HttpContext.RequestAborted);
+
+            await _knowledgeApproRepository.UpdateNav(approves)
+                .Include(d => d.Knowledge)
+                .ExecuteCommandAsync();
+
+            #endregion
+
+            #region push to ds
+
+            var pushApproves = approves.Where(d => d.KnowledgeApproveType != EKnowledgeApproveType.OffShelf)
+                  .ToList();
+            if (!pushApproves.Any()) return;
+
+            //高效办成一件事配置
+            var cancelPublishOrderEnabled = _systemSettingCacheManager.GetSetting(SettingConstants.EfficientlyAccomplish)?.SettingValue[0];
+            foreach (var pushApprove in pushApproves)
+            {
+                var pushKnowledge = _mapper.Map<KnowledgeSendDto>(pushApprove.Knowledge);
+                pushKnowledge.CategoryCode = "01";
+                pushKnowledge.CategoryName = "公共服务";
+
+                if (!string.IsNullOrEmpty(cancelPublishOrderEnabled))
+                {
+                    var isEfficientlyAccomplish = pushApprove.Knowledge.KnowledgeTypes.Any(
+                            d => d.SpliceName.StartsWith(cancelPublishOrderEnabled));
+                    //是高效,处理默认值
+                    if (isEfficientlyAccomplish)
+                    {
+                        pushKnowledge.CategoryCode = "25";
+                        pushKnowledge.CategoryName = "高效办成一件事";
+                    }
+                }
+
+                switch (pushApprove.KnowledgeApproveType)
+                {
+                    case EKnowledgeApproveType.Add:
+                        await _capPublisher.PublishAsync(EventNames.HotlineKnowledgeAdd, pushKnowledge, cancellationToken: HttpContext.RequestAborted);
+                        break;
+                    case EKnowledgeApproveType.Update:
+                        await _capPublisher.PublishAsync(EventNames.HotlineKnowledgeUpdate, pushKnowledge, cancellationToken: HttpContext.RequestAborted);
+                        break;
+                    case EKnowledgeApproveType.Delete:
+                        await _capPublisher.PublishAsync(EventNames.HotlineKnowledgeRemove, pushKnowledge, cancellationToken: HttpContext.RequestAborted);
+                        break;
+                    case EKnowledgeApproveType.OffShelf:
+                    default:
+                        throw new ArgumentOutOfRangeException();
+                }
+            }
+
+            #endregion
+
+            //todo return $"总共: {dto.KnowledgeIds.Length}, 成功: {success}, 失败: {fail}, 失败原因: {result.ToString()}";
+        }
+
+        #endregion
     }
 }

+ 117 - 109
src/Hotline.Api/Controllers/OrderController.cs

@@ -6,6 +6,7 @@ using Hotline.Application.ExportExcel;
 using Hotline.Application.FlowEngine;
 using Hotline.Application.Orders;
 using Hotline.Application.Quality;
+using Hotline.Application.Snapshot;
 using Hotline.Application.Systems;
 using Hotline.Authentications;
 using Hotline.Caching.Interfaces;
@@ -40,7 +41,9 @@ using Hotline.Share.Dtos.Order.Detail;
 using Hotline.Share.Dtos.Order.Handle;
 using Hotline.Share.Dtos.Order.Migration;
 using Hotline.Share.Dtos.Order.Publish;
+using Hotline.Share.Dtos.Org;
 using Hotline.Share.Dtos.Settings;
+using Hotline.Share.Dtos.Snapshot;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.Order;
@@ -49,6 +52,8 @@ using Hotline.Share.Enums.Settings;
 using Hotline.Share.Mq;
 using Hotline.Share.Requests;
 using Hotline.Share.Tools;
+using Hotline.Snapshot;
+using Hotline.Snapshot.Interfaces;
 using Hotline.Tools;
 using Hotline.Users;
 using Hotline.YbEnterprise.Sdk;
@@ -68,20 +73,6 @@ using XF.Domain.Entities;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
-using Hotline.Application.Contracts.Validators.FlowEngine;
-using Hotline.Authentications;
-using Hotline.Share.Dtos.CallCenter;
-using Hotline.Share.Mq;
-using Hotline.CallCenter.Calls;
-using Hotline.FlowEngine.Notifications;
-using Hotline.Share.Dtos.Order.Detail;
-using Hotline.Share.Dtos.File;
-using Hotline.Share.Dtos.Org;
-using Hotline.Snapshot.Interfaces;
-using Hotline.Snapshot.Notifications;
-using Hotline.Snapshot;
-using Hotline.Application.Snapshot;
-using Hotline.Share.Dtos.Snapshot;
 using OrderDto = Hotline.Share.Dtos.Order.OrderDto;
 
 namespace Hotline.Api.Controllers;
@@ -380,6 +371,7 @@ public class OrderController : BaseController
     public async Task<PagedDto<PublishDto>> PublishOrderList([FromQuery] QueryOrderPublishDto dto)
     {
         var (total, items) = await _orderRepository.Queryable()
+            .Includes(d => d.OrderTags)
             //.Includes(d => d.OrderPublish)
             .Where(x => x.Status == EOrderStatus.Filed)
             .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Title.StartsWith(dto.Keyword!))
@@ -388,7 +380,8 @@ public class OrderController : BaseController
             //.WhereIF(dto.PubState == EPubState.Pub, d => d.Status >= EOrderStatus.Published)
             //.WhereIF(dto.PubState == EPubState.NoPub, d => d.Status == EOrderStatus.Filed)
             .WhereIF(!string.IsNullOrEmpty(dto.Channel), d => d.SourceChannelCode == dto.Channel)
-            .WhereIF(!string.IsNullOrEmpty(dto.OrderTag), d => d.OrderTagCode == dto.OrderTag!) //工单标签
+            //.WhereIF(!string.IsNullOrEmpty(dto.OrderTag), d => d.OrderTagCode == dto.OrderTag!) //工单标签
+            .WhereIF(!string.IsNullOrEmpty(dto.OrderTag), d => d.OrderTags.Any(ot => ot.DicDataValue == dto.OrderTag)) //工单标签
             .WhereIF(!string.IsNullOrEmpty(dto.CenterToOrgHandlerName), d => d.CenterToOrgHandlerName == dto.CenterToOrgHandlerName!) //派单人
             .WhereIF(!string.IsNullOrEmpty(dto.NameOrNo), d => d.AcceptorName == dto.NameOrNo! || d.AcceptorStaffNo == dto.NameOrNo!) //受理人/坐席
             .WhereIF(!string.IsNullOrEmpty(dto.ActualHandleOrgName), d => d.ActualHandleOrgName.Contains(dto.ActualHandleOrgName)) //接办部门(综合查询模糊)
@@ -972,8 +965,8 @@ public class OrderController : BaseController
         var users = await _userRepository.Queryable()
             .Includes(d => d.Organization)
             .Includes(d => d.Roles)
-            .WhereIF(!_appOptions.Value.IsZiGong, d => d.Roles.Any(x => roles.Contains(x.Name)))
-            .WhereIF(_appOptions.Value.IsZiGong, d => d.OrgId == OrgSeedData.CenterId)
+            .WhereIF(!_appOptions.Value.IsZiGong && !_appOptions.Value.IsLuZhou, d => d.Roles.Any(x => roles.Contains(x.Name)))
+            .WhereIF(_appOptions.Value.IsZiGong || _appOptions.Value.IsLuZhou, d => d.OrgId == OrgSeedData.CenterId)
             .ToListAsync(HttpContext.RequestAborted);
         return users.Select(d => new OrderMigrationHandler
         {
@@ -1172,10 +1165,10 @@ public class OrderController : BaseController
         //    x => x.OrderId == orderVisit.OrderId && x.AgainState == EAgainState.DoAgain, HttpContext.RequestAborted);
         var voiceEvaluate = EnumExts.GetDescriptions<EVoiceEvaluate>();
         var seatEvaluate = EnumExts.GetDescriptions<ESeatEvaluate>();
-        if (_appOptions.Value.IsZiGong==true)
+        if (_appOptions.Value.IsZiGong == true)
         {
             voiceEvaluate = EnumExtensions.GetEnumKeyValueList<EVoiceEvaluate>();
-            seatEvaluate=EnumExtensions.GetEnumKeyValueList<ESeatEvaluate>();
+            seatEvaluate = EnumExtensions.GetEnumKeyValueList<ESeatEvaluate>();
         }
         var visitSatisfaction = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.VisitSatisfaction);
         var visitManner = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.VisitManner);
@@ -1989,7 +1982,7 @@ public class OrderController : BaseController
             if (_appOptions.Value.IsZiGong)
             {
                 count = await _orderDelayRepository.CountAsync(x =>
-                    x.OrderId == delaydto.OrderId &&  x.ApplyOrgCode == _sessionContext.RequiredOrgId &&
+                    x.OrderId == delaydto.OrderId && x.ApplyOrgCode == _sessionContext.RequiredOrgId &&
                     (x.DelayState == EDelayState.Pass || x.DelayState == EDelayState.NoPass));
             }
 
@@ -2069,19 +2062,20 @@ public class OrderController : BaseController
         }
     }
 
-	/// <summary>
-	/// 批量审批延期
-	/// </summary>
-	[HttpPost("delay/batch_audit")]
-	[LogFilter("批量审批延期")]
-	public async Task<string> BatchAuditDelay([FromBody] BatchDelayNextFlowDto dto) {
-		var result = new StringBuilder();
-		var fail = 0;
-		var success = 0;
-		foreach (var item in dto.DelayId)
-		{
-			try
-			{
+    /// <summary>
+    /// 批量审批延期
+    /// </summary>
+    [HttpPost("delay/batch_audit")]
+    [LogFilter("批量审批延期")]
+    public async Task<string> BatchAuditDelay([FromBody] BatchDelayNextFlowDto dto)
+    {
+        var result = new StringBuilder();
+        var fail = 0;
+        var success = 0;
+        foreach (var item in dto.DelayId)
+        {
+            try
+            {
                 var workflow = dto.NextWorkflow;
 				if (workflow.NextHandlers.Any() && workflow.NextHandlers.Count() == 1)
 				{
@@ -2097,77 +2091,79 @@ public class OrderController : BaseController
 				var currentStep =
 					workflowEntuty.Steps.FirstOrDefault(d => d.Status == EWorkflowStepStatus.WaitForAccept || d.Status == EWorkflowStepStatus.WaitForHandle);
 
-				NextStepsWithOpinionDto<NextStepOption> next = null;
+                NextStepsWithOpinionDto<NextStepOption> next = null;
 
-				try
-				{
-					next = await _workflowApplication.GetNextStepsAsync(delay.WorkflowId, HttpContext.RequestAborted);
-				}
-				catch (UserFriendlyException e)
-				{
-					if (e.Message.Contains("未找到对应节点"))
-					{
-						result.Append("无权审核:" + delay.No);
-						fail++;
-					}
-					else
-					{
-						throw;
-					}
-				}
-				if (next == null) continue;
+                try
+                {
+                    next = await _workflowApplication.GetNextStepsAsync(delay.WorkflowId, HttpContext.RequestAborted);
+                }
+                catch (UserFriendlyException e)
+                {
+                    if (e.Message.Contains("未找到对应节点"))
+                    {
+                        result.Append("无权审核:" + delay.No);
+                        fail++;
+                    }
+                    else
+                    {
+                        throw;
+                    }
+                }
+                if (next == null) continue;
 
-				if (!delay.Order.IsProvince)
-				{
-					if (next.Steps.Any(x => x.Value == "省审批"))
-					{
-						next.Steps.Remove(next.Steps.First(x => x.Value == "省审批"));
-					}
-				}
+                if (!delay.Order.IsProvince)
+                {
+                    if (next.Steps.Any(x => x.Value == "省审批"))
+                    {
+                        next.Steps.Remove(next.Steps.First(x => x.Value == "省审批"));
+                    }
+                }
 
-				if (!_sessionContext.OrgIsCenter && currentStep.Name != "中心初审")
-				{
-					if (next.Steps.Any(x => x.Value == "中心终审"))
-					{
-						next.Steps.Remove(next.Steps.First(x => x.Value == "中心终审"));
-					}
-				}
+                if (!_sessionContext.OrgIsCenter && currentStep.Name != "中心初审")
+                {
+                    if (next.Steps.Any(x => x.Value == "中心终审"))
+                    {
+                        next.Steps.Remove(next.Steps.First(x => x.Value == "中心终审"));
+                    }
+                }
 
-				var isBatch = next.Steps.Where(x => x.Value == workflow.NextStepName).Any();
+                var isBatch = next.Steps.Where(x => x.Value == workflow.NextStepName).Any();
                 if (isBatch)
                 {
-					var step = next.Steps.Where(x => x.Value == workflow.NextStepName).FirstOrDefault();
-					workflow.NextStepCode = step.Key;
-					workflow.NextStepName = step.Value;
-				}
-                else {
-					result.Append("无权审核:" + delay.No);
-					fail++;
+                    var step = next.Steps.Where(x => x.Value == workflow.NextStepName).FirstOrDefault();
+                    workflow.NextStepCode = step.Key;
+                    workflow.NextStepName = step.Value;
+                }
+                else
+                {
+                    result.Append("无权审核:" + delay.No);
+                    fail++;
                     continue;
-				}
+                }
 
-				workflow.StepId = next.StepId;
+                workflow.StepId = next.StepId;
                 workflow.ReviewResult = dto.IsPass ? EReviewResult.Approval : EReviewResult.Failed;
 
-				if (workflow.ReviewResult == EReviewResult.Approval) {
-					await _workflowDomainService.NextAsync(_sessionContext, workflow,cancellationToken: HttpContext.RequestAborted);
-				}
-				else
-				{
-					var reject = workflow.Adapt<RejectDto>();
-					await _workflowApplication.RejectAsync(reject, HttpContext.RequestAborted);
-				}
-				success++;
-			}
-			catch (UserFriendlyException e)
-			{
-				result.Append(e.Message);
-				fail++;
-			}
-		}
-		return $"总共: {dto.DelayId.Length}, 成功: {success}, 失败: {fail}, 失败原因: {result.ToString()}";
+                if (workflow.ReviewResult == EReviewResult.Approval)
+                {
+                    await _workflowDomainService.NextAsync(_sessionContext, workflow, cancellationToken: HttpContext.RequestAborted);
+                }
+                else
+                {
+                    var reject = workflow.Adapt<RejectDto>();
+                    await _workflowApplication.RejectAsync(reject, HttpContext.RequestAborted);
+                }
+                success++;
+            }
+            catch (UserFriendlyException e)
+            {
+                result.Append(e.Message);
+                fail++;
+            }
+        }
+        return $"总共: {dto.DelayId.Length}, 成功: {success}, 失败: {fail}, 失败原因: {result.ToString()}";
 
-	}
+    }
 
     /// <summary>
     ///  延期查询流程办理下一步可选节点
@@ -3410,6 +3406,7 @@ public class OrderController : BaseController
             .Includes(d => d.OrderPublish)
             .Includes(d => d.OrderPushTypes)
             .Includes(d => d.OrderComplements)
+            .Includes(d => d.OrderTags)
             //.Includes(d => d.OrderScreens)
             .Includes(d => d.OrderVisits.Where(x => x.VisitState == EVisitState.Visited).ToList(), x => x.OrderVisitDetails)
             .Includes(d => d.OrderVisits.Where(x => x.VisitState == EVisitState.Visited).ToList(), x => x.Employee)
@@ -3689,9 +3686,6 @@ public class OrderController : BaseController
                     dto.IndustryId = snapshot.IndustryId;
                 });
         }
-        dto.IsReTransact = await _orderSpecialRepository.Queryable()
-            .Where(x => x.OrderId == dto.Id && x.SpecialType == ESpecialType.ReTransact).AnyAsync();
-
         return _sessionContext.OrgIsCenter ? dto : dto.DataMask();
     }
 
@@ -3745,6 +3739,16 @@ public class OrderController : BaseController
             order.PushType = string.Join(",", pushTypes);
         }
 
+        if (dto.Tags != null && dto.Tags.Any())
+        {
+            var orderTags = new List<SystemDicData>();
+            foreach (var item in dto.Tags)
+            {
+                orderTags.Add(new SystemDicData() { Id = item });
+            }
+            order.OrderTags = orderTags;
+        }
+
         await _orderDomainService.AddAsync(order, true, HttpContext.RequestAborted);
 
         if (_systemSettingCacheManager.Snapshot && dto.IndustryId.NotNullOrEmpty() && dto.IndustryName.NotNullOrEmpty())
@@ -3777,6 +3781,8 @@ public class OrderController : BaseController
             await _repeatableEventDetailRepository.AddRangeAsync(repeatables, HttpContext.RequestAborted);
         }
 
+
+
         //工单ID跟通话记录相关联
         //var callRecord = await _trCallRecordRepository.GetAsync(p => p.CallAccept == order.CallId, HttpContext.RequestAborted);//由CallAccept改为OtherAccept
         //var callRecord = await _trCallRecordRepository.GetAsync(p => p.OtherAccept == order.CallId && string.IsNullOrEmpty(p.OtherAccept) == false, HttpContext.RequestAborted);
@@ -3979,6 +3985,15 @@ public class OrderController : BaseController
             var pushTypes = dto.OrderPushTypes.Select(x => x.PushType);
             order.PushType = string.Join(",", pushTypes);
         }
+        if (dto.Tags != null && dto.Tags.Any())
+        {
+            var orderTags = new List<SystemDicData>();
+            foreach (var item in dto.Tags)
+            {
+                orderTags.Add(new SystemDicData() { Id = item });
+            }
+            order.OrderTags = orderTags;
+        }
 
         //处理工单的期满时间
         //首先是工单编辑页面提交的、流程已经开启、工单未归档的工单才能修改期满时间
@@ -4010,7 +4025,7 @@ public class OrderController : BaseController
             }
         }
 
-        await _orderRepository.UpdateNav(order, new UpdateNavRootOptions { IgnoreColumns = ["CallId"] }).Include(d => d.OrderExtension).ExecuteCommandAsync();
+        await _orderRepository.UpdateNav(order, new UpdateNavRootOptions { IgnoreColumns = ["CallId"] }).Include(d => d.OrderExtension).Include(d => d.OrderTags).ExecuteCommandAsync();
 
         //订阅此事件的内部处理工单数据只能更新各自业务的字段,不能全部更新
         //修改工单其他处理事件  (受理短信)
@@ -4229,7 +4244,7 @@ public class OrderController : BaseController
                     continue;
                 }
                 throw;
-            }            
+            }
             var stepInfo = nextSteps.Steps.FirstOrDefault(m => m.BusinessType == EBusinessType.Send);
             if (stepInfo == null)
             {
@@ -4321,7 +4336,7 @@ public class OrderController : BaseController
                 orderHandleFlowDto.CrossSteps = orderHandleFlowDto.CrossSteps.OrderBy(d => d.Sort).ToList();
                 var stepCount = orderHandleFlowDto.CrossSteps.Count;
                 var unhandleSteps = new List<WorkflowStep> { startStep };
-                for (int i = 0;i < stepCount;i++)
+                for (int i = 0; i < stepCount; i++)
                 {
                     var crossStep = orderHandleFlowDto.CrossSteps[i];
                     var tempSteps = new List<WorkflowStep>();
@@ -7344,13 +7359,6 @@ public class OrderController : BaseController
     [HttpGet("order/about_expire/list")]
     public async Task<PagedDto<OrderDto>> AboutList([FromQuery] AboutToExpireListDto dto)
     {
-        var version = _systemSettingCacheManager.GetAboutToExpireVersion;
-        if (version == 1)
-        {
-            var (t, i) = await _orderApplication.GetAboutToExpireAsync(dto)
-                .ToPagedListAsync(dto, HttpContext.RequestAborted);
-            return new PagedDto<OrderDto>(t, _mapper.Map<IReadOnlyList<OrderDto>>(i));
-        }
         var (total, items) = await _orderApplication.GetAboutToExpireAsync(dto)
             .ToPagedListAsync(dto, HttpContext.RequestAborted);
         return new PagedDto<OrderDto>(total, _mapper.Map<IReadOnlyList<OrderDto>>(items));
@@ -7364,7 +7372,7 @@ public class OrderController : BaseController
     [HttpGet("order/about_expire/list_items")]
     public async Task<List<OrderDto>> AboutListAsnc([FromQuery] AboutToExpireListDto dto)
     {
-        return (await _orderApplication.GetAboutToExpireAsync(dto).ToFixedListAsync(dto.PageIndex-1, dto.PageSize, HttpContext.RequestAborted))
+        return (await _orderApplication.GetAboutToExpireAsync(dto).ToFixedListAsync(dto.PageIndex - 1, dto.PageSize, HttpContext.RequestAborted))
             .Adapt<List<OrderDto>>();
     }
 
@@ -7376,8 +7384,8 @@ public class OrderController : BaseController
     [HttpGet("order/about_expire/list_count")]
     public async Task<int> AboutListCountAsnc([FromQuery] AboutToExpireListDto dto)
     {
-		return await _orderApplication.GetAboutToExpireAsync(dto).CountAsync(HttpContext.RequestAborted);
-	}
+        return await _orderApplication.GetAboutToExpireAsync(dto).CountAsync(HttpContext.RequestAborted);
+    }
 
     /// <summary>
     /// 工单即将超期列表(优化测试)

+ 54 - 40
src/Hotline.Api/Controllers/QualityController.cs

@@ -50,9 +50,9 @@ namespace Hotline.Api.Controllers
         private readonly ICallApplication _callApplication;
         private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
         private readonly IRepository<SystemLog> _logRepository;
-		private readonly IRepository<QualityTransferRecords> _qualityTransferRecordsRepository;
+        private readonly IRepository<QualityTransferRecords> _qualityTransferRecordsRepository;
 
-		public QualityController(
+        public QualityController(
             ISessionContext sessionContext,
             IMapper mapper,
             IQualityRepository qualitey,
@@ -72,7 +72,7 @@ namespace Hotline.Api.Controllers
             ICallApplication callApplication,
             IOptionsSnapshot<AppConfiguration> appOptions,
             IRepository<SystemLog> logRepository,
-			IRepository<QualityTransferRecords> qualityTransferRecordsRepository)
+            IRepository<QualityTransferRecords> qualityTransferRecordsRepository)
         {
             _sessionContext = sessionContext;
             _mapper = mapper;
@@ -94,7 +94,7 @@ namespace Hotline.Api.Controllers
             _appOptions = appOptions;
             _logRepository = logRepository;
             _qualityTransferRecordsRepository = qualityTransferRecordsRepository;
-		}
+        }
         #region 质检管理
         /// <summary>
         /// 删除质检
@@ -123,12 +123,25 @@ namespace Hotline.Api.Controllers
             await _qualityApplication.UpdateQualityAsync(dto, HttpContext.RequestAborted);
         }
 
-        /// <summary>
-        /// 获取质检列表
-        /// </summary>
-        /// <param name="dto"></param>
-        /// <returns></returns>
-        [HttpGet("list")]
+		/// <summary>
+		/// 更新质检
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[HttpPut("transfer")]
+		public async Task TransferUpdate([FromBody] TransferUpdate dto)
+		{
+			await _qualitey.Updateable().SetColumns(x=> new Hotline.Quality.Quality { Transfer  = dto.Transfer }).Where(x=>x.Id == dto.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+		}
+
+
+
+		/// <summary>
+		/// 获取质检列表
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[HttpGet("list")]
         public async Task<PagedDto<QualityDto>> List([FromQuery] QualityListDto dto)
         {
             var (total, items) = await _qualitey.Queryable()
@@ -137,6 +150,7 @@ namespace Hotline.Api.Controllers
                 .Includes(x => x.Visit, e => e.Employee)
                 .Includes(x => x.QualityDetails)
                 .WhereIF(!string.IsNullOrEmpty(dto.Keyword), x => x.Order.Title.Contains(dto.Keyword!) || x.Order.No.Contains(dto.Keyword!))
+                .WhereIF(!string.IsNullOrEmpty(dto.No), x => x.Order.No.Contains(dto.No!))
                 .WhereIF(dto.State.HasValue, x => x.State == dto.State)
                 .WhereIF(dto.Source.HasValue, x => x.Source == dto.Source)
                 .WhereIF(dto.Source.HasValue && dto.Source == EQualitySource.Accepted, x => !string.IsNullOrEmpty(x.Order.CallId))
@@ -617,34 +631,34 @@ namespace Hotline.Api.Controllers
             return rsp;
         }
 
-		/// <summary>
-		/// 智能质检转写_兴唐
-		/// </summary>
-		/// <param name="dto"></param>
-		/// <returns></returns>
-		[HttpPost("aitransfer_xt")]
-		[LogFilter("智能质检转写_兴唐")]
-		public async Task AiTransfer_XT([FromBody] List<AiQualityXTDto> dto)
-		{
-			var transfers = new List<QualityTransferRecords>();
-			foreach (var item in dto)
-			{
-                var records =  await _qualityTransferRecordsRepository.Queryable().Where(x=>x.QualityId == item.Id  &&  x.IsFinished == false).AnyAsync();
+        /// <summary>
+        /// 智能质检转写_兴唐
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("aitransfer_xt")]
+        [LogFilter("智能质检转写_兴唐")]
+        public async Task AiTransfer_XT([FromBody] List<AiQualityXTDto> dto)
+        {
+            var transfers = new List<QualityTransferRecords>();
+            foreach (var item in dto)
+            {
+                var records = await _qualityTransferRecordsRepository.Queryable().Where(x => x.QualityId == item.Id && x.IsFinished == false).AnyAsync();
                 if (records)
                     continue;
 
-				var transfer = new QualityTransferRecords();
-				transfer.QualityId = item.Id;
-				transfer.IsFinished = false;
-				transfer.TransferState = EQualityTransferState.NotStarted;
-				transfers.Add(transfer);
-				await _qualitey.Updateable().SetColumns(x => new Hotline.Quality.Quality { TransferState = EQualityTransferState.Translating }).Where(x => x.Id == item.Id).ExecuteCommandAsync();
-			}
-			await _qualityTransferRecordsRepository.AddRangeAsync(transfers);
-		}
+                var transfer = new QualityTransferRecords();
+                transfer.QualityId = item.Id;
+                transfer.IsFinished = false;
+                transfer.TransferState = EQualityTransferState.NotStarted;
+                transfers.Add(transfer);
+                await _qualitey.Updateable().SetColumns(x => new Hotline.Quality.Quality { TransferState = EQualityTransferState.Translating }).Where(x => x.Id == item.Id).ExecuteCommandAsync();
+            }
+            await _qualityTransferRecordsRepository.AddRangeAsync(transfers);
+        }
 
 		/// <summary>
-		/// 智能质检转写
+		/// 智能质检转写_兴唐_定时调用
 		/// </summary>
 		/// <param name="dto"></param>
 		/// <returns></returns>
@@ -666,17 +680,17 @@ namespace Hotline.Api.Controllers
             else {
 				var notStarted = await _qualityTransferRecordsRepository.Queryable().Where(x => x.IsFinished == false && x.TransferState == EQualityTransferState.NotStarted).OrderBy(x => x.TransferTime).FirstAsync();
 				await _qualityTransferRecordsRepository.Updateable().SetColumns(x => new QualityTransferRecords { TransferState = EQualityTransferState.Translating , TransferTime = DateTime.Now }).Where(x => x.Id == notStarted.Id).ExecuteCommandAsync();
-				_qualityApplication.Transfer_XT(notStarted.QualityId, HttpContext.RequestAborted);
+				await _qualityApplication.Transfer_XT(notStarted.QualityId, HttpContext.RequestAborted);
 			}
 		}
 
 
-		/// <summary>
-		/// 智能质检结果返回接收 
-		/// </summary>
-		/// <param name="dto"></param>
-		/// <returns></returns>
-		[AllowAnonymous]
+        /// <summary>
+        /// 智能质检结果返回接收 
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
         [HttpPost("AiResult")]
         [LogFilter("智能质检结果返回接收")]
         public async Task AiResult([FromBody] List<AiQualityResultDto> dto)

+ 2 - 14
src/Hotline.Api/Controllers/Snapshot/SnapshotBulletinController.cs

@@ -43,20 +43,8 @@ public class SnapshotBulletinController : BaseController
     /// <param name="dto"></param>
     /// <returns></returns>
     [HttpGet("bulletin/query")]
-    public async Task<PagedDto<SnapshotBulletinItemsOutDto>> QueryBulletinList([FromQuery] SnapshotBulletinItemsInDto dto)
-    {
-        var query = _bulletinRepository.Queryable()
-            .Includes(x => x.ExaminMan)
-            .WhereIF(!string.IsNullOrEmpty(dto.SnapshotBulletinTypeName), d => d.SnapshotBulletinTypeName.Contains(dto.SnapshotBulletinTypeName))
-            .WhereIF(!string.IsNullOrEmpty(dto.Title), d => d.Title.Contains(dto.Title))
-            .WhereIF(dto.BeginCreationTime.HasValue, d => d.BulletinTime >= dto.BeginCreationTime)
-            .WhereIF(dto.EndCreationTime.HasValue, d => d.BulletinTime <= dto.EndCreationTime)
-            .WhereIF(dto.State.HasValue, d => d.BulletinState == dto.State)
-            .OrderByDescending(d => d.CreationTime)
-            .Select<SnapshotBulletinItemsOutDto>();
-        return (await query
-            .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted)).ToPaged();
-    }
+    public async Task<PagedDto<SnapshotBulletinItemsOutDto>> QueryBulletinItems([FromQuery] SnapshotBulletinItemsInDto dto)
+        => (await _bulletinApplication.QueryBulletinItems(dto).ToPagedListAsync(dto)).ToPaged();
 
     /// <summary>
     /// 公告详情(内部)

+ 12 - 2
src/Hotline.Api/Controllers/Snapshot/SnapshotController.cs

@@ -50,8 +50,9 @@ public class SnapshotController : BaseController
     private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
     private readonly ISessionContext _sessionContext;
     private readonly IThirdAccountRepository _thirdAccountRepository;
+    private readonly ILogger<SnapshotController> _logger;
 
-    public SnapshotController(IRepository<Order> orderRepository, ISnapshotApplication snapshotApplication, ISystemAreaDomainService systemAreaDomainService, IIndustryRepository industryRepository, IOrderDomainService orderDomainService, IFileRepository fileRepository, IOrderSnapshotRepository orderSnapshotRepository, ISystemDicDataCacheManager systemDicDataCacheManager, ISessionContext sessionContext, IThirdAccountRepository thirdAccountRepository)
+    public SnapshotController(IRepository<Order> orderRepository, ISnapshotApplication snapshotApplication, ISystemAreaDomainService systemAreaDomainService, IIndustryRepository industryRepository, IOrderDomainService orderDomainService, IFileRepository fileRepository, IOrderSnapshotRepository orderSnapshotRepository, ISystemDicDataCacheManager systemDicDataCacheManager, ISessionContext sessionContext, IThirdAccountRepository thirdAccountRepository, ILogger<SnapshotController> logger)
     {
         _orderRepository = orderRepository;
         _snapshotApplication = snapshotApplication;
@@ -63,6 +64,7 @@ public class SnapshotController : BaseController
         _systemDicDataCacheManager = systemDicDataCacheManager;
         _sessionContext = sessionContext;
         _thirdAccountRepository = thirdAccountRepository;
+        _logger = logger;
     }
 
     /// <summary>
@@ -119,6 +121,14 @@ public class SnapshotController : BaseController
         orderSnapshot.IndustryId = dto.IndustryId;
         orderSnapshot.IndustryName = industry.Name;
         orderSnapshot.CompanyName = dto.CompanyName;
+        try
+        {
+            orderSnapshot.JobTypeName = _systemDicDataCacheManager.JobType.FirstOrDefault(m => m.DicDataValue == dto.JobType.ToString())?.DicDataName;
+        }
+        catch (Exception e)
+        {
+            _logger.LogError(e, $"添加随手拍工单, 获取JobTypeName异常. '{dto.JobType}'");
+        }
         if (dto.StartWorkTime.NotNullOrEmpty()) orderSnapshot.StartWorkTime = dto.StartWorkTime.ObjToDate();
         if (dto.EndWorkTime.NotNullOrEmpty()) orderSnapshot.EndWorkTime = dto.EndWorkTime.ObjToDate();
         if (dto.Name.NotNullOrEmpty())
@@ -389,7 +399,7 @@ public class SnapshotController : BaseController
                 EventType = 0,
                 WorkAddress = order.Address,
                 CreatedTime = order.CreationTime,
-                WorkType = snapshot.JobType.ObjToInt(),
+                WorkType = snapshot.JobType.Value,
                 Source = 1,
                 WorkTimeStart = snapshot.StartWorkTime.Value,
                 WorkTimeStop = snapshot.EndWorkTime.Value,

+ 26 - 26
src/Hotline.Api/Controllers/SysController.cs

@@ -57,7 +57,7 @@ namespace Hotline.Api.Controllers
         private readonly IServiceScopeFactory _serviceScopeFactory;
         private readonly IRedisCachingProvider _redisCaching;
         private readonly IEasyCachingProvider _easyCaching;
-		private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
+        private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
 
         /// <summary>
         /// 系统管理相关接口
@@ -475,9 +475,9 @@ namespace Hotline.Api.Controllers
         [HttpGet("common-list")]
         public async Task<IReadOnlyList<SystemCommonOpinion>> GetCommon([FromQuery] ECommonType commonType, bool isOpen)
         {
-            return await _commonOpinionRepository.Queryable().Where(x => x.CommonType == commonType )
-			    .WhereIF(isOpen == true, x =>(x.IsOpen == true || (x.CreatorOrgId == _sessionContext.RequiredOrgId && x.AttributionType == ECommonAttributionType.Org && x.IsOpen == false)))
-				.WhereIF(isOpen == false, x => x.IsOpen == false && x.CreatorId == _sessionContext.RequiredUserId).ToListAsync();
+            return await _commonOpinionRepository.Queryable().Where(x => x.CommonType == commonType)
+                .WhereIF(isOpen == true, x => (x.IsOpen == true || (x.CreatorOrgId == _sessionContext.RequiredOrgId && x.AttributionType == ECommonAttributionType.Org && x.IsOpen == false)))
+                .WhereIF(isOpen == false, x => x.IsOpen == false && x.CreatorId == _sessionContext.RequiredUserId).ToListAsync();
         }
 
         /// <summary>
@@ -565,27 +565,27 @@ namespace Hotline.Api.Controllers
         /// <param name="dto"></param>
         /// <returns></returns>
         [HttpGet("log_list")]
-		public async Task<PagedDto<SystemLogDto>> List([FromQuery] SysLogPagedKeywordRequest dto)
-		{
-			var (total, items) = await _systemLogRepository.Queryable()
-				.WhereIF(dto.IsAll.HasValue && !dto.IsAll.Value, x => !string.IsNullOrEmpty(x.Name))
-				.WhereIF(!string.IsNullOrEmpty(dto.Name), x => x.Name.Contains(dto.Name))
-				.WhereIF(!string.IsNullOrEmpty(dto.CreatorName), x => x.CreatorName.Contains(dto.CreatorName))
-				.WhereIF(!string.IsNullOrEmpty(dto.ExecuteUrl), x => x.ExecuteUrl.Contains(dto.ExecuteUrl))
-				.WhereIF(!string.IsNullOrEmpty(dto.ExecuteParam),
-					x => SqlFunc.JsonLike(x.ExecuteParam, dto.ExecuteParam))
-				.WhereIF(dto.StartTime.HasValue && dto.EndTime.HasValue,
-					x => x.CreationTime >= dto.StartTime && x.CreationTime <= dto.EndTime)
-				//.WhereIF(!string.IsNullOrEmpty(dto.Keyword), x => x.Name.Contains(dto.Keyword!) || x.CreatorName.Contains(dto.Keyword!))
-				.Select(x => new SystemLogDto
-				{
-					ExecuteParam = x.ExecuteParam
-				}, true)
-				.OrderByDescending(x => x.CreationTime)
-				.ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
-			return new PagedDto<SystemLogDto>(total, items);
-
-		}
+        public async Task<PagedDto<SystemLogDto>> List([FromQuery] SysLogPagedKeywordRequest dto)
+        {
+            var (total, items) = await _systemLogRepository.Queryable()
+                .WhereIF(dto.IsAll.HasValue && !dto.IsAll.Value, x => !string.IsNullOrEmpty(x.Name))
+                .WhereIF(!string.IsNullOrEmpty(dto.Name), x => x.Name.Contains(dto.Name))
+                .WhereIF(!string.IsNullOrEmpty(dto.CreatorName), x => x.CreatorName.Contains(dto.CreatorName))
+                .WhereIF(!string.IsNullOrEmpty(dto.ExecuteUrl), x => x.ExecuteUrl.Contains(dto.ExecuteUrl))
+                .WhereIF(!string.IsNullOrEmpty(dto.ExecuteParam),
+                    x => SqlFunc.JsonLike(x.ExecuteParam, dto.ExecuteParam))
+                .WhereIF(dto.StartTime.HasValue && dto.EndTime.HasValue,
+                    x => x.CreationTime >= dto.StartTime && x.CreationTime <= dto.EndTime)
+                                //.WhereIF(!string.IsNullOrEmpty(dto.Keyword), x => x.Name.Contains(dto.Keyword!) || x.CreatorName.Contains(dto.Keyword!))
+                .Select(x => new SystemLogDto
+                {
+                    ExecuteParam = x.ExecuteParam
+                }, true)
+                .OrderByDescending(x => x.CreationTime)
+                .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+            return new PagedDto<SystemLogDto>(total, items);
+
+        }
 
 		/// <summary>
 		/// 获取日志实体
@@ -656,7 +656,7 @@ namespace Hotline.Api.Controllers
         public async Task<List<string>> GetCacheByKey(string key)
         {
             var cacheResult = new List<string>();
-            var result = await _easyCaching.GetAsync<object>(key,cancellationToken: HttpContext.RequestAborted);
+            var result = await _easyCaching.GetAsync<object>(key, cancellationToken: HttpContext.RequestAborted);
             if (result != null)
             {
                 cacheResult.Add(result.ToJson());

+ 8 - 7
src/Hotline.Api/Controllers/TestController.cs

@@ -560,12 +560,12 @@ ICallApplication callApplication,
         //var r = _timeLimitDomainService.CalcExpiredTime(DateTime.Now, EFlowDirection.CenterToCenter, batchId);
         //var r = _timeLimitDomainService.CalcEndTime(DateTime.Parse("2024-09-12 14:45:47"), Share.Enums.Settings.ETimeType.WorkDay, 2, 80, 50);
         //_capPublisher.PublishDelay((DateTime.Now.AddMinutes(2) - DateTime.Now), EventNames.OrderRelateCall, "123");
-        //var times =await _expireTime.CalcExpiredTime(DateTime.Parse("2024-12-31 13:25:13.137977"), DateTime.Parse("2024-12-31 13:25:13.137977"), EFlowDirection.CenterToOrg,new OrderTimeClacInfo() { AcceptTypeCode= "10" });
+        var times =await _expireTime.CalcExpiredTime(DateTime.Parse("2025-01-14 09:45:00"), DateTime.Parse("2025-01-07 09:16:53.691249"), EFlowDirection.CenterToOrg,new OrderTimeClacInfo() { AcceptTypeCode= "20" });
         //await _expireTime.CalcWorkTimeToDecimal(visit.VisitTime.Value, DateTime.Now, false);
         //var times = await _expireTime.CalcWorkTimeToDecimal(DateTime.Parse("2024-12-16 21:36:27"), DateTime.Parse("2024-12-17 12:47:05"), false);
-        var query = _userRepository.Queryable().Where(x => false);
+        //var query = _userRepository.Queryable().Where(x => false);
         //await _capPublisher.PublishDelay(EventNames.OrderRelateCall, "123", cancellationToken: HttpContext.RequestAborted);
-        return OpenResponse.Ok(query.ToSqlString());
+        return OpenResponse.Ok(times);
     }
 
 
@@ -1490,12 +1490,13 @@ ICallApplication callApplication,
 	/// <returns></returns>
 	[HttpPost("end_order_data_dispose")]
 	[AllowAnonymous]
-	public async Task EndOrderDataDispose([FromBody] string No ) {
-        var orderNo = await _orderRepository.Queryable().Where(x => x.No == No).FirstAsync(HttpContext.RequestAborted);
+	public async Task EndOrderDataDispose([FromBody] string No)
+	{
+		var orderNo = await _orderRepository.Queryable().Where(x => x.No == No).FirstAsync(HttpContext.RequestAborted);
 
-        var workflow =  await _workflowRepository.Queryable().Where(x=>x.ExternalId == orderNo.Id).FirstAsync(HttpContext.RequestAborted);
+		var workflow = await _workflowRepository.Queryable().Where(x => x.ExternalId == orderNo.Id).FirstAsync(HttpContext.RequestAborted);
 
-        var notification = await _workflowStepRepository.Queryable().Where(x => x.ExternalId == orderNo.Id && x.BusinessType == EBusinessType.File && x.StepType == EStepType.End).FirstAsync(HttpContext.RequestAborted);
+		var notification = await _workflowStepRepository.Queryable().Includes(x=>x.Workflow,w=>w.Steps).Where(x => x.ExternalId == orderNo.Id && x.BusinessType == EBusinessType.File && x.StepType == EStepType.End).FirstAsync(HttpContext.RequestAborted);
 		var order = await _orderDomainService.GetOrderAsync(orderNo.Id,
 					   withExtension: true, cancellationToken: HttpContext.RequestAborted);
 		//order.CheckIfFiled();

+ 515 - 4
src/Hotline.Api/Controllers/WebPortalController.cs

@@ -8,6 +8,7 @@ using Hotline.KnowledgeBase;
 using Hotline.Orders;
 using Hotline.Push.Notifies;
 using Hotline.Repository.SqlSugar.Extensions;
+using Hotline.Repository.SqlSugar.Knowledge;
 using Hotline.Settings;
 using Hotline.Settings.Hotspots;
 using Hotline.Share.Dtos;
@@ -25,6 +26,7 @@ using MediatR;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Options;
+using Senparc.Weixin.WxOpen.AdvancedAPIs.WxApp.WxAppJson;
 using SqlSugar;
 using XF.Domain.Authentications;
 using XF.Domain.Cache;
@@ -35,6 +37,8 @@ namespace Hotline.Api.Controllers
 {
     public class WebPortalController : BaseController
     {
+        #region 注入
+
         private readonly IMapper _mapper;
         private readonly IMediator _mediator;
         private readonly IRepository<Bulletin> _bulletinRepository;
@@ -52,13 +56,13 @@ namespace Hotline.Api.Controllers
         private readonly IRepository<OrderVisitDetail> _orderVisitDetailRepository;
         private readonly IBulletinApplication _bulletinApplication;
         private readonly IRepository<OldPublicData> _oldPublicDataRepository;
-        private readonly IRepository<KnowledgeRelationType> _knowledgeRelationTypeRepository;
         private readonly IRepository<KnowledgeType> _knowledgeTypeRepository;
         private readonly IRepository<Knowledge> _knowledgeRepository;
         private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
         private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
         private readonly ITypedCache<string> _getVailData;
         private readonly IRepository<KnowledgeWord> _knowledgeWordRepository;
+        private readonly ISystemDicDataCacheManager _sysDicDataCacheManager;
 
         public WebPortalController(IMapper mapper,
             IMediator mediator,
@@ -77,13 +81,13 @@ namespace Hotline.Api.Controllers
             IRepository<OrderVisitDetail> orderVisitDetailRepository,
             IBulletinApplication bulletinApplication,
             IRepository<OldPublicData> oldPublicDataRepository,
-            IRepository<KnowledgeRelationType> knowledgeRelationTypeRepository,
             IRepository<KnowledgeType> knowledgeTypeRepository,
             IRepository<Knowledge> knowledgeRepository,
             ISystemDicDataCacheManager systemDicDataCacheManager,
             IOptionsSnapshot<AppConfiguration> appOptions,
             ITypedCache<string> getVailData,
-            IRepository<KnowledgeWord> knowledgeWordRepository
+            IRepository<KnowledgeWord> knowledgeWordRepository,
+            ISystemDicDataCacheManager sysDicDataCacheManager
             )
         {
             _mapper = mapper;
@@ -103,15 +107,17 @@ namespace Hotline.Api.Controllers
             _orderVisitDetailRepository = orderVisitDetailRepository;
             _bulletinApplication = bulletinApplication;
             _oldPublicDataRepository = oldPublicDataRepository;
-            _knowledgeRelationTypeRepository = knowledgeRelationTypeRepository;
             _knowledgeTypeRepository = knowledgeTypeRepository;
             _knowledgeRepository = knowledgeRepository;
             _systemDicDataCacheManager = systemDicDataCacheManager;
             _appOptions = appOptions;
             _getVailData = getVailData;
             _knowledgeWordRepository = knowledgeWordRepository;
+            _sysDicDataCacheManager = sysDicDataCacheManager;
         }
 
+        #endregion
+
         #region 通知
         /// <summary>
         /// 获取列表
@@ -1311,6 +1317,8 @@ namespace Hotline.Api.Controllers
         }
         #endregion
 
+        #region 知识库
+
         /// <summary>
         /// 获取知识库分类
         /// </summary>
@@ -1405,6 +1413,10 @@ namespace Hotline.Api.Controllers
             return OpenResponse.Ok(WebPortalDeResponse<IReadOnlyList<KnowledgeInfoDto>>.Success(dataDto));
         }
 
+        #endregion
+
+        #region 查询受理类型
+
         /// <summary>
         /// 查询受理类型----受理量
         /// </summary>
@@ -1509,6 +1521,10 @@ namespace Hotline.Api.Controllers
             return OpenResponse.Ok(WebPortalDeResponse<IReadOnlyList<OrderFormCount>>.Success(formFileCount));
         }
 
+        #endregion
+
+        #region 缓存值
+
         /// <summary>
         /// 缓存值
         /// </summary>
@@ -1550,5 +1566,500 @@ namespace Hotline.Api.Controllers
 
             return OpenResponse.Ok(WebPortalDeResponse<string>.Success("-1"));
         }
+
+        #endregion
+
+        #region 通知-宜宾
+
+        /// <summary>
+        /// 获取列表
+        /// </summary>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("getarticlelist_yibin")]
+        public async Task<OpenResponse> GetArticleListYiBin([FromBody] QueryArticleListDto dto)
+        {
+            if (string.IsNullOrEmpty(dto.PushRanges))
+                dto.PushRanges = "2";
+
+            RefAsync<int> total = 0;
+            var items1 = _bulletinRepository.Queryable()
+                .Where(p => p.LoseEfficacyTime >= DateTime.Now)
+                .Where(p => p.IsArrive == true)
+                .Where(p => SqlFunc.JsonListObjectAny(p.PushRanges, "Key", dto.PushRanges))
+                .Where(p => p.BulletinTypeId == dto.NoticeType)
+                .WhereIF(!string.IsNullOrEmpty(dto.Condition), p => p.Title.Contains(dto.Condition))
+                .Select(it => new ArticleListDto
+                {
+                    Page = 0,
+                    NoticeID = it.Id,
+                    //NoticeContent = it.Content,
+                    NoticeTypeName = it.BulletinTypeName,
+                    NoticeTitle = it.Title,
+                    NoticeBMName = it.SourceOrgName,
+                    NoticeCreateDate = it.CreationTime,
+                    VideoUrl = "",
+                    WNLT_FullCode = ""
+                });
+
+            List<ArticleListDto> items = new List<ArticleListDto>();
+            if (dto.NoticeType == "6")
+            {
+                //知识库
+                var items2 = _knowledgeRepository.Queryable()
+                    .Where(k => k.IsPublic == true)
+                    .Where(k => k.Status == EKnowledgeStatus.OnShelf)
+                    .Where(k => k.Attribution == "部门知识库")
+                    .Select(it => new ArticleListDto
+                    {
+                        Page = 0,
+                        NoticeID = it.Id,
+                        //NoticeContent = it.Content,
+                        NoticeTypeName = "知识库",
+                        NoticeTitle = it.Title,
+                        NoticeBMName = it.CreatorOrgName,
+                        NoticeCreateDate = it.CreationTime,
+                        VideoUrl = "",
+                        WNLT_FullCode = ""
+                    });
+
+                items = await _orderRepository.UnionAll(items1, items2)
+                  .Select(x => new ArticleListDto
+                  {
+                      Page = SqlFunc.RowNumber($"{x.NoticeCreateDate} desc "),
+                      NoticeID = x.NoticeID,
+                      //NoticeContent = x.NoticeContent,
+                      NoticeTypeName = x.NoticeTypeName,
+                      NoticeTitle = x.NoticeTitle,
+                      NoticeBMName = x.NoticeBMName,
+                      NoticeCreateDate = x.NoticeCreateDate,
+                      VideoUrl = x.VideoUrl,
+                      WNLT_FullCode = x.WNLT_FullCode
+                  })
+                  .OrderByDescending(p => p.NoticeCreateDate)
+                  .ToPageListAsync(dto.PageIndex, dto.PageSize, total);
+            }
+            else
+            {
+                items = await items1
+                  .OrderByDescending(p => p.NoticeCreateDate)
+                  .ToPageListAsync(dto.PageIndex, dto.PageSize, total);
+
+            }
+
+            //计算总页数
+            int nPageCount = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(total) / dto.PageSize));
+            ArticleListDataDto dataDto = new()
+            {
+                PageCount = nPageCount,
+                Total = total,
+                data = _mapper.Map<IReadOnlyList<ArticleListDto>>(items)
+            };
+
+            return OpenResponse.Ok(WebPortalDeResponse<ArticleListDataDto>.Success(dataDto));
+        }
+
+        /// <summary>
+        /// 获取详情,修改阅读次数
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("getarticledetails_yibin")]
+        public async Task<OpenResponse> GetArticleDetailsYiBin([FromBody] ArticleIdDto dto)
+        {
+            ArticleDetailsDto detailsDto = null;
+            if (dto.TypeName == "知识库")
+            {
+                var data = await _knowledgeRepository.GetAsync(p => p.Id == dto.Id, HttpContext.RequestAborted);
+                if (data != null)
+                {
+                    data.PageView = data.PageView++;
+                    await _knowledgeRepository.UpdateAsync(data, HttpContext.RequestAborted);
+                    detailsDto = new()
+                    {
+                        NoticeID = data.Id,
+                        NoticeTypeName = "知识库",
+                        NoticeTitle = data.Title,
+                        NoticeBMName = data.CreatorOrgName,
+                        NoticeCreateDate = data.CreationTime,
+                        NoticeRCount = data.PageView,
+                        WNED_VideoUrl = "",
+                        NoticeContent = data.Content
+                    };
+                    if (data != null && !string.IsNullOrEmpty(data.Content))
+                        data.Content = _bulletinApplication.GetSiteUrls(data.Content);
+                }
+                else
+                    detailsDto = new();
+            }
+            else
+            {
+                var data = await _bulletinRepository.GetAsync(p => p.Id == dto.Id, HttpContext.RequestAborted);
+                if (data != null)
+                {
+                    data.ReadedNum = data.ReadedNum++;
+                    await _bulletinRepository.UpdateAsync(data, HttpContext.RequestAborted);
+                    detailsDto = new()
+                    {
+                        NoticeID = data.Id,
+                        NoticeTypeName = data.BulletinTypeName,
+                        NoticeTitle = data.Title,
+                        NoticeBMName = data.SourceOrgName,
+                        NoticeCreateDate = data.CreationTime,
+                        NoticeRCount = data.ReadedNum,
+                        WNED_VideoUrl = "",
+                        NoticeContent = data.Content
+                    };
+                    if (data != null && !string.IsNullOrEmpty(data.Content))
+                        data.Content = _bulletinApplication.GetSiteUrls(data.Content);
+                }
+                else
+                    detailsDto = new();
+            }
+
+            List<ArticleDetailsDto> dataDto = new() { detailsDto };
+            return OpenResponse.Ok(WebPortalDeResponse<IReadOnlyList<ArticleDetailsDto>>.Success(dataDto));
+        }
+
+        /// <summary>
+        /// 上一条和下一条
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("getpreviousandnext_yibin")]
+        public async Task<OpenResponse> GetPreviousAndNextYiBin([FromBody] ArticlePreviousAndNextDto dto)
+        {
+            if (string.IsNullOrEmpty(dto.PushRanges))
+                dto.PushRanges = "2";
+
+            var sugar = _bulletinRepository.Queryable()
+                .Where(p => p.LoseEfficacyTime >= DateTime.Now)
+                .Where(p => p.IsArrive == true)
+                .Where(p => SqlFunc.JsonListObjectAny(p.PushRanges, "Key", dto.PushRanges));
+
+            if (dto.FullSearch == "1")//全文搜索
+            {
+                if (_appOptions.Value.IsYiBin)
+                {
+                    sugar.Where(p => p.BulletinTypeId == "1" || p.BulletinTypeId == "5" || p.BulletinTypeId == "6" || p.BulletinTypeId == "7" || p.BulletinTypeId == "3" || p.BulletinTypeId == "4")
+                    .WhereIF(!string.IsNullOrEmpty(dto.Condition), p => p.Content.Contains(dto.Condition) || p.Title.Contains(dto.Condition));
+                }
+                if (_appOptions.Value.IsZiGong)
+                {
+                    sugar.Where(p => p.BulletinTypeId == "5013" || p.BulletinTypeId == "5016" || p.BulletinTypeId == "5011" || p.BulletinTypeId == "5015" || p.BulletinTypeId == "5017")
+                    .WhereIF(!string.IsNullOrEmpty(dto.Condition), p => p.Content.Contains(dto.Condition) || p.Title.Contains(dto.Condition));
+                }
+            }
+            else//指定分类
+            {
+                sugar.WhereIF(!string.IsNullOrEmpty(dto.Condition), p => p.Title.Contains(dto.Condition))
+                    .Where(p => p.BulletinTypeId == dto.NoticeTypeId);
+            }
+
+            var items1 = sugar.Select(it => new ArticleDetailDataDto
+            {
+                Page = SqlFunc.RowNumber($"{it.CreationTime} desc "),// SqlFunc.MappingColumn(default(int), "  row_number()  over( order by 'NoticeCreateDate'  ) "),
+                NoticeID = it.Id,
+                NoticeTitle = it.Title,
+                NoticeTypeName = it.BulletinTypeName,
+                NoticeCreateDate = it.CreationTime
+            });
+
+            List<ArticleDetailDataDto> list = new List<ArticleDetailDataDto>();
+            if (dto.NoticeTypeId == "6")
+            {
+                //知识库
+                var items2 = _knowledgeRepository.Queryable()
+                    .Where(k => k.IsPublic == true)
+                    .Where(k => k.Status == EKnowledgeStatus.OnShelf)
+                    .Where(k => k.Attribution == "部门知识库")
+                    .WhereIF(!string.IsNullOrEmpty(dto.Condition), k => k.Content.Contains(dto.Condition) || k.Title.Contains(dto.Condition))
+                    .Select(it => new ArticleDetailDataDto
+                    {
+                        Page = 0,
+                        NoticeID = it.Id,
+                        NoticeTitle = it.Title,
+                        NoticeTypeName = "知识库",
+                        NoticeCreateDate = it.CreationTime
+                    });
+
+                list = await _orderRepository.UnionAll(items1, items2)
+                    .Select(x => new ArticleDetailDataDto
+                    {
+                        Page = SqlFunc.RowNumber($"{x.NoticeCreateDate} desc "),
+                        NoticeID = x.NoticeID,
+                        NoticeTitle = x.NoticeTitle,
+                        NoticeTypeName = x.NoticeTypeName,
+                        NoticeCreateDate = x.NoticeCreateDate
+                    })
+                    .OrderByDescending(x => x.NoticeCreateDate)
+                    .ToListAsync();
+            }
+            else
+            {
+                list = await items1.OrderByDescending(x => x.NoticeCreateDate).ToListAsync();
+            }
+
+            //数据为空返回空数据
+            if (list == null || list.Count == 0)
+                return OpenResponse.Ok(WebPortalDeResponse<List<ArticlePreviousAndNextDataDto>>.Success(null));
+            else
+            {
+                var temp = list.Find(p => p.NoticeID == dto.ID);
+                if (temp != null)
+                {
+                    List<ArticlePreviousAndNextDataDto> returnDto = new();
+                    //上一条
+                    var pTemp = list.Find(p => p.Page == temp.Page - 1);
+                    if (pTemp != null)
+
+                        returnDto.Add(new()
+                        {
+                            NoticeID = pTemp.NoticeID,
+                            NoticeTitle = pTemp.NoticeTitle,
+                            NoticeTypeName = pTemp.NoticeTypeName,
+                            pntype = "p"
+                        });
+
+                    //下一条
+                    var nTemp = list.Find(p => p.Page == temp.Page + 1);
+                    if (nTemp != null)
+                        returnDto.Add(new()
+                        {
+                            NoticeID = nTemp.NoticeID,
+                            NoticeTitle = nTemp.NoticeTitle,
+                            NoticeTypeName = nTemp.NoticeTypeName,
+                            pntype = "n"
+                        });
+                    return OpenResponse.Ok(WebPortalDeResponse<List<ArticlePreviousAndNextDataDto>>.Success(returnDto));
+                }
+                else
+                    return OpenResponse.Ok(WebPortalDeResponse<List<ArticlePreviousAndNextDataDto>>.Success(null));
+            }
+        }
+
+        /// <summary>
+        /// 全文检索
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("getfulltextsearchlist_yibin")]
+        public async Task<OpenResponse> GetFullTextSearchList_YiBin([FromBody] QueryArticleListDto dto)
+        {
+            if (string.IsNullOrEmpty(dto.PushRanges))
+                dto.PushRanges = "2";
+
+            if (string.IsNullOrEmpty(dto.Condition))
+                return OpenResponse.Ok(WebPortalDeResponse<FullTextSearchListDataDto>.Success(null));
+
+            RefAsync<int> total = 0;
+            var items1 = _bulletinRepository.Queryable()
+                 .Where(p => p.LoseEfficacyTime >= DateTime.Now)
+                 .Where(p => p.IsArrive == true)
+                 .Where(p => SqlFunc.JsonListObjectAny(p.PushRanges, "Key", dto.PushRanges))
+                 .WhereIF(_appOptions.Value.IsYiBin, p => p.BulletinTypeId == "1" || p.BulletinTypeId == "5" || p.BulletinTypeId == "6" || p.BulletinTypeId == "7" || p.BulletinTypeId == "3" || p.BulletinTypeId == "4")
+                 .WhereIF(_appOptions.Value.IsZiGong, p => p.BulletinTypeId == "5013" || p.BulletinTypeId == "5016" || p.BulletinTypeId == "5011" || p.BulletinTypeId == "5015" || p.BulletinTypeId == "5017")
+                 .WhereIF(!string.IsNullOrEmpty(dto.Condition), p => p.Title.Contains(dto.Condition))
+                 .OrderByDescending(p => p.CreationTime)
+                 .Select(it => new FullTextSearchListDto
+                 {
+                     Page = 0,
+                     NoticeID = it.Id,
+                     //Content = it.Content,
+                     NoticeTypeID = it.BulletinTypeId,
+                     NoticeTypeName = it.BulletinTypeName,
+                     NoticeTitle = it.Title,
+                     NoticeBMName = it.SourceOrgName,
+                     NoticeCreateDate = it.CreationTime.ToString("yyyy-MM-dd HH:mm:ss"),
+                     TypeCode = ""
+                 });
+
+            List<FullTextSearchListDto> items = new List<FullTextSearchListDto>();
+            if (dto.NoticeType == "6")
+            {
+                //知识库
+                var items2 = _knowledgeRepository.Queryable()
+                    .Where(k => k.IsPublic == true)
+                    .Where(k => k.Status == EKnowledgeStatus.OnShelf)
+                    .Where(k => k.Attribution == "部门知识库")
+                    .WhereIF(!string.IsNullOrEmpty(dto.Condition), k => k.Content.Contains(dto.Condition) || k.Title.Contains(dto.Condition))
+                    .Select(it => new FullTextSearchListDto
+                    {
+                        Page = 0,
+                        NoticeID = it.Id,
+                        //NoticeContent = it.Content,
+                        NoticeTypeID = "6",
+                        NoticeTypeName = "知识库",
+                        NoticeTitle = it.Title,
+                        NoticeBMName = it.CreatorOrgName,
+                        NoticeCreateDate = it.CreationTime.ToString("yyyy-MM-dd HH:mm:ss"),
+                        TypeCode = ""
+                    });
+
+                items = await _orderRepository.UnionAll(items1, items2)
+                  .Select(x => new FullTextSearchListDto
+                  {
+                      Page = SqlFunc.RowNumber($"{x.NoticeCreateDate} desc "),
+                      NoticeID = x.NoticeID,
+                      //NoticeContent = it.Content,
+                      NoticeTypeID = x.NoticeTypeID,
+                      NoticeTypeName = x.NoticeTypeName,
+                      NoticeTitle = x.NoticeTitle,
+                      NoticeBMName = x.NoticeBMName,
+                      NoticeCreateDate = x.NoticeCreateDate,
+                      TypeCode = ""
+                  })
+                  .OrderByDescending(p => p.NoticeCreateDate)
+                  .ToPageListAsync(dto.PageIndex, dto.PageSize, total);
+            }
+            else
+            {
+                items = await items1
+                  .OrderByDescending(p => p.NoticeCreateDate)
+                  .ToPageListAsync(dto.PageIndex, dto.PageSize, total);
+            }
+
+            //计算总页数
+            int nPageCount = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(total) / dto.PageSize));
+            FullTextSearchListDataDto dataDto = new()
+            {
+                PageCount = nPageCount,
+                data = _mapper.Map<IReadOnlyList<FullTextSearchListDto>>(items)
+            };
+            return OpenResponse.Ok(WebPortalDeResponse<FullTextSearchListDataDto>.Success(dataDto));
+        }
+
+        #endregion
+
+        #region 办件-宜宾
+
+        /// <summary>
+        /// 办件摘编详情
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("getorderdetailbyid_yibin")]
+        public async Task<object> GetOrderDetailByIdYiBin([FromBody] ArticleIdDto dto)
+        {
+            var data = await _orderRepository.GetAsync(p => p.Id == dto.Id, HttpContext.RequestAborted);
+
+            var orderDetail = _mapper.Map<OrderDetail>(data);
+            if (data != null)
+            {
+                //如果是省工单或者是省政民互动的工单,市民不能进行评价
+                if (data.IsProvince == true || data.SourceChannelCode == "ZMHD")
+                    orderDetail.IsProvinceOrder = "1";
+                else
+                    orderDetail.IsProvinceOrder = "0";
+
+                //获取发布的数据
+                var orderPublish = await _orderPublishRepository.GetAsync(p => p.OrderId == data.Id, HttpContext.RequestAborted);
+                if (orderPublish != null)
+                {
+                    orderDetail.PubFlag = "1";
+                    orderDetail.FlowTitle = orderPublish.ArrangeTitle;
+                    orderDetail.FlowContent = orderPublish.ArrangeContent;
+                    orderDetail.FlowResult = orderPublish.ArrangeOpinion;
+                    if (orderPublish.PublishState)
+                        orderDetail.FlowPubFlagName = "公开";
+                }
+
+                //能否进行评价
+                var orderVisit = await _orderVisitRepository.GetAsync(p => p.OrderId == data.Id && p.VisitState != EVisitState.None, HttpContext.RequestAborted);
+                if (orderVisit == null)
+                    orderDetail.VisitType = "0";
+                else
+                {
+                    orderDetail.VisitType = orderVisit.VisitState switch
+                    {
+                        EVisitState.WaitForVisit => "1",
+                        EVisitState.Visited => "2",
+                        EVisitState.Visiting or EVisitState.NoSatisfiedWaitForVisit or EVisitState.None => "0",
+                        _ => "0",
+                    };
+                }
+            }
+            //List<OrderDetail> dataDto = new() { orderDetail };
+            //return OpenResponse.Ok(WebPortalDeResponse<IReadOnlyList<OrderDetail>>.Success(dataDto));
+
+            var dissatisfiedReason = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.DissatisfiedReason);
+
+            return new
+            {
+                OrderDetail = orderDetail,
+                DissatisfiedReason = dissatisfiedReason
+            };
+        }
+
+        #endregion
+
+        #region 评价-宜宾
+
+        /// <summary>
+        /// 评价内容
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("receivevisitdata_yibin")]
+        public async Task<OpenResponse> ReceiveVisitData_YiBin([FromBody] OrderVisitListDataDto dto)
+        {
+            if (dto == null || dto.VistListDtos == null || dto.VistListDtos.Count == 0)
+                return OpenResponse.Ok(WebPortalDeResponse<string>.Failed("评价数据不能为空!"));
+
+            var dataOrder = await _orderVisitRepository.GetAsync(p => p.OrderId == dto.OrderId && p.VisitState != EVisitState.None, HttpContext.RequestAborted);
+            if (dataOrder == null)
+                return OpenResponse.Ok(WebPortalDeResponse<string>.Failed("工单已经评价!"));
+
+            //组装数据
+            OrderVisitWebDto visitWebDto = new()
+            {
+                VisitType = Hotline.Share.Enums.Order.EVisitType.WebVisit,
+                VisitTime = DateTime.Now
+            };
+
+            List<OrderVisitDetailWebDto> orderVisitDetails = new();
+
+            #region 部门评价
+            //部门评价
+            //遍历数据
+            foreach (var item in dto.VistListDtos)
+            {
+                Hotline.Share.Dtos.Kv kv = item.SatisfactionCode switch
+                {
+                    "5" => new Hotline.Share.Dtos.Kv { Key = "5", Value = "非常满意" },
+                    "4" => new Hotline.Share.Dtos.Kv { Key = "4", Value = "满意" },
+                    "2" => new Hotline.Share.Dtos.Kv { Key = "2", Value = "不满意" },
+                    _ => new Hotline.Share.Dtos.Kv { Key = "7", Value = "不做评价" },
+                };
+                //组装回访数据
+                visitWebDto.Id = item.VisitId;
+                OrderVisitDetailWebDto detailDto = new()
+                {
+                    Id = item.Id,
+                    VisitTarget = Hotline.Share.Enums.Order.EVisitTarget.Org,
+                    OrgProcessingResults = kv,
+                    OrgNoSatisfiedReason = item.OrgNoSatisfiedReason,
+                    OrgHandledAttitude = kv,
+                    VisitContent = item.VisitContent,
+                    IsContact = item.IsContact,
+                    Volved = item.Volved
+                };
+                orderVisitDetails.Add(detailDto);
+            }
+            #endregion
+
+            visitWebDto.OrderVisitDetailDto = orderVisitDetails;
+
+            //推送数据
+            await _orderApplication.OrderVisitWeb(visitWebDto, HttpContext.RequestAborted);
+            return OpenResponse.Ok(WebPortalDeResponse<string>.Success("1"));
+        }
+
+        #endregion
     }
 }

+ 4 - 1
src/Hotline.Api/StartupExtensions.cs

@@ -47,6 +47,7 @@ using Hotline.Orders.DatabaseEventHandler;
 using Hotline.Snapshot;
 using Hotline.WeChat;
 using Hotline.Ai.XingTang;
+using Hotline.Pdf;
 
 
 namespace Hotline.Api;
@@ -211,7 +212,9 @@ internal static class StartupExtensions
 
         //job
         services.RegisterJob(appConfiguration);
-        
+
+        services.AddPdfManager();
+
         //compression
         services.RejisterCompression();
 

+ 5 - 4
src/Hotline.Api/config/appsettings.Development.json

@@ -27,8 +27,8 @@
     "ZiGong": {
       //智能质检
       "AiQuality": {
-        "Url": "http://175.10.86.234:10095" // 正式
-        //"Url": "http://118.122.73.80:19072/", // 测试
+        "Url": "http://175.10.86.234:10095" //  测试
+        //"Url": "http://10.56.131.13:10095/", // 正式
       },
       "AreaCode": "510300",
       "CallCenterType": "XingTang"
@@ -62,8 +62,9 @@
       "Ip": "222.213.23.229"
     },
     "XingTang": {
-      //"DbConnectionString": "server=123.56.10.71;Database=callcenter_db;Uid=root;Pwd=Lhw1981!(*!"
-      "DbConnectionString": "server=110.188.24.182;Database=callcenter_xingtang;Uid=dev;Pwd=fengwo11!!"
+        //"DbConnectionString": "server=123.56.10.71;Database=callcenter_db;Uid=root;Pwd=Lhw1981!(*!"
+        "DbConnectionString": "server=110.188.24.182;Database=callcenter_xingtang;Uid=dev;Pwd=fengwo11!!"
+        //"DbConnectionString": "PORT=50143;server=110.188.24.182;Database=callcenter_db;Uid=dev;Pwd=fengwo123!@#;"
     }
   },
   "ConnectionStrings": {

+ 18 - 14
src/Hotline.Application.Tests/Application/KnowApplicationTest.cs

@@ -25,17 +25,21 @@ namespace Hotline.Application.Tests.Application;
 public class KnowApplicationTest : TestBase
 {
     private readonly IKnowApplication _knowApplication;
-    private readonly IRepository<KnowledgeRelationType> _knowledgeRelationTypeRepository;
     private readonly IMediator _mediator;
     private readonly IRepository<KnowledgeBase.Knowledge> _knowledgeRepository;
     private readonly IKnowledgeDomainService _knowledgeDomainService;
     private readonly IRepository<KnowledgeWord> _knowledgeWordRepository;
     private readonly IRepository<KnowledgeHotWord> _knowledgeHotWordRepository;
 
-    public KnowApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, IKnowApplication knowApplication, IRepository<KnowledgeRelationType> knowledgeRelationTypeRepository, IMediator mediator, IRepository<KnowledgeBase.Knowledge> knowledgeRepository, IKnowledgeDomainService knowledgeDomainService, IRepository<KnowledgeWord> knowledgeWordRepository, IRepository<KnowledgeHotWord> knowledgeHotWordRepository, IThirdIdentiyService thirdService, IThirdAccountRepository thirdAccount) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdService, thirdAccount)
+    public KnowApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository,
+        UserController userController, IServiceScopeFactory scopeFactory,
+        IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor,
+        IKnowApplication knowApplication, IMediator mediator, IRepository<KnowledgeBase.Knowledge> knowledgeRepository,
+        IKnowledgeDomainService knowledgeDomainService, IRepository<KnowledgeWord> knowledgeWordRepository,
+        IRepository<KnowledgeHotWord> knowledgeHotWordRepository, IThirdIdentiyService thirdService, IThirdAccountRepository thirdAccount)
+        : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdService, thirdAccount)
     {
         _knowApplication = knowApplication;
-        _knowledgeRelationTypeRepository = knowledgeRelationTypeRepository;
         _mediator = mediator;
         _knowledgeRepository = knowledgeRepository;
         _knowledgeDomainService = knowledgeDomainService;
@@ -66,18 +70,18 @@ public class KnowApplicationTest : TestBase
     [Fact]
     public async Task GetPageViewList_Test()
     {
-        var r = await _knowledgeRelationTypeRepository.Queryable()
-            .OrderByDescending(m => m.CreationTime)
-            .FirstAsync();
-        var knowledge = await _knowledgeRepository.GetAsync(r.KnowledgeId);
-        await _knowledgeDomainService.KnowledgePvIncreaseAsync(knowledge, new CancellationToken());
+        //var r = await _knowledgeRelationTypeRepository.Queryable()
+        //    .OrderByDescending(m => m.CreationTime)
+        //    .FirstAsync();
+        //var knowledge = await _knowledgeRepository.GetAsync(r.KnowledgeId);
+        //await _knowledgeDomainService.KnowledgePvIncreaseAsync(knowledge, new CancellationToken());
 
-        var inDto = new PageViewInDto
-        {
-            KnowledgeTypeId = r.KnowledgeTypeId
-        };
-        var (total, items) = await _knowApplication.GetPageViewListAsync(inDto);
-        total.ShouldNotBe(0);
+        //var inDto = new PageViewInDto
+        //{
+        //    KnowledgeTypeId = r.KnowledgeTypeId
+        //};
+        //var (total, items) = await _knowApplication.GetPageViewListAsync(inDto);
+        //total.ShouldNotBe(0);
     }
 
     [Fact]

+ 2 - 1
src/Hotline.Application.Tests/Controller/SnapshotControllerTest.cs

@@ -70,7 +70,7 @@ public class SnapshotControllerTest : TestBase
         inDto.County = "沿滩区";
         inDto.Description = "单元测试添加的时间描述";
         inDto.IsSecret = false;
-        //inDto.JobType = 
+        inDto.JobType = 1;
         foreach (var item in inDto.Files)
         {
             item.FileName = DateTime.Now.ToShortTimeString() + "文件.doc";
@@ -85,5 +85,6 @@ public class SnapshotControllerTest : TestBase
         orderEntity.Content.ShouldNotBeNullOrEmpty();
         var orderSnapshotEntity = await _orderSnapshotRepository.GetAsync(order.Id);
         orderSnapshotEntity.ShouldNotBeNull();
+        orderSnapshotEntity.JobType.ShouldBe(inDto.JobType);
     }
 }

+ 1 - 1
src/Hotline.Application.Tests/Startup.cs

@@ -64,8 +64,8 @@ using TianQue.Sdk;
 using Hotline.Snapshot.Notifications;
 using Hotline.File;
 using Hotline.Application.Tests.Mock.Interfaces;
-using Hotline.Orders.DatabaseEventHandler;
 using Hotline.Orders;
+using Hotline.Orders.DatabaseEventHandler;
 using XF.Domain.Repository.Events;
 
 namespace Hotline.Application.Tests;

+ 3 - 1
src/Hotline.Application.Tests/TestBase.cs

@@ -31,7 +31,9 @@ public class TestBase
     public readonly IThirdAccountRepository _thirdAccountRepository;
 
 
-    public TestBase(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, IThirdIdentiyService thirdIdentiyService, IThirdAccountRepository thirdAccountRepository)
+    public TestBase(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController,
+        IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor,
+        IThirdIdentiyService thirdIdentiyService, IThirdAccountRepository thirdAccountRepository)
     {
         _thirdAccountRepository = thirdAccountRepository;
         _thirdIdentiyService = thirdIdentiyService;

+ 17 - 2
src/Hotline.Application/ExportExcel/ExportApplication.cs

@@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.DependencyInjection;
 using MiniExcelLibs;
 using NPOI.HPSF;
+using SqlSugar;
 using System.Net.Http;
 using System.Reflection;
 using XF.Domain.Dependency;
@@ -42,7 +43,7 @@ namespace Hotline.Application.ExportExcel
             stream.Seek(0, SeekOrigin.Begin);
             return new FileStreamResult(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
             {
-                FileDownloadName = !string.IsNullOrEmpty(name) ? DateTime.Now.ToString("yyyyMMddhhmmss") + ".xlsx" : name
+                FileDownloadName = string.IsNullOrEmpty(name) ? DateTime.Now.ToString("yyyyMMddhhmmss") + ".xlsx" : name
             };
         }
 
@@ -79,7 +80,21 @@ namespace Hotline.Application.ExportExcel
             dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(columnInfos);
 
             var dtos = items
-                .Select(item => _mapper.Map(item, typeT, dynamicClass))
+                .Select(item =>
+                {
+                    var mappedItem = _mapper.Map(item, typeT, dynamicClass);
+                    foreach (var prop in mappedItem.GetType().GetProperties())
+                    {
+                        if (prop.PropertyType == typeof(string))
+                        {
+                            if((string)prop.GetValue(mappedItem) == "True")
+                                prop.SetValue(mappedItem, "是");
+                            if((string)prop.GetValue(mappedItem) == "False")
+                                prop.SetValue(mappedItem, "否");
+                        }
+                    }
+                    return mappedItem;
+                })
                 .Cast<object>()
                 .ToList();
 

+ 1 - 1
src/Hotline.Application/ExportWord/WordHelper.cs

@@ -11,7 +11,7 @@ namespace Hotline.Application.ExportWord
     {
         public static Stream ConvertHtmlToPdf(string htmlContent)
         {
-            var pdf = Pdf
+            var pdf = OpenHtmlToPdf.Pdf
                 .From(htmlContent)
                 .WithObjectSetting("web.defaultEncoding", "utf-8")
                 .Content();

+ 13 - 3
src/Hotline.Application/Handlers/FlowEngine/WorkflowEndHandler.cs

@@ -50,7 +50,7 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
     private readonly ICalcExpireTime _expireTime;
     private readonly IRepository<OrderTerminate> _orderTerminateRepository;
 
-	public WorkflowEndHandler(
+    public WorkflowEndHandler(
         IMapper mapper,
         IKnowledgeDomainService knowledgeDomainService,
         IOrderDomainService orderDomainService,
@@ -106,8 +106,8 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
                 case WorkflowModuleConsts.KnowledgeUpdate://修改知识库
                 case WorkflowModuleConsts.KnowledgeDelete://删除知识库
                 case WorkflowModuleConsts.KnowledgeOffshelf: //下架知识库
-					//var knowledgeWork = await _knowledgeWorkFlowRepository.Queryable().Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
-					var knowledge = await _knowledgeRepository.Queryable().Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
+                                                             //var knowledgeWork = await _knowledgeWorkFlowRepository.Queryable().Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
+                    var knowledge = await _knowledgeRepository.Queryable().Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
                     knowledge.Flowed(workflow.FlowedUserIds, workflow.FlowedOrgIds, workflow.HandlerUsers, workflow.HandlerOrgs);
                     await _knowledgeRepository.UpdateAsync(knowledge, cancellationToken);
                     if (isReviewPass)
@@ -230,6 +230,16 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
 
                     //这里需要判断是否是警情退回
                     orderFlowDto.IsNonPoliceReturn = notification.Dto.External == null ? false : notification.Dto.External.IsPoliceReturn;
+
+                    //如果是泸州,这里需要查询是否有工单延期
+                    if (_appOptions.Value.IsLuZhou)
+                    {
+                        var orderDelay = await _orderDelayRepository.Queryable().Where(p => p.OrderId == order.Id && p.DelayState == EDelayState.Pass)
+                                .OrderByDescending(p => p.ApplyDelayTime)
+                                .FirstAsync();
+                        orderFlowDto.OrderSearchDelay = _mapper.Map<OrderSearchDelayDto>(orderDelay);
+                    }
+
                     await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderFiled, orderFlowDto, cancellationToken: cancellationToken);
                     if (_systemSettingCacheManager.Snapshot)
                     {

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

@@ -20,6 +20,7 @@
     <ProjectReference Include="..\Hotline.Ai.XingTang\Hotline.Ai.XingTang.csproj" />
     <ProjectReference Include="..\Hotline.Application.Contracts\Hotline.Application.Contracts.csproj" />
     <ProjectReference Include="..\Hotline.NewRock\Hotline.NewRock.csproj" />
+    <ProjectReference Include="..\Hotline.Pdf\Hotline.Pdf.csproj" />
     <ProjectReference Include="..\Hotline.Repository.SqlSugar\Hotline.Repository.SqlSugar.csproj" />
     <ProjectReference Include="..\Hotline.Wex\Hotline.Wex.csproj" />
     <ProjectReference Include="..\Hotline.XingTang\Hotline.XingTang.csproj" />

+ 83 - 40
src/Hotline.Application/Jobs/XingTangCallsSyncJob.cs

@@ -82,64 +82,107 @@ namespace Hotline.Application.Jobs
             if (!occupyCalls.Any()) return;
             try
             {
-                var calls = _mapper.Map<List<CallNative>>(occupyCalls);
-                //填充user信息
-                var staffNos = calls.Select(d => d.StaffNo).ToList();
-                var users = await _userRepository.Queryable()
-                    .Where(d => staffNos.Contains(d.StaffNo) && d.StaffNo != "0")
-                    .ToListAsync(context.CancellationToken);
-
-                foreach (var call in calls)
+                if (occupyCalls.Any(m => m.CustomerId.IsNullOrEmpty() == true))
                 {
-                    call.Id = await GetCallIdAsync(call.CallNo, context.CancellationToken);
-                    var user = users.FirstOrDefault(d => d.StaffNo == call.StaffNo && d.StaffNo != "0");
-                    if (user is not null)
+                    var calls = _mapper.Map<List<CallNative>>(occupyCalls);
+                    //填充user信息
+                    var staffNos = calls.Select(d => d.StaffNo).ToList();
+                    var users = await _userRepository.Queryable()
+                        .Where(d => staffNos.Contains(d.StaffNo) && d.StaffNo != "0")
+                        .ToListAsync(context.CancellationToken);
+
+                    foreach (var call in calls)
                     {
-                        call.UserId = user.Id;
-                        call.UserName = user.Name;
-                    }
+                        call.Id = await GetCallIdAsync(call.CallNo, context.CancellationToken);
+                        var user = users.FirstOrDefault(d => d.StaffNo == call.StaffNo && d.StaffNo != "0");
+                        if (user is not null)
+                        {
+                            call.UserId = user.Id;
+                            call.UserName = user.Name;
+                        }
 
-                    if (call.RingDuration == 0)
-                    {
-                        if (call.BeginRingTime != null)
+                        if (call.RingDuration == 0)
                         {
-                            if (call.EndRingTime != null)
+                            if (call.BeginRingTime != null)
                             {
-                                call.RingDuration = call.EndRingTime.Value.GetDifferenceSeconds(call.BeginRingTime.Value);
-                            }
-                            else
-                            {
-                                call.RingDuration = call.EndTime.GetDifferenceSeconds(call.BeginRingTime.Value);
+                                if (call.EndRingTime != null)
+                                {
+                                    call.RingDuration = call.EndRingTime.Value.GetDifferenceSeconds(call.BeginRingTime.Value);
+                                }
+                                else
+                                {
+                                    call.RingDuration = call.EndTime.GetDifferenceSeconds(call.BeginRingTime.Value);
+                                }
                             }
                         }
+
+                        if (call.GroupId == "0" && call.CallState != ECallState.IVRNoAccept)
+                        {
+                            call.CallState = ECallState.Invalid;
+                        }
                     }
 
-                    if (call.GroupId == "0" && call.CallState != ECallState.IVRNoAccept)
+                    await _callRepository.AddRangeAsync(calls, context.CancellationToken);
+
+                    // 开关
+                    var unPushTime = _systemSettingCacheManager.CallSyncUnPushDateTime;
+                    calls = calls.Where(m => m.BeginIvrTime >= unPushTime).ToList();
+                    if (calls.Any())
                     {
-                        call.CallState = ECallState.Invalid;
+                        //推省上
+                        await _capPublisher.PublishAsync(EventNames.HotlineCallAdd, calls.Adapt<List<CallNativeDto>>());
                     }
+                    _logger.LogInformation($"旧方法同步通话记录成功,数量:{calls.Count}");
                 }
+                else
+                {
+                    var calls = new List<CallNative>();
 
-                await _callRepository.AddRangeAsync(calls, context.CancellationToken);
+                    foreach (var occupyCall in occupyCalls)
+                    {
+                        var call = occupyCall.Adapt<CallNative>();
+                        call.Id = await GetCallIdAsync(call.CallNo, context.CancellationToken);
+                        var userSplit = occupyCall.CustomerId.Split(':');
+                        call.UserId = userSplit[0];
+                        call.UserName = userSplit[1];
 
-                // 开关
-                var unPushTime = _systemSettingCacheManager.CallSyncUnPushDateTime;
-                calls = calls.Where(m => m.BeginIvrTime >= unPushTime).ToList();
-                if (calls.Any())
-                {
-                    //推省上
-                    await _capPublisher.PublishAsync(EventNames.HotlineCallAdd, calls.Adapt<List<CallNativeDto>>());
+                        if (call.RingDuration == 0)
+                        {
+                            if (call.BeginRingTime != null)
+                            {
+                                if (call.EndRingTime != null)
+                                {
+                                    call.RingDuration = call.EndRingTime.Value.GetDifferenceSeconds(call.BeginRingTime.Value);
+                                }
+                                else
+                                {
+                                    call.RingDuration = call.EndTime.GetDifferenceSeconds(call.BeginRingTime.Value);
+                                }
+                            }
+                        }
+
+                        if (call.GroupId == "0" && call.CallState != ECallState.IVRNoAccept)
+                        {
+                            call.CallState = ECallState.Invalid;
+                        }
+                        calls.Add(call);
+                    }
+
+                    await _callRepository.AddRangeAsync(calls, context.CancellationToken);
+
+                    // 开关
+                    var unPushTime = _systemSettingCacheManager.CallSyncUnPushDateTime;
+                    calls = calls.Where(m => m.BeginIvrTime >= unPushTime).ToList();
+                    if (calls.Any())
+                    {
+                        //推省上
+                        await _capPublisher.PublishAsync(EventNames.HotlineCallAdd, calls.Adapt<List<CallNativeDto>>());
+                    }
+                    _logger.LogInformation($"新方法同步通话记录成功,数量:{calls.Count}");
                 }
-                ////todo
-                //var callIns = calls.Where(d => d.Direction == ECallDirection.In).ToList();
-                //if (callIns.Any())
-                //{
-                //    await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineCallAdd, callIns);
-                //}
             }
             catch (Exception e)
             {
-                //Console.WriteLine(e);
                 _logger.LogError($"获取通话记录异常:{e.Message} \n {e.StackTrace}");
                 foreach (var occupyCall in occupyCalls)
                 {

+ 44 - 21
src/Hotline.Application/Jobs/XingTangTelOperationSyncJob.cs

@@ -14,6 +14,8 @@ using Hotline.CallCenter.Tels;
 using MapsterMapper;
 using Hotline.Users;
 using XF.Domain.Repository;
+using Hotline.Share.Tools;
+using Mapster;
 
 namespace Hotline.Application.Jobs
 {
@@ -44,10 +46,10 @@ namespace Hotline.Application.Jobs
 
         public async Task Execute(IJobExecutionContext context)
         {
-           var xingtangOperations = await _db.Queryable<XingtangSeatOperation>()
-                .Where(d => (d.IsSync == null || !d.IsSync) && (d.Tries == null || d.Tries <= 20))
-                .Take(50)
-                .ToListAsync(context.CancellationToken);
+            var xingtangOperations = await _db.Queryable<XingtangSeatOperation>()
+                 .Where(d => (d.IsSync == null || !d.IsSync) && (d.Tries == null || d.Tries <= 20))
+                 .Take(50)
+                 .ToListAsync(context.CancellationToken);
 
             var occupyOperations = new List<XingtangSeatOperation>();
             foreach (var operation in xingtangOperations)
@@ -63,30 +65,51 @@ namespace Hotline.Application.Jobs
 
             try
             {
-                var operations = _mapper.Map<List<TelOperation>>(occupyOperations);
-                //填充user信息
-                var staffNos = operations
-                    .Where(d=>!string.IsNullOrEmpty(d.StaffNo) && d.StaffNo != "0")
-                    .Select(d => d.StaffNo).ToList();
-                var users = await _userRepository.Queryable()
-                    .Where(d => staffNos.Contains(d.StaffNo))
-                    .ToListAsync(context.CancellationToken);
+                if (occupyOperations.Any(m => m.UserData.IsNullOrEmpty() == true))
+                {
+                    var operations = _mapper.Map<List<TelOperation>>(occupyOperations);
+                    //填充user信息
+                    var staffNos = operations
+                        .Where(d => !string.IsNullOrEmpty(d.StaffNo) && d.StaffNo != "0")
+                        .Select(d => d.StaffNo).ToList();
+                    var users = await _userRepository.Queryable()
+                        .Where(d => staffNos.Contains(d.StaffNo))
+                        .ToListAsync(context.CancellationToken);
+
+                    foreach (var operation in operations)
+                    {
+                        var user = users.FirstOrDefault(d => d.StaffNo == operation.StaffNo);
+                        if (user is not null)
+                        {
+                            operation.UserId = user.Id;
+                            operation.UserName = user.Name;
+                            var work = _userCacheManager.GetWorkByUserNoExp(user.Id);
+                            if (work is not null)
+                                operation.GroupId = work.QueueId;
+                        }
 
-                foreach (var operation in operations)
+                    }
+                    await _telOperationRepository.AddRangeAsync(operations, context.CancellationToken);
+                    _logger.LogInformation($"旧方法同步分机操作记录成功,数量:{operations.Count}");
+                }
+                else
                 {
-                    var user = users.FirstOrDefault(d => d.StaffNo == operation.StaffNo);
-                    if (user is not null)
+                    var operations = new List<TelOperation>();
+                    foreach (var occupyOperation in occupyOperations)
                     {
-                        operation.UserId = user.Id;
-                        operation.UserName = user.Name;
-                        var work = _userCacheManager.GetWorkByUserNoExp(user.Id);
+                        var operation = occupyOperation.Adapt<TelOperation>();
+                        var userSplit = occupyOperation.UserData.Split(':');
+                        operation.UserId = userSplit[0];
+                        operation.UserName = userSplit[1];
+                        var work = _userCacheManager.GetWorkByUserNoExp(operation.UserId);
                         if (work is not null)
                             operation.GroupId = work.QueueId;
+
+                        operations.Add(operation);
                     }
-                    
+                    await _telOperationRepository.AddRangeAsync(operations, context.CancellationToken);
+                    _logger.LogInformation($"新方法同步分机操作记录成功,数量:{operations.Count}");
                 }
-
-                await _telOperationRepository.AddRangeAsync(operations, context.CancellationToken);
             }
             catch (Exception e)
             {

+ 148 - 61
src/Hotline.Application/Knowledge/KnowApplication.cs

@@ -1,6 +1,8 @@
-using Hotline.Application.Tools;
+using DocumentFormat.OpenXml.EMMA;
+using Hotline.Application.Tools;
 using Hotline.Configurations;
 using Hotline.KnowledgeBase;
+using Hotline.Pdf;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Settings;
 using Hotline.Settings.Hotspots;
@@ -34,19 +36,24 @@ namespace Hotline.Application.Knowledge
         private readonly IRepository<KnowledgeApply> _knowledgeApplyRepository;
         private readonly IRepository<KnowledgeWord> _knowledgeWordRepository;
         private readonly IRepository<KnowledgeHotWord> _knowledgeHotWordRepository;
+        private readonly IPdfManager _pdfManager;
         private readonly ISessionContext _sessionContext;
         private readonly IMapper _mapper;
         private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
 
-		/// <summary>
-		/// 
-		/// </summary>
-		/// <param name="knowledgeRepository"></param>
-		/// <param name="knowledgeApplyRepository"></param>
-		/// <param name="sessionContext"></param>
-		/// <param name="knowledgeDomainService"></param>
-		/// <param name="mapper"></param>
-		public KnowApplication(IKnowledgeRepository knowledgeRepository, IOptionsSnapshot<AppConfiguration> appOptions, IRepository<KnowledgeApply> knowledgeApplyRepository, ISessionContext sessionContext, IMapper mapper, IRepository<KnowledgeType> knowledgeTypeRepository, IRepository<Hotspot> hotspotTypeRepository, IRepository<KnowledgeWord> knowledgeWordRepository, IRepository<KnowledgePv> knowledgePvepository, IRepository<KnowledgeHotWord> knowledgeHotWordRepository)
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="knowledgeRepository"></param>
+        /// <param name="knowledgeApplyRepository"></param>
+        /// <param name="sessionContext"></param>
+        /// <param name="knowledgeDomainService"></param>
+        /// <param name="mapper"></param>
+        public KnowApplication(IKnowledgeRepository knowledgeRepository, IOptionsSnapshot<AppConfiguration> appOptions,
+            IRepository<KnowledgeApply> knowledgeApplyRepository, ISessionContext sessionContext, IMapper mapper,
+            IRepository<KnowledgeType> knowledgeTypeRepository, IRepository<Hotspot> hotspotTypeRepository,
+            IRepository<KnowledgeWord> knowledgeWordRepository, IRepository<KnowledgePv> knowledgePvepository,
+            IRepository<KnowledgeHotWord> knowledgeHotWordRepository, IPdfManager pdfManager)
         {
             _knowledgeRepository = knowledgeRepository;
             _knowledgeApplyRepository = knowledgeApplyRepository;
@@ -57,9 +64,10 @@ namespace Hotline.Application.Knowledge
             _knowledgeWordRepository = knowledgeWordRepository;
             _knowledgePvepository = knowledgePvepository;
             _knowledgeHotWordRepository = knowledgeHotWordRepository;
-			_appOptions  = appOptions;
+            _pdfManager = pdfManager;
+            _appOptions = appOptions;
 
-		}
+        }
 
         /// <summary>
         /// 知识库查询
@@ -68,17 +76,17 @@ namespace Hotline.Application.Knowledge
         /// <returns></returns>
         public async Task<(int, IList<KnowledgeDataDto>)> GetKnowList(KnowPagedListDto pagedDto, CancellationToken cancellationToken)
         {
-	        if (!_sessionContext.OrgIsCenter)
-	        {
-		        pagedDto.Attribution = "部门知识库";
-	        }
+            if (!_sessionContext.OrgIsCenter)
+            {
+                pagedDto.Attribution = "部门知识库";
+            }
 
-	        if (_appOptions.Value.IsZiGong || _appOptions.Value.IsLuZhou)
-	        {
-		        pagedDto.Attribution = string.Empty;
-	        }
+            if (_appOptions.Value.IsZiGong || _appOptions.Value.IsLuZhou)
+            {
+                pagedDto.Attribution = string.Empty;
+            }
 
-	        var typeSpliceName = string.Empty;
+            var typeSpliceName = string.Empty;
             var hotspotHotSpotFullName = string.Empty;
             if (!string.IsNullOrEmpty(pagedDto.KnowledgeTypeId))
             {
@@ -90,31 +98,104 @@ namespace Hotline.Application.Knowledge
                 var hotspot = await _hotspotTypeRepository.GetAsync(x => x.Id == pagedDto.HotspotId);
                 hotspotHotSpotFullName = hotspot?.HotSpotFullName;
             }
-            var (total, temp) = await _knowledgeRepository.Queryable(false, false, false)
-                .Includes(x => x.User)
+
+            var query = _knowledgeRepository.Queryable();
+
+            switch (pagedDto.Status)
+            {
+                case EKnowledgeStatusRequest.OnShelf:
+                    query.Where(d => d.Status == EKnowledgeStatus.OnShelf
+                    && (d.ExpiredTime == null || (d.ExpiredTime != null && d.ExpiredTime > DateTime.Now)));
+                    break;
+                case EKnowledgeStatusRequest.OffShelf:
+                    query.Where(d => d.Status == EKnowledgeStatus.OffShelf
+                    || (d.ExpiredTime != null && d.ExpiredTime <= DateTime.Now && d.Status != EKnowledgeStatus.Drafts));
+                    break;
+                case EKnowledgeStatusRequest.Auditing:
+                    query.Where(d => d.Status == EKnowledgeStatus.Auditing);
+                    switch (pagedDto.ModuleCode)
+                    {
+                        case "add":
+                            query.Where(d => d.KnowledgeApproves.Any(x => x.KnowledgeApproveType == EKnowledgeApproveType.Add
+                                                                          && x.KnowledgeApproveStatus == EKnowledgeApproveStatus.Unhandle));
+                            break;
+                        case "update":
+                            query.Where(d => d.KnowledgeApproves.Any(x => x.KnowledgeApproveType == EKnowledgeApproveType.Update
+                                                                          && x.KnowledgeApproveStatus == EKnowledgeApproveStatus.Unhandle));
+                            break;
+                        case "delete":
+                            query.Where(d => d.KnowledgeApproves.Any(x => x.KnowledgeApproveType == EKnowledgeApproveType.Delete
+                                                                          && x.KnowledgeApproveStatus == EKnowledgeApproveStatus.Unhandle));
+                            break;
+                        case "offshelf":
+                            query.Where(d => d.KnowledgeApproves.Any(x => x.KnowledgeApproveType == EKnowledgeApproveType.OffShelf
+                                                                          && x.KnowledgeApproveStatus == EKnowledgeApproveStatus.Unhandle));
+                            break;
+                    }
+                    break;
+                case EKnowledgeStatusRequest.Drafts:
+                    switch (pagedDto.NewDraftsStatus)
+                    {
+                        case EKnowledgeDraftTypeRequest.All:
+                            query.Where(d => (d.Status == EKnowledgeStatus.Drafts && d.CreatorId == _sessionContext.UserId)
+                                             || d.Status == EKnowledgeStatus.Revert);
+                            break;
+                        case EKnowledgeDraftTypeRequest.UnApproval:
+                            query.Where(d => (d.Status == EKnowledgeStatus.Drafts && d.CreatorId == _sessionContext.UserId));
+                            break;
+                        case EKnowledgeDraftTypeRequest.Failed:
+                            query.Where(d => d.Status == EKnowledgeStatus.Revert);
+                            break;
+                        default:
+                            throw new ArgumentOutOfRangeException();
+                    }
+                    break;
+                case EKnowledgeStatusRequest.All:
+                    break;
+                default:
+                    throw new ArgumentOutOfRangeException();
+            }
+
+            if (!_sessionContext.OrgIsCenter)
+                query.Where(x => x.KnowledgeTypes.Any(t => t.Orgs.Any(to => to.Id == _sessionContext.RequiredOrgId)));
+
+
+            var (total, temp) = await query
+                //.Includes(x => x.User)
                 .Includes(x => x.SystemOrganize)
                 .Includes(x => x.SourceOrganize)
+                .Includes(x => x.KnowledgeTypes)
                 .Includes(x => x.HotspotType)
-                .Includes(x => x.Workflow)
-                .Includes(x => x.KnowledgeType)
-                .Where(x => x.IsDeleted == false)
-                .Where(x => x.KnowledgeType.Any(t => t.KnowledgeType.KnowledgeTypeOrgs.Any(to => to.OrgId == _sessionContext.RequiredOrgId) || t.KnowledgeType.KnowledgeTypeOrgs.Any() == false))
-                .Where(x => (x.Status == EKnowledgeStatus.Drafts && x.CreatorId == _sessionContext.UserId) || (x.Status != EKnowledgeStatus.Drafts))
+                //.Includes(x => x.Workflow)
+                //.Includes(x => x.KnowledgeType)
+                //.Where(x => x.IsDeleted == false)
+                //.Where(x => x.KnowledgeType.Any(t => t.KnowledgeType.KnowledgeTypeOrgs.Any(to => to.OrgId == _sessionContext.RequiredOrgId)
+                //                                     || t.KnowledgeType.KnowledgeTypeOrgs.Any() == false))
+                //.Where(x => x.KnowledgeType.Any(t => t.KnowledgeType.Orgs.Any(to => to.Id == _sessionContext.RequiredOrgId)
+                //                                     || t.KnowledgeType.Orgs.Any() == false))
+                //.Where(x => x.KnowledgeTypes.Any(t => t.KnowledgeTypeOrgs.Any(to => to.OrgId == _sessionContext.RequiredOrgId)
+                //                                      || t.KnowledgeTypeOrgs.Any() == false))
+                //.Where(x => x.KnowledgeTypes.Any(t => t.Orgs.Any(to => to.Id == _sessionContext.RequiredOrgId)
+                //                                      /*|| t.Orgs.Any() == false*/))
+                //.Where(x => (x.Status == EKnowledgeStatus.Drafts && x.CreatorId == _sessionContext.UserId) || (x.Status != EKnowledgeStatus.Drafts))
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.Title), x => x.Title.Contains(pagedDto.Title!))
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.Keyword), x => x.Title.Contains(pagedDto.Keyword!) || x.CreatorName.Contains(pagedDto.Keyword!) || x.CreatorOrgName.Contains(pagedDto.Keyword!) || x.SourceOrganize.Name.Contains(pagedDto.Keyword!))
-                .WhereIF(pagedDto.Status.HasValue && pagedDto.Status != EKnowledgeStatus.OffShelf && pagedDto.Status != EKnowledgeStatus.NewDrafts && pagedDto.Status != EKnowledgeStatus.All, x => x.Status == pagedDto.Status && ((x.ExpiredTime != null && x.ExpiredTime > DateTime.Now) || x.ExpiredTime == null))
-                .WhereIF(pagedDto.Status.HasValue && pagedDto.Status == EKnowledgeStatus.OffShelf , x => x.Status == pagedDto.Status || (x.ExpiredTime != null && x.ExpiredTime < DateTime.Now && x.Status != EKnowledgeStatus.Drafts))
+                //.WhereIF(pagedDto.Status.HasValue && pagedDto.Status != EKnowledgeStatus.OffShelf && pagedDto.Status != EKnowledgeStatus.NewDrafts && pagedDto.Status != EKnowledgeStatus.All,
+                //    x => x.Status == pagedDto.Status && ((x.ExpiredTime != null && x.ExpiredTime > DateTime.Now) || x.ExpiredTime == null))
+                //.WhereIF(pagedDto.Status.HasValue && pagedDto.Status == EKnowledgeStatus.OffShelf,
+                //    x => x.Status == pagedDto.Status || (x.ExpiredTime != null && x.ExpiredTime < DateTime.Now && x.Status != EKnowledgeStatus.Drafts))
                 .WhereIF(pagedDto.IsPublic.HasValue, x => x.IsPublic == pagedDto.IsPublic)
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.Summary), x => x.Summary != null && x.Summary.Contains(pagedDto.Summary!))
-                .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.StartsWith(typeSpliceName)))
+                //.WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.StartsWith(typeSpliceName)))
+                .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeTypes.Any(t => t.SpliceName.StartsWith(typeSpliceName)))
                 .WhereIF(!string.IsNullOrEmpty(hotspotHotSpotFullName), x => x.HotspotType.HotSpotFullName.EndsWith(hotspotHotSpotFullName!))
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.CreateOrgId), x => x.CreatorOrgId != null && x.CreatorOrgId.StartsWith(pagedDto.CreateOrgId!))
-                .WhereIF(!string.IsNullOrEmpty(pagedDto.ModuleCode), x => x.Workflow.ModuleCode == pagedDto.ModuleCode)
-                .WhereIF(pagedDto.Status ==  EKnowledgeStatus.NewDrafts ,x=>x.Status == EKnowledgeStatus.Drafts || x.Status == EKnowledgeStatus.Revert)
-                .WhereIF(pagedDto.NewDraftsStatus is EKnowledgeStatus.Drafts , x=>x.Status == EKnowledgeStatus.Drafts)
-                .WhereIF(pagedDto.NewDraftsStatus is EKnowledgeStatus.Revert, x => x.Status == EKnowledgeStatus.Revert)
+                //.WhereIF(!string.IsNullOrEmpty(pagedDto.ModuleCode), x => x.Workflow.ModuleCode == pagedDto.ModuleCode)
+                //.WhereIF(pagedDto.Status == EKnowledgeStatus.NewDrafts, x => x.Status == EKnowledgeStatus.Drafts || x.Status == EKnowledgeStatus.Revert)
+                //.WhereIF(pagedDto.NewDraftsStatus is EKnowledgeStatus.Drafts, x => x.Status == EKnowledgeStatus.Drafts)
+                //.WhereIF(pagedDto.NewDraftsStatus is EKnowledgeStatus.Revert, x => x.Status == EKnowledgeStatus.Revert)
 
-                .WhereIF(pagedDto.CreationStartTime.HasValue,x=>x.CreationTime>= pagedDto.CreationStartTime)
+                .WhereIF(pagedDto.CreationStartTime.HasValue, x => x.CreationTime >= pagedDto.CreationStartTime)
                 .WhereIF(pagedDto.CreationEndTime.HasValue, x => x.CreationTime <= pagedDto.CreationEndTime)
 
                   .WhereIF(pagedDto.StartOnShelfTime.HasValue, x => x.OnShelfTime >= pagedDto.StartOnShelfTime)
@@ -125,9 +206,9 @@ namespace Hotline.Application.Knowledge
 
                   .WhereIF(pagedDto.StartUpdateTime.HasValue, x => x.LastModificationTime >= pagedDto.StartUpdateTime)
                   .WhereIF(pagedDto.EndUpdateTime.HasValue, x => x.LastModificationTime <= pagedDto.EndUpdateTime)
-                .WhereIF(!string.IsNullOrEmpty(pagedDto.Attribution),x=>x.Attribution == pagedDto.Attribution)
-				.OrderByDescending(d => d.CreationTime)
-                .ToPagedListAsync(pagedDto.PageIndex, pagedDto.PageSize, cancellationToken);
+                .WhereIF(!string.IsNullOrEmpty(pagedDto.Attribution), x => x.Attribution == pagedDto.Attribution)
+                .OrderByDescending(d => d.CreationTime)
+                .ToPagedListAsync(pagedDto, cancellationToken);
             //返回数据
             return (total, _mapper.Map<IList<KnowledgeDataDto>>(temp));
         }
@@ -214,7 +295,7 @@ namespace Hotline.Application.Knowledge
 
             var tasks = knowList.Select(async item =>
             {
-                var stream = await Task.Run(() => item.Content.HtmlToStream(dto.FileType), cancellationToken);
+                var stream = await Task.Run(() => item.Content.HtmlToStream(dto.FileType, item.Title, _pdfManager), cancellationToken);
                 return new KeyValuePair<string, Stream>(
                     item.Title + dto.FileType.GetFileExtension(),
                     stream
@@ -238,7 +319,7 @@ namespace Hotline.Application.Knowledge
         {
             var keywords = new List<KnowledgeWordOutDto>();
             var splitWords = new Segment().DoSegment(title);
-            for (int i = 0;i < splitWords.Count;i++)
+            for (int i = 0; i < splitWords.Count; i++)
             {
                 var word = splitWords.ElementAt(i);
                 if (word is not { WordType: WordType.SimplifiedChinese, Word.Length: > 1 }) continue;
@@ -267,14 +348,14 @@ namespace Hotline.Application.Knowledge
 
         public async Task<(int, IList<PageViewOutDto>)> GetPageViewListAsync(PageViewInDto dto, CancellationToken requestAborted = default)
         {
-	        var typeSpliceName = string.Empty;
-	        if (!string.IsNullOrEmpty(dto.KnowledgeTypeId))
-	        {
-		        var type = await _knowledgeTypeRepository.GetAsync(x => x.Id == dto.KnowledgeTypeId);
-		        typeSpliceName = type?.SpliceName;
-			}
-
-			var query = _knowledgePvepository.Queryable(includeDeleted: true)
+            var typeSpliceName = string.Empty;
+            if (!string.IsNullOrEmpty(dto.KnowledgeTypeId))
+            {
+                var type = await _knowledgeTypeRepository.GetAsync(x => x.Id == dto.KnowledgeTypeId);
+                typeSpliceName = type?.SpliceName;
+            }
+
+            var query = _knowledgePvepository.Queryable(includeDeleted: true)
                 .LeftJoin<KnowledgeBase.Knowledge>((p, k) => p.KnowledgeCode == k.Code)
                 .LeftJoin<KnowledgeRelationType>((p, k, r) => r.KnowledgeId == k.Id)
                 .WhereIF(dto.Title.NotNullOrEmpty(), (p, k, r) => k.Title.Contains(dto.Title))
@@ -343,19 +424,25 @@ namespace Hotline.Application.Knowledge
 
             if (!_sessionContext.OrgIsCenter)
             {
-	            dto.Attribution = "部门知识库";
+                dto.Attribution = "部门知识库";
             }
 
-            var sugar = _knowledgeRepository
-                .Queryable(false, false, false)
+            var query = _knowledgeRepository.Queryable(false, false, false);
+
+            if (!_sessionContext.OrgIsCenter)
+                query.Where(x => x.KnowledgeTypes.Any(t => t.Orgs.Any(to => to.Id == _sessionContext.RequiredOrgId)));
+
+            var sugar = query
                 .Includes(x => x.User)
                 .Includes(x => x.SystemOrganize)
                 .Includes(x => x.HotspotType)
                 .Includes(x => x.KnowledgeType)
                 .Where(x => x.IsDeleted == false)
                 .Where(x => x.Status == EKnowledgeStatus.OnShelf)
-                .Where(x => x.KnowledgeType.Any(t => t.KnowledgeType.KnowledgeTypeOrgs.Any(to => to.OrgId == _sessionContext.RequiredOrgId) || t.KnowledgeType.KnowledgeTypeOrgs.Any() == false))
-                .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.StartsWith(typeSpliceName)))
+                //.Where(x => x.KnowledgeType.Any(t => t.KnowledgeType.KnowledgeTypeOrgs.Any(to => to.OrgId == _sessionContext.RequiredOrgId)
+                //                                     || t.KnowledgeType.KnowledgeTypeOrgs.Any() == false))
+                //.WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.StartsWith(typeSpliceName)))
+                .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeTypes.Any(t => t.SpliceName.StartsWith(typeSpliceName)))
                 .WhereIF(!string.IsNullOrEmpty(hotspotHotSpotFullName), x => x.HotspotType.HotSpotFullName.EndsWith(hotspotHotSpotFullName!))
                 .WhereIF(!string.IsNullOrEmpty(dto.HotspotName), x => x.HotspotType.HotSpotFullName.EndsWith(dto.HotspotName!))
                 .WhereIF(!string.IsNullOrEmpty(dto.CreateOrgId), x => x.CreatorOrgId != null && x.CreatorOrgId.EndsWith(dto.CreateOrgId!))
@@ -387,13 +474,13 @@ namespace Hotline.Application.Knowledge
             {
                 var keywords = dto.Content!.GetSegment();
                 var exp = Expressionable.Create<KnowledgeBase.Knowledge>();
-                 _knowledgeWordRepository.Queryable()
-                    .Where(m => keywords.Contains(m.Tag) && m.IsEnable == 0)
-                    .Select(m => m.Id)
-                    .ToList()
-                    .ForEach(m => 
-                        exp.Or(x => SqlFunc.JsonArrayAny(x.Keywords, m))
-                    );
+                _knowledgeWordRepository.Queryable()
+                   .Where(m => keywords.Contains(m.Tag) && m.IsEnable == 0)
+                   .Select(m => m.Id)
+                   .ToList()
+                   .ForEach(m =>
+                       exp.Or(x => SqlFunc.JsonArrayAny(x.Keywords, m))
+                   );
                 sugar = sugar.Where(exp.ToExpression());
             }
 

+ 39 - 2
src/Hotline.Application/Mappers/KnowledgeMapperConfigs.cs

@@ -14,9 +14,28 @@ public class KnowledgeMapperConfigs : IRegister
             .Map(d => d.CreationOrgName, x => x.SystemOrganize.Name);
 
         config.NewConfig<Hotline.KnowledgeBase.Knowledge, KnowledgeDataDto>()
-            .Map(d => d.CreatorName, x => x.User.Name)
+            .IgnoreIf((s, d) => s.HotspotType == null, d => d.HotspotType)
+            .IgnoreIf((s, d) => s.HotspotType == null, d => d.HotspotName)
+            .Map(d => d.CreatorName, x => x.CreatorName)
             .Map(d => d.HotspotName, x => x.HotspotType.HotSpotFullName)
-            ;
+            .AfterMapping((s, d) => d.KnowledgeTypeText = s.KnowledgeTypes != null && s.KnowledgeTypes.Any()
+                ? string.Join(',', s.KnowledgeTypes.Select(x => x.Name))
+                : string.Empty);
+        ;
+
+        config.ForType<KnowledgeBase.Knowledge, KnowledgeDto>()
+            .Map(d=>d.HotspotName,s=>s.HotspotType.HotSpotFullName)
+            .IgnoreIf((s, d) => s.HotspotType == null, d => d.HotspotName)
+            .IgnoreIf((s, d) => s.SourceOrganize == null, d => d.SourceOrganize)
+            .Ignore(d => d.Collect)
+            .AfterMapping((s, d) => d.KnowledgeTypeText = s.KnowledgeTypes != null && s.KnowledgeTypes.Any()
+                ? string.Join(',', s.KnowledgeTypes.Select(x => x.Name))
+                : string.Empty); 
+
+        config.ForType<KnowledgeBase.Knowledge, KnowledgeInfoDto>()
+            .AfterMapping((s, d) => d.KnowledgeTypeText = s.KnowledgeTypes != null && s.KnowledgeTypes.Any()
+                ? string.Join(',', s.KnowledgeTypes.Select(x => x.Name))
+                : string.Empty);
 
         config.NewConfig<Hotline.KnowledgeBase.Knowledge, KnowledgeApprovalDataDto>()
             .Map(d => d.CreatorName, x => x.User.Name)
@@ -53,7 +72,25 @@ public class KnowledgeMapperConfigs : IRegister
             .Map(d => d.BeginDate, x => x.OnShelfTime)
             .Map(d => d.DisableDate, x => x.ExpiredTime)
             .Map(d => d.UID, x => x.Code)
+            .IgnoreIf((s, d) => s.FileJson == null, d => d.FileJson)
+            .IgnoreIf((s,d)=>s.HotspotType == null, d=>d.CategoryCode)
+            .IgnoreIf((s,d)=>s.HotspotType == null, d=>d.CategoryName)
+            .IgnoreIf((s,d)=>s.User == null, d=>d.PublishPerson)
+            .IgnoreIf((s,d)=>s.SystemOrganize == null, d=>d.OuName)
             ;
+
+        config.ForType<KnowledgeRelationTypeDto, KnowledgeType>()
+            .Map(d => d.Name, s => s.KnowledgeTypeName)
+            .Map(d => d.SpliceName, s => s.KnowledgeTypeSpliceName)
+            .AfterMapping((s, d) => d.Id = s.KnowledgeTypeId)
+            ;
+
+        config.ForType<KnowledgeApprove, KnowledgeApproveDto>()
+            .IgnoreIf((s, d) => s.Approver == null, d => d.Approver);
+
+        config.ForType<KnowledgeImportTemplate, KnowledgeBase.Knowledge>()
+            .Ignore(d => d.IsPublic);
+
         #endregion
     }
 }

+ 6 - 6
src/Hotline.Application/Orders/Handles/OrderScreenHandler/OrderScreenNextWorkflowHandler.cs

@@ -77,12 +77,12 @@ public class OrderScreenNextWorkflowHandler : INotificationHandler<NextStepNotif
                                     screenDto.Content = notification.Dto.Opinion;
                                     screenDto.Files = new List<Share.Dtos.File.FileDto>();
                                 }
-                                if (_appOptions.Value.IsLuZhou)
-                                {
-                                    screenDto.Content = notification.Dto.Opinion;
-                                }
-                                //推省上
-                                _capPublisher.Publish(EventNames.HotlineOrderScreenApply, new PublishScreenDto()
+								if (_appOptions.Value.IsLuZhou)
+								{
+									screenDto.Content = notification.Dto.Opinion;
+								}
+								//推省上
+								_capPublisher.Publish(EventNames.HotlineOrderScreenApply, new PublishScreenDto()
                                 {
                                     Order = screenOrderDto,
                                     Screen = screenDto,

+ 0 - 7
src/Hotline.Application/Orders/IOrderApplication.cs

@@ -49,13 +49,6 @@ namespace Hotline.Application.Orders
         /// </summary>
         /// <param name="dto"></param>
         /// <returns></returns>
-        //ISugarQueryable<Order> GetAboutToExpireAsyncV1(AboutToExpireListDto dto);
-
-        /// <summary>
-        /// 即将超期列表
-        /// </summary>
-        /// <param name="dto"></param>
-        /// <returns></returns>
         ISugarQueryable<Order> GetAboutToExpireAsync(AboutToExpireListDto dto);
 
         //Task<PagedDto<WorkflowOrderDto>> GetAboutToExpireNodeAsync(AboutToExpireListDto dto, CancellationToken cancellationToken);

+ 47 - 21
src/Hotline.Application/Orders/OrderApplication.cs

@@ -100,9 +100,9 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     private readonly IRepository<StatisticsDepartSatisfied> _statisticsDepartSatisfiedRepository;
     private readonly IRepository<OrderTsDetails> _orderTsDetailsRepository;
     private readonly IRepository<KnowledgeQuote> _knowledgeQuoteRepository;
-	private readonly IRepository<OrderSpecial> _orderSpecialRepository;
+    private readonly IRepository<OrderSpecial> _orderSpecialRepository;
 
-	public OrderApplication(
+    public OrderApplication(
         IOrderDomainService orderDomainService,
         IOrderRepository orderRepository,
         IWorkflowDomainService workflowDomainService,
@@ -145,7 +145,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         IOrderDelayRepository orderDelayRepository,
         IRepository<OrderTsDetails> orderTsDetailsRepository,
         IRepository<KnowledgeQuote> knowledgeQuoteRepository,
-		IRepository<OrderSpecial> orderSpecialRepository)
+        IRepository<OrderSpecial> orderSpecialRepository)
     {
         _orderDomainService = orderDomainService;
         _workflowDomainService = workflowDomainService;
@@ -191,7 +191,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         _knowledgeQuoteRepository = knowledgeQuoteRepository;
         _orderSpecialRepository = orderSpecialRepository;
 
-	}
+    }
 
     /// <summary>
     /// 更新工单办理期满时间(延期调用,其他不调用)
@@ -397,7 +397,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
 
     /// <summary>
     /// 即将超期列表
-    /// 查询时间长注释
+    /// 25年1月14日: 因为查询时间太久,注释了;
     /// </summary>
     /// <param name="dto"></param>
     /// <param name="cancellationToken"></param>
@@ -739,7 +739,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     /// <returns></returns>
     public ISugarQueryable<Order> GetPublishOrderList(QueryOrderPublishDto dto)
     {
-        var query = _orderRepository.Queryable();
+        var query = _orderRepository.Queryable().Includes(d => d.OrderTags);
         if (_appOptions.Value.IsLuZhou)
             query = query.Includes(d => d.FwCallRecord);
         //.Includes(d => d.OrderPublish)
@@ -750,7 +750,8 @@ public class OrderApplication : IOrderApplication, IScopeDependency
               //.WhereIF(dto.PubState == EPubState.Pub, d => d.Status >= EOrderStatus.Published)
               //.WhereIF(dto.PubState == EPubState.NoPub, d => d.Status == EOrderStatus.Filed)
               .WhereIF(!string.IsNullOrEmpty(dto.Channel), d => d.SourceChannelCode == dto.Channel)
-              .WhereIF(!string.IsNullOrEmpty(dto.OrderTag), d => d.OrderTagCode == dto.OrderTag!) //工单标签
+              //.WhereIF(!string.IsNullOrEmpty(dto.OrderTag), d => d.OrderTagCode == dto.OrderTag!) //工单标签
+              .WhereIF(!string.IsNullOrEmpty(dto.OrderTag), d => d.OrderTags.Any(ot => ot.DicDataValue == dto.OrderTag)) //工单标签
               .WhereIF(!string.IsNullOrEmpty(dto.CenterToOrgHandlerName), d => d.CenterToOrgHandlerName.Contains(dto.CenterToOrgHandlerName)) //派单人
               .WhereIF(!string.IsNullOrEmpty(dto.NameOrNo), d => d.AcceptorName.Contains(dto.NameOrNo) || d.AcceptorStaffNo.Contains(dto.NameOrNo)) //受理人/坐席
               .WhereIF(!string.IsNullOrEmpty(dto.ActualHandleOrgName), d => d.ActualHandleOrgName.Contains(dto.ActualHandleOrgName)) //接办部门(综合查询模糊)
@@ -1017,13 +1018,13 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         {
             throw UserFriendlyException.SameMessage("该工单存在正在审核中的退回,不能办理");
         }
-		if (await _orderSpecialRepository.AnyAsync(x => x.OrderId == order.Id && x.State == 0,
-		   cancellationToken))
-		{
-			throw UserFriendlyException.SameMessage("该工单存在正在审核中的特提,不能办理");
-		}
+        if (await _orderSpecialRepository.AnyAsync(x => x.OrderId == order.Id && x.State == 0,
+           cancellationToken))
+        {
+            throw UserFriendlyException.SameMessage("该工单存在正在审核中的特提,不能办理");
+        }
 
-		ExpiredTimeWithConfig? expiredTimeConfig = null;
+        ExpiredTimeWithConfig? expiredTimeConfig = null;
         var settingBase = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
         CityBaseConfiguration cityBase = System.Text.Json.JsonSerializer.Deserialize<CityBaseConfiguration>(settingBase);
         if (dto.Workflow.NextHandlers.Any(d => d.Key == cityBase.CityProvince.OrgId || d.Key == cityBase.CityProvinceAssign.OrgId))
@@ -1376,7 +1377,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     {
         var isCenter = _sessionContextProvider.SessionContext.OrgIsCenter;
 
-        var query = _orderRepository.Queryable();
+        var query = _orderRepository.Queryable(canView: false);
         if (!isCenter)
         {
             query.Where(d => SqlFunc.Subqueryable<WorkflowStep>()
@@ -1385,7 +1386,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
                                step.HandlerOrgId.StartsWith(_sessionContextProvider.SessionContext.RequiredOrgId)).Any());
         }
 
-        query = query.Includes(x => x.OrderScreens);
+        query = query.Includes(d => d.OrderScreens).Includes(d => d.OrderTags);
 
         //if (!_appOptions.Value.IsYiBin)
         //{
@@ -1457,14 +1458,15 @@ public class OrderApplication : IOrderApplication, IScopeDependency
              d.SourceChannelCode == "SZMHD" && d.IsProvince == true) //政民互动
          .WhereIF(!string.IsNullOrEmpty(dto.ProvinceChannel) && dto.ProvinceChannel == "3", d => d.Source == ESource.ProvinceStraight &&
              d.SourceChannelCode == "S12345" && d.IsProvince == true) //省12345
-         //.WhereIF(!string.IsNullOrEmpty(dto.ContentRetrieval),d => d.Title.Contains(dto.ContentRetrieval) || d.Content.Contains(dto.ContentRetrieval) || d.FileOpinion.Contains(dto.ContentRetrieval) || d.ActualOpinion.Contains(dto.ContentRetrieval))
-         .WhereIF(!string.IsNullOrEmpty(dto.ContentRetrieval), d=> d.Content.Contains(dto.ContentRetrieval!))
-         .WhereIF(!string.IsNullOrEmpty(dto.FileOption),d=>d.FileOpinion.Contains(dto.FileOption!))
+                                                                      //.WhereIF(!string.IsNullOrEmpty(dto.ContentRetrieval),d => d.Title.Contains(dto.ContentRetrieval) || d.Content.Contains(dto.ContentRetrieval) || d.FileOpinion.Contains(dto.ContentRetrieval) || d.ActualOpinion.Contains(dto.ContentRetrieval))
+         .WhereIF(!string.IsNullOrEmpty(dto.ContentRetrieval), d => d.Content.Contains(dto.ContentRetrieval!))
+         .WhereIF(!string.IsNullOrEmpty(dto.FileOption), d => d.FileOpinion.Contains(dto.FileOption!))
          .WhereIF(dto.IsSgin.HasValue && dto.IsSgin == true, d => d.CurrentStepAcceptTime != null)
          .WhereIF(dto.IsSgin.HasValue && dto.IsSgin == false, d => d.CurrentStepAcceptTime == null)
          .WhereIF(dto.FiledType is FiledType.CenterFiled, d => d.FileOrgIsCenter == true)//d => d.ProcessType == EProcessType.Zhiban
          .WhereIF(dto.FiledType is FiledType.OrgFiled, d => d.FileOrgIsCenter == false)//d => d.ProcessType == EProcessType.Jiaoban
-         .WhereIF(!string.IsNullOrEmpty(dto.OrderTagCode), d => d.OrderTagCode == dto.OrderTagCode)
+                                                                                       //.WhereIF(!string.IsNullOrEmpty(dto.OrderTagCode), d => d.OrderTagCode == dto.OrderTagCode)// 工单标签
+         .WhereIF(!string.IsNullOrEmpty(dto.OrderTagCode), d => d.OrderTags.Any(ot => ot.DicDataValue == dto.OrderTagCode)) //工单标签
          .WhereIF(!string.IsNullOrEmpty(dto.FocusOnEvents), d => SqlFunc.SplitIn(d.FocusOnEvents, dto.FocusOnEvents))
          .OrderByIF(string.IsNullOrEmpty(dto.SortField), d => d.CreationTime, OrderByType.Desc)//默认排序时间为创建时间
          .OrderByIF(dto is { SortField: "no", SortRule: 0 }, d => d.No, OrderByType.Asc) //工单编号升序
@@ -1842,7 +1844,30 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             .WhereIF(dto.TypeCode != null && dto.TypeCode == 1, x => x.OrderVisit.Order.IdentityType == EIdentityType.Citizen)
             .WhereIF(dto.TypeCode != null && dto.TypeCode == 2, x => x.OrderVisit.Order.IdentityType == EIdentityType.Enterprise)
             .WhereIF(IsCenter == false, x => x.VisitOrgCode.StartsWith(_sessionContextProvider.SessionContext.OrgId))
-            .WhereIF(dto.VisitType != null, x => x.OrderVisit.VisitType == dto.VisitType);
+            .WhereIF(dto.VisitType != null, x => x.OrderVisit.VisitType == dto.VisitType)
+
+            .WhereIF(!string.IsNullOrEmpty(dto.Title), x => x.OrderVisit.Order.Title.Contains(dto.Title))                       // 工单标题
+            .WhereIF(!string.IsNullOrEmpty(dto.No), x => x.OrderVisit.Order.No == dto.No)                                       // 工单编号
+            .WhereIF(dto.CreationTimeStart.HasValue, x => x.OrderVisit.Order.CreationTime >= dto.CreationTimeStart)             // 受理时间开始
+            .WhereIF(dto.CreationTimeEnd.HasValue, x => x.OrderVisit.Order.CreationTime <= dto.CreationTimeEnd)                 // 受理时间结束
+            .WhereIF(dto.OrgVisitStatisticsType.HasValue && dto.OrgVisitStatisticsType == EOrgVisitStatisticsType.CallCenter,   // 部门分类 中心归档
+                         x => x.OrderVisit.Order.ActualHandleOrgCode == OrgSeedData.CenterId)
+            .WhereIF(dto.OrgVisitStatisticsType.HasValue && dto.OrgVisitStatisticsType == EOrgVisitStatisticsType.Org,          // 部门分类 部门归档
+                         x => x.OrderVisit.Order.ActualHandleOrgCode != OrgSeedData.CenterId)
+            .WhereIF(!string.IsNullOrEmpty(dto.Channel), x => x.OrderVisit.Order.SourceChannelCode == dto.Channel)              // 来源渠道	
+            .WhereIF(!string.IsNullOrEmpty(dto.VisitUser), x => x.OrderVisit.Employee.Name.Contains(dto.VisitUser))             // 回访人
+            .WhereIF(dto.OrgProcessingResults != null,                                                                          // 办件结果
+                         dto.AttitudeType == EAttitudeType.ProcessingResult ?
+                         x => dto.OrgProcessingResults.Contains(SqlFunc.JsonField(x.OrgProcessingResults, "Key")) :
+                         x => dto.OrgProcessingResults.Contains(SqlFunc.JsonField(x.OrgHandledAttitude, "Key")))
+            .WhereIF(!string.IsNullOrEmpty(dto.Hotspot), x => x.OrderVisit.Order.HotspotSpliceName != null &&
+                         x.OrderVisit.Order.HotspotSpliceName.Contains(dto.Hotspot))                                            // 热点分类
+            .WhereIF(!string.IsNullOrEmpty(dto.OrgId), x => x.VisitOrgCode == dto.OrgId)                                        // 回访部门
+            .WhereIF(dto.ActualHandleTimeStart.HasValue, x => x.OrderVisit.Order.ActualHandleTime >= dto.ActualHandleTimeStart) // 办结时间开始
+            .WhereIF(dto.ActualHandleTimeEnd.HasValue, x => x.OrderVisit.Order.ActualHandleTime <= dto.ActualHandleTimeEnd)     // 办结时间结束
+            .WhereIF(dto.IsProvinceOrder.HasValue && dto.IsProvinceOrder == true, x => x.OrderVisit.Order.Source == ESource.ProvinceStraight)  //省/市工单
+            .WhereIF(dto.IsProvinceOrder.HasValue && dto.IsProvinceOrder == false, x => x.OrderVisit.Order.Source != ESource.ProvinceStraight) //省/市工单
+            .WhereIF(!string.IsNullOrEmpty(dto.ContentRetrieval), x => x.VisitContent.Contains(dto.ContentRetrieval!));         // 回访内容检索
 
         var data = new List<VisitAndOrgSatisfactionStatisticsDto>();
         var oldData = new List<VisitAndOrgSatisfactionStatisticsDto>();
@@ -3077,6 +3102,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
                 d => d.Visit.VisitTime >= dto.VisitTime && d.Visit.VisitTime <= dto.EndVisitTime)
             .WhereIF(!string.IsNullOrEmpty(dto.Contact), d => d.Order!.Contact! == dto.Contact!)
             .WhereIF(!string.IsNullOrEmpty(dto.FromPhone), d => d.Order!.FromPhone! == dto.FromPhone!)
+            .WhereIF(!string.IsNullOrEmpty(dto.CurrentStepName), d => !SqlFunc.IsNullOrEmpty(d.WorkflowId) && d.Workflow.ActualHandleStepName == dto.CurrentStepName) //当前节点
             .OrderByIF(dto is { SortRule: 0, SortField: "creationTime" }, d => d.CreationTime, OrderByType.Asc)
             .OrderByIF(dto is { SortRule: 1, SortField: "creationTime" } || dto.SortRule is null, d => d.CreationTime, OrderByType.Desc);
     }
@@ -3290,7 +3316,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             .WhereIF(dto.IsProvince != null && dto.IsProvince == true, d => d.Order.IsProvince == true)
             .WhereIF(dto.IsProvince != null && dto.IsProvince == false, d => d.Order.IsProvince == false)
             .WhereIF(dto.IsEffectiveAiVisit != null, d => d.IsEffectiveAiVisit == dto.IsEffectiveAiVisit)
-            .WhereIF(!string.IsNullOrEmpty(dto.FromPhone), d => d.Order.FromPhone == dto.FromPhone)
+            .WhereIF(!string.IsNullOrEmpty(dto.FromPhone), d => d.Order.FromPhone.Contains(dto.FromPhone!))
             .WhereIF(!string.IsNullOrEmpty(dto.Contact), d => d.Order.Contact == dto.Contact)
             .WhereIF(dto.VoiceEvaluate.Any(), d => d.OrderVisitDetails.Any(m => dto.VoiceEvaluate.Contains(m.VoiceEvaluate.Value)))
             .WhereIF(dto.SeatEvaluate.Any(), d => d.OrderVisitDetails.Any(m => dto.SeatEvaluate.Contains(m.SeatEvaluate.Value)))

+ 3 - 0
src/Hotline.Application/Quality/IQualityApplication.cs

@@ -17,6 +17,9 @@ namespace Hotline.Application.Quality
 		ISugarQueryable<Hotline.Quality.Quality> SeatsQualityAnalyse(PagedKeywordRequest dto, CancellationToken cancellationToken);
 
 		ISugarQueryable<QualityOrderOverviewDto> QualityOrderOverview(QualityWorkAnalysisRequest dto, int allOrderNum, CancellationToken cancellationToken);
+		ISugarQueryable<SeatsQualityGradeAnalyseDto> SeatsQualityGradeAnalyse(QualityWorkAnalysisRequest dto, CancellationToken cancellationToken);
+
+		Task<List<MonthQualityGradeDto>> MonthQualityGrade(QualityWorkAnalysisRequest dto, CancellationToken cancellationToken);
 
 	}
 }

Plik diff jest za duży
+ 13 - 4
src/Hotline.Application/Quality/QualityApplication.cs


+ 4 - 1
src/Hotline.Application/Snapshot/ISnapshotBulletinApplication.cs

@@ -1,4 +1,6 @@
-using System;
+using Hotline.Share.Dtos.Snapshot;
+using SqlSugar;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
@@ -14,4 +16,5 @@ public interface ISnapshotBulletinApplication
     /// <param name="sHtmlText"></param>
     /// <returns></returns>
     string GetSiteUrls(string sHtmlText);
+    ISugarQueryable<SnapshotBulletinItemsOutDto> QueryBulletinItems(SnapshotBulletinItemsInDto dto);
 }

+ 7 - 4
src/Hotline.Application/Snapshot/IndustryApplication.cs

@@ -3,6 +3,7 @@ using Hotline.Caching.Interfaces;
 using Hotline.File;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Settings;
+using Hotline.Share.Attributes;
 using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.Snapshot;
 using Hotline.Share.Tools;
@@ -72,7 +73,7 @@ public class IndustryApplication : IIndustryApplication, IScopeDependency
         return id;
     }
 
-    [Description("行业集合")]
+    [ExportExcel("行业集合")]
     public ISugarQueryable<IndustryItemsOutDto> GetIndustres(IndustryListInDto dto)
     {
         var query = _industryRepository.Queryable()
@@ -124,7 +125,7 @@ public class IndustryApplication : IIndustryApplication, IScopeDependency
     }
 
     #region 行业线索
-    [Description("行业线索")]
+    [ExportExcel("行业线索")]
     public ISugarQueryable<IndustryCaseItemOutDto> GetIndustryCaseItems(IndustryCaseItemInDto dto)
     {
         var query = _industryCaseRepository.Queryable()
@@ -182,7 +183,7 @@ public class IndustryApplication : IIndustryApplication, IScopeDependency
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    [Description("行业模板")]
+    [ExportExcel("行业审批短信")]
     public ISugarQueryable<SnapshotSMSTemplateItemsOutDto> GetSMSTemplates(SnapshotSMSTemplateItemsInDto dto)
     {
         var query = _snapshotSMSTemplateRepository.Queryable()
@@ -245,6 +246,7 @@ public class IndustryApplication : IIndustryApplication, IScopeDependency
     /// <param name="dto"></param>
     /// <returns></returns>
     /// <exception cref="NotImplementedException"></exception>
+    [ExportExcel("区域从业人员")]
     public ISugarQueryable<PractitionerItemsOutDto> GetPractitionerItems(PractitionerItemsInDto dto)
     {
         var query = _practitionerRepository.Queryable()
@@ -315,7 +317,7 @@ public class IndustryApplication : IIndustryApplication, IScopeDependency
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    /// <exception cref="NotImplementedException"></exception>
+    [ExportExcel("志愿者名单")]
     public ISugarQueryable<VolunteerItemsOutDto> GetVolunteerItems(VolunteerItemsInDto dto)
     {
         var query = _volunteerRepository.Queryable()
@@ -379,6 +381,7 @@ public class IndustryApplication : IIndustryApplication, IScopeDependency
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
+    [ExportExcel("志愿者上报")]
     public ISugarQueryable<VolunteerReportItemsOutDto> GetVolunteerReportItems(VolunteerReportItemsInDto dto)
     {
         var query = _volunteerReportRepository.Queryable()

+ 2 - 0
src/Hotline.Application/Snapshot/InviteCodeApplication.cs

@@ -7,6 +7,7 @@ using Mapster;
 using SqlSugar;
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -64,6 +65,7 @@ public class InviteCodeApplication : IInviteCodeApplication, IScopeDependency
         return _inviteCodeRepository.Queryable();
     }
 
+    [Description("邀请码统计")]
     public ISugarQueryable<InviteCodeStatisticOutDto> GetInviteCodeStatisticAsync(GetInviteCodeStatisticInDto dto)
     {
         dto.ValidateObject();

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

@@ -1,4 +1,5 @@
 using Hotline.Orders;
+using Hotline.Share.Attributes;
 using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.Snapshot;
 using Hotline.Share.Enums.Order;
@@ -207,7 +208,7 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    [Description("市民红包审批")]
+    [ExportExcel("市民红包审批")]
     public ISugarQueryable<SnapshotOrderAuditItemsOutDto> GetRedPackAuditItems(SnapshotOrderAuditItemsInDto dto)
     {
         ERedPackAuditStatus? status = null;
@@ -285,7 +286,7 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    [Description("网格员红包审批")]
+    [ExportExcel("网格员红包审批")]
     public ISugarQueryable<SnapshotOrderGuiderAuditItemsOutDto> GetRedPackGuiderAuditItems(SnapshotOrderGuiderAuditItemsInDto dto)
     {
         var areaCode = _sessionContext.OrgAreaCode;
@@ -434,7 +435,7 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    [Description("市民红包发放记录")]
+    [ExportExcel("市民红包发放记录")]
     public ISugarQueryable<SnapshotRedPackRecordItemsOutDto> GetRedPackRecordItems(SnapshotRedPackRecordItemsInDto dto)
     {
         var query = _redPackRecordRepository.Queryable()
@@ -471,7 +472,7 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
         return order;
     }
 
-    [Description("网格员红包发放记录")]
+    [ExportExcel("网格员红包发放记录")]
     public ISugarQueryable<SnapshotRedPackRecordItemsGuiderOutDto> GetRedPackRecordGuiderItems(SnapshotRedPackRecordItemsGuiderInDto dto)
     {
         var query = _redPackRecordRepository.Queryable()
@@ -492,7 +493,7 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
         return query;
     }
 
-    [Description("红包发放明细")]
+    [ExportExcel("红包发放明细")]
     public ISugarQueryable<SnapshotRedPackRecordSendOutDto> GetRedPackRecordDetail(SnapshotRedPackRecordSendInDto dto)
     {
         var query = _redPackRecordRepository.Queryable()
@@ -524,7 +525,7 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    [Description("补充发放")]
+    [ExportExcel("补充发放")]
     public ISugarQueryable<SnapshotRedPackRecordSupplementItemsOutDto> GetRedPackRecordSupplementItems(SnapshotRedPackRecordSupplementItemsInDto dto)
     {
         var query = _supplementRecordRepository.Queryable()

+ 23 - 1
src/Hotline.Application/Snapshot/SnapshotBulletinApplication.cs

@@ -1,5 +1,10 @@
 using Hotline.Caching.Interfaces;
 using Hotline.Settings;
+using Hotline.Share.Attributes;
+using Hotline.Share.Dtos.Snapshot;
+using Hotline.Snapshot.Interfaces;
+using Microsoft.AspNetCore.Http;
+using SqlSugar;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -13,10 +18,12 @@ public class SnapshotBulletinApplication : ISnapshotBulletinApplication, IScopeD
 {
 
     private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+    private readonly ISnapshotBulletinRepository _bulletinRepository;
 
-    public SnapshotBulletinApplication(ISystemSettingCacheManager systemSettingCacheManager)
+    public SnapshotBulletinApplication(ISystemSettingCacheManager systemSettingCacheManager, ISnapshotBulletinRepository bulletinRepository)
     {
         _systemSettingCacheManager = systemSettingCacheManager;
+        _bulletinRepository = bulletinRepository;
     }
 
     /// <summary>
@@ -65,4 +72,19 @@ public class SnapshotBulletinApplication : ISnapshotBulletinApplication, IScopeD
 
         return sb.ToString();
     }
+
+    [ExportExcel("随手拍公告")]
+    public ISugarQueryable<SnapshotBulletinItemsOutDto> QueryBulletinItems(SnapshotBulletinItemsInDto dto)
+    {
+        var query = _bulletinRepository.Queryable()
+            .Includes(x => x.ExaminMan)
+            .WhereIF(!string.IsNullOrEmpty(dto.SnapshotBulletinTypeName), d => d.SnapshotBulletinTypeName.Contains(dto.SnapshotBulletinTypeName))
+            .WhereIF(!string.IsNullOrEmpty(dto.Title), d => d.Title.Contains(dto.Title))
+            .WhereIF(dto.BeginCreationTime.HasValue, d => d.BulletinTime >= dto.BeginCreationTime)
+            .WhereIF(dto.EndCreationTime.HasValue, d => d.BulletinTime <= dto.EndCreationTime)
+            .WhereIF(dto.State.HasValue, d => d.BulletinState == dto.State)
+            .OrderByDescending(d => d.CreationTime)
+            .Select<SnapshotBulletinItemsOutDto>();
+        return query;
+    }
 }

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

@@ -3,6 +3,7 @@ using Hotline.File;
 using Hotline.FlowEngine.Notifications;
 using Hotline.FlowEngine.Workflows;
 using Hotline.Orders;
+using Hotline.Share.Attributes;
 using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.File;
 using Hotline.Share.Dtos.FlowEngine;
@@ -21,6 +22,7 @@ using NPOI.POIFS.Properties;
 using SqlSugar;
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -169,6 +171,7 @@ public class SnapshotOrderApplication : IOrderSnapshotApplication, IScopeDepende
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
+    [ExportExcel("随手拍公开")]
     public ISugarQueryable<GetOrderSnapshotPublishItemsOutDto> GetOrderSnapshotPublishItems(GetOrderSnapshotPublishItemsInDto dto)
     {
         var query = _orderSnapshotRepository.Queryable(includeDeleted: true)
@@ -200,6 +203,7 @@ public class SnapshotOrderApplication : IOrderSnapshotApplication, IScopeDepende
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
+    [ExportExcel("工单标注")]
     public ISugarQueryable<SignOrderSnapshotItemsOutDto> GetSignOrderSnapshotItems(SignOrderSnapshotItemsInDto dto)
     {
         var query = _orderSnapshotRepository.Queryable()
@@ -352,6 +356,7 @@ public class SnapshotOrderApplication : IOrderSnapshotApplication, IScopeDepende
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
+    [ExportExcel("工单标注日志")]
     public ISugarQueryable<LabelOrderSnapshotLogItemsOutDto> GetLabelOrderSnapshotLogItems(LabelOrderSnapshotLogItemsInDto dto)
     {
         var query = _snapshotLabelLogRepository.Queryable()
@@ -424,6 +429,7 @@ public class SnapshotOrderApplication : IOrderSnapshotApplication, IScopeDepende
 
     }
 
+    [ExportExcel("随手拍工单")]
     public ISugarQueryable<OrderSnapshotItemsOutDto> GetOrderSnapshotItems(OrderSnapshotItemsInDto dto)
     {
         var query = _orderSnapshotRepository.Queryable()
@@ -444,6 +450,7 @@ public class SnapshotOrderApplication : IOrderSnapshotApplication, IScopeDepende
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
+    [ExportExcel("工单标注")]
     public ISugarQueryable<LabeledOrderSnapshotItemsOutDto> GetLabeledOrderSnapshotItems(LabeledOrderSnapshotItemsInDto dto)
     {
         var query = _orderSnapshotRepository.Queryable()

+ 48 - 3
src/Hotline.Application/Tools/StringExtensions.cs

@@ -1,5 +1,8 @@
-using Hotline.Application.ExportWord;
+using System.Text;
+using Hotline.Application.ExportWord;
+using Hotline.Pdf;
 using Hotline.Share.Enums.Article;
+using XF.Domain.Exceptions;
 
 namespace Hotline.Application.Tools;
 public static class StringExtensions
@@ -14,9 +17,51 @@ public static class StringExtensions
         return WordHelper.ConvertHtmlToPdf(value);
     }
 
-    public static Stream HtmlToStream(this string value, EFileType fileType)
+    public static Stream HtmlToPDF(this string value, string? title, IPdfManager? pdfManager)
     {
-        if (fileType == EFileType.pdf) return value.HtmlToPDF();
+        if (pdfManager is null)
+            throw new UserFriendlyException($"非法参数: {nameof(pdfManager)}");
+        var content = value
+                .Replace("&nbsp;", " ")
+                .Replace("&amp;", "&")
+                .Replace("&quot;", "\"")
+                .Replace("&gt;", ">")
+                .Replace("&lt;", "<")
+            ;
+
+        //去除html标签
+        var sb = new StringBuilder();
+        while (!string.IsNullOrEmpty(content) && (content.StartsWith('<') || content.EndsWith('>')))
+        {
+            var indexright = content.IndexOf('>');
+            var indexSecLeft = content.IndexOf('<', indexright);
+            if (indexSecLeft < indexright)
+            {
+                content = content.Remove(0, indexright + 1);
+            }
+            else
+            {
+                var startIndex = indexright + 1;
+                var str = content.Substring(startIndex, indexSecLeft - startIndex);
+                if (!string.IsNullOrEmpty(str)) sb.AppendLine(str);
+                content = content.Remove(0, indexSecLeft);
+            }
+        }
+
+        var sbStr = sb.ToString();
+        if (!string.IsNullOrEmpty(sbStr))
+            content = sbStr;
+
+        var stream = new MemoryStream();
+        pdfManager.GeneratePdf(title, content, stream);
+        stream.Seek(0, SeekOrigin.Begin);
+        return stream;
+    }
+
+    public static Stream HtmlToStream(this string value, EFileType fileType, string? title = null, IPdfManager? pdfManager = null)
+    {
+        //if (fileType == EFileType.pdf) return value.HtmlToPDF();
+        if (fileType == EFileType.pdf) return value.HtmlToPDF(title, pdfManager);
         if (fileType == EFileType.word) return value.HtmlToWord();
         throw new NotImplementedException($"无效的 fileType 入参: {fileType}");
     }

+ 17 - 0
src/Hotline.Pdf/Hotline.Pdf.csproj

@@ -0,0 +1,17 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="QuestPDF" Version="2024.12.2" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\Hotline\Hotline.csproj" />
+  </ItemGroup>
+
+</Project>

+ 52 - 0
src/Hotline.Pdf/QuestPdfManager.cs

@@ -0,0 +1,52 @@
+using Hotline.Ai.Quality;
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace Hotline.Pdf
+{
+    public class QuestPdfManager : IPdfManager
+    {
+        public void GeneratePdf(string title, string content, Stream stream) => CreateDocument(title, content).GeneratePdf(stream);
+
+        public void GeneratePdf(string title, string content, string path) => CreateDocument(title, content).GeneratePdf(path);
+
+
+        private Document CreateDocument(string title, string content)
+        {
+            return Document.Create(container =>
+            {
+                container.Page(page =>
+                {
+                    page.Size(PageSizes.A4);
+                    page.Margin(2, Unit.Centimetre);
+                    page.PageColor(Colors.White);
+                    page.DefaultTextStyle(x => x.FontSize(20));
+
+                    page.Header()
+                        .Text(title)
+                        .SemiBold().FontSize(36).FontColor(Colors.Blue.Medium);
+
+                    page.Content()
+                        .PaddingVertical(1, Unit.Centimetre)
+                        .Column(x =>
+                        {
+                            x.Spacing(20);
+
+                            //x.Item().Text(Placeholders.LoremIpsum());
+                            //x.Item().Image(Placeholders.Image(200, 100));
+                            x.Item().Text(content);
+                        });
+
+                    page.Footer()
+                        .AlignCenter()
+                        .Text(x =>
+                        {
+                            x.Span("Page ");
+                            x.CurrentPageNumber();
+                        });
+                });
+            });
+        }
+    }
+}

+ 16 - 0
src/Hotline.Pdf/QuestPdfManagerStartupExtensions.cs

@@ -0,0 +1,16 @@
+using Microsoft.Extensions.DependencyInjection;
+using QuestPDF.Infrastructure;
+
+namespace Hotline.Pdf;
+
+public static class QuestPdfManagerStartupExtensions
+{
+    public static IServiceCollection AddPdfManager(this IServiceCollection services)
+    {
+        services.AddSingleton<IPdfManager, QuestPdfManager>();
+
+        QuestPDF.Settings.License = LicenseType.Community;
+
+        return services;
+    }
+}

+ 1 - 1
src/Hotline.Repository.SqlSugar/CallCenter/TrCallRecordRepository.cs

@@ -306,7 +306,7 @@ namespace Hotline.Repository.SqlSugar.CallCenter
                     EnterpriseCallInPutthroughCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.IvrDtmf.Substring(x.IvrDtmf.Length - 1, 1) == "2" && x.OnState == EOnState.On, 1, 0)),
                     AiCallInPutthroughCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.Gateway == "82826886", 1, 0)),
                     PersonRingOffCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.IvrDtmf.Substring(x.IvrDtmf.Length - 1, 1) == "1" && x.QueueTims > 0 && x.OnState == EOnState.NoOn, 1, 0)),//个人服务挂断
-                    EnterpriseRingOffCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.IvrDtmf.Substring(x.IvrDtmf.Length - 1, 1) == "2" && x.QueueTims > 0 && x.RingTimes == 0 && x.OnState == EOnState.NoOn, 1, 0)), //企业挂断
+                    EnterpriseRingOffCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.IvrDtmf.Substring(x.IvrDtmf.Length - 1, 1) == "2" && x.QueueTims > 0 && x.OnState == EOnState.NoOn, 1, 0)), //企业挂断
                     IvrRingOffCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.BeginIvrTime.HasValue && !x.BeginQueueTime.HasValue && !x.BeginRingTime.HasValue && x.OnState == EOnState.NoOn, 1, 0)), //IVR挂断
                 })
                 .OrderBy(x=>x.Date);

+ 5 - 3
src/Hotline.Repository.SqlSugar/Orders/OrderRepository.cs

@@ -74,7 +74,7 @@ namespace Hotline.Repository.SqlSugar.Orders
             //弥补AddNav方法没有自动指派到创建人
             order.AssignToCreator(_dataPermissionFilterBuilder.DataPermissionManager);
 
-            await AddNav(order).Include(d => d.OrderExtension).ExecuteCommandAsync();
+            await AddNav(order).Include(d => d.OrderExtension).Include(d=>d.OrderTags).ExecuteCommandAsync();
 
             return order.Id;
         }
@@ -1842,7 +1842,8 @@ namespace Hotline.Repository.SqlSugar.Orders
                     FileOpinion = x.OrderVisit.Order.FileOpinion,
                     FiledTime = x.OrderVisit.Order.FiledTime,
                     VisitOrgName = x.VisitOrgName,
-                    ActualHandleOrgName = x.OrderVisit.Order.ActualHandleOrgName
+                    ActualHandleOrgName = x.OrderVisit.Order.ActualHandleOrgName,
+                    IsProvinceOrder = x.OrderVisit.Order.Source == ESource.ProvinceStraight ? true : false
                 }).MergeTable().OrderByIF(string.IsNullOrEmpty(dto.SortField), x => x.VisitTime, OrderByType.Desc)
                 .OrderByIF(dto is { SortField: "creationTime", SortRule: 0 }, x => x.CreationTime, OrderByType.Asc) //受理时间升序
                 .OrderByIF(dto is { SortField: "creationTime", SortRule: 1 }, x => x.CreationTime, OrderByType.Desc) //受理时间降序
@@ -1906,7 +1907,8 @@ namespace Hotline.Repository.SqlSugar.Orders
                     Content = x.OrderVisit.Order.Content,
                     FileOpinion = x.OrderVisit.Order.FileOpinion,
                     FiledTime = x.OrderVisit.Order.FiledTime,
-                    VisitOrgName = x.VisitOrgName
+                    VisitOrgName = x.VisitOrgName,
+                    IsProvinceOrder = x.OrderVisit.Order.Source == ESource.ProvinceStraight ? true : false
                 }).MergeTable()
                 .OrderByIF(string.IsNullOrEmpty(dto.SortField), x => x.VisitTime, OrderByType.Desc)
                 .OrderByIF(dto is { SortField: "creationTime", SortRule: 0 }, x => x.CreationTime, OrderByType.Asc) //受理时间升序

+ 19 - 0
src/Hotline.Share/Attributes/ExportExcelAttribute.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Attributes;
+public class ExportExcelAttribute : Attribute
+{
+    /// <summary>
+    /// 文件名称
+    /// </summary>
+    public string FileName { get; }
+
+    public ExportExcelAttribute(string fileName)
+    {
+        FileName = fileName;
+    }
+}

+ 19 - 0
src/Hotline.Share/Dtos/Knowledge/ApproveBatchRequest.cs

@@ -0,0 +1,19 @@
+namespace Hotline.Share.Dtos.Knowledge;
+
+public class ApproveBatchRequest
+{
+    /// <summary>
+    /// 待审核记录id
+    /// </summary>
+    public List<string> ApproveIds { get; set; }
+
+    /// <summary>
+    /// 是否通过
+    /// </summary>
+    public bool IsSuccess { get; set; }
+
+    /// <summary>
+    /// 审核意见
+    /// </summary>
+    public string? Opinion { get; set; }
+}

+ 14 - 0
src/Hotline.Share/Dtos/Knowledge/DeleteKnowledgeDto.cs

@@ -0,0 +1,14 @@
+namespace Hotline.Share.Dtos.Knowledge;
+
+public class DeleteKnowledgeDto
+{
+    /// <summary>
+    /// ID
+    /// </summary>
+    public string Id { get; set; }
+
+    /// <summary>
+    /// 删除原因
+    /// </summary>
+    public string Opinion { get; set; }
+}

+ 15 - 0
src/Hotline.Share/Dtos/Knowledge/ExportKnowledgeRequest.cs

@@ -0,0 +1,15 @@
+namespace Hotline.Share.Dtos.Knowledge;
+
+public class ExportKnowledgeRequest
+{
+    /// <summary>
+    /// 知识id
+    /// </summary>
+    public string Id { get; set; }
+
+    /// <summary>
+    /// pdf(暂只支持)
+    /// </summary>
+    public string ExportType { get; set; }
+
+}

+ 98 - 0
src/Hotline.Share/Dtos/Knowledge/KnowPagedListDto.cs

@@ -0,0 +1,98 @@
+using Hotline.Share.Enums.KnowledgeBase;
+using Hotline.Share.Requests;
+
+namespace Hotline.Share.Dtos.Knowledge;
+
+public record KnowPagedListDto : PagedKeywordRequest
+{
+    /// <summary>
+    /// 标题
+    /// </summary>
+    public string? Title { get; set; }
+
+    /// <summary>
+    /// 状态
+    /// </summary>
+    public EKnowledgeStatusRequest Status { get; set; }
+
+    /// <summary>
+    /// 草稿状态下的查询条件
+    /// </summary>
+    public EKnowledgeDraftTypeRequest NewDraftsStatus { get; set; }
+
+    /// <summary>
+    /// 是否公开
+    /// </summary>
+    public bool? IsPublic { get; set; }
+
+    /// <summary>
+    /// 摘要
+    /// </summary>
+    public string? Summary { get; set; }
+
+    /// <summary>
+    /// 部门
+    /// </summary>
+    public string? CreateOrgId { get; set; }
+
+    /// <summary>
+    /// 热点
+    /// </summary>
+    public string? HotspotId { get; set; }
+
+    /// <summary>
+    /// 分类
+    /// </summary>
+    public string? KnowledgeTypeId { get; set; }
+
+    /// <summary>
+    /// 审批类型
+    /// </summary>
+    public string? ModuleCode { get; set; }
+
+    /// <summary>
+    ///   归属
+    /// </summary>
+    public string? Attribution { get; set; }
+
+    /// <summary>
+    /// 创建开始
+    /// </summary>
+    public DateTime? CreationStartTime { get; set; }
+
+    /// <summary>
+    /// 创建结束
+    /// </summary>
+    public DateTime? CreationEndTime { get; set; }
+
+    /// <summary>
+    /// 上架开始
+    /// </summary>
+    public DateTime? StartOnShelfTime { get; set; }
+
+    /// <summary>
+    /// 上架结束
+    /// </summary>
+    public DateTime? EndOnShelfTime { get; set; }
+
+    /// <summary>
+    /// 下架开始
+    /// </summary>
+    public DateTime? StartOffShelfTime { get; set; }
+
+    /// <summary>
+    /// 下架结束
+    /// </summary>
+    public DateTime? EndOffShelfTime { get; set; }
+
+    /// <summary>
+    /// 更新时间开始
+    /// </summary>
+    public DateTime? StartUpdateTime { get; set; }
+
+    /// <summary>
+    /// 更新时间结束
+    /// </summary>
+    public DateTime? EndUpdateTime { get; set; }
+
+}

+ 74 - 0
src/Hotline.Share/Dtos/Knowledge/KnowledgeApproveDto.cs

@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Hotline.KnowledgeBase;
+using Hotline.Share.Dtos.Users;
+using XF.Utility.EnumExtensions;
+
+namespace Hotline.Share.Dtos.Knowledge
+{
+    public class KnowledgeApproveDto
+    {
+        /// <summary>
+        /// 知识Id
+        /// </summary>
+        public string KnowledgeId { get; set; }
+
+        /// <summary>
+        /// 审核类型
+        /// </summary>
+        public EKnowledgeApproveType KnowledgeApproveType { get; set; }
+
+        public string KnowledgeApproveTypeText => KnowledgeApproveType.GetDescription();
+
+        /// <summary>
+        /// 审核状态
+        /// </summary>
+        public EKnowledgeApproveStatus KnowledgeApproveStatus { get; set; }
+        public string KnowledgeApproveStatusText => KnowledgeApproveStatus.GetDescription();
+
+        /// <summary>
+        /// 审批发起意见
+        /// </summary>
+        public string? CreateOpinion { get; set; }
+
+        /// <summary>
+        /// 审批人Id
+        /// </summary>
+        public string? ApproverId { get; set; }
+
+        /// <summary>
+        /// 审核意见
+        /// </summary>
+        public string? ApproveOpinion { get; set; }
+
+        /// <summary>
+        /// 审核时间
+        /// </summary>
+        public DateTime? ApproveTime { get; set; }
+
+        public string Id { get; set; }
+
+        public string? CreatorId { get; set; }
+
+        public string? CreatorName { get; set; }
+
+        public string? CreatorOrgId { get; set; }
+
+        public string? CreatorOrgName { get; set; }
+
+        public int CreatorOrgLevel { get; set; }
+
+        public DateTime CreationTime { get; set; }
+
+
+        public KnowledgeDto Knowledge { get; set; }
+
+        /// <summary>
+        /// 审批人
+        /// </summary>
+        public UserDto Approver { get; set; }
+    }
+}

+ 29 - 28
src/Hotline.Share/Dtos/Knowledge/KnowledgeDataDto.cs

@@ -19,33 +19,34 @@ namespace Hotline.Share.Dtos.Knowledge
         /// </summary>
         public string Title { get; set; }
 
-		/// <summary>
-		/// 知识分类
-		/// </summary>
-		public List<KnowledgeRelationTypeDto> KnowledgeType { get; set; }
+        ///// <summary>
+        ///// 知识分类
+        ///// </summary>
+        //public List<KnowledgeRelationTypeDto> KnowledgeType { get; set; }
 
-		/// <summary>
-		/// 知识分类名称
-		/// </summary>
-		public string KnowledgeTypeText => GetKnowledgeTypeText(KnowledgeType);
+        /// <summary>
+        /// 知识分类名称
+        /// </summary>
+        //public string KnowledgeTypeText => GetKnowledgeTypeText(KnowledgeType);
+        public string KnowledgeTypeText { get; set; }
 
-		/// <summary>
-		/// 获取知识分类名称
-		/// </summary>
-		/// <returns></returns>
-		public string GetKnowledgeTypeText(List<KnowledgeRelationTypeDto> items)
-		{
+        ///// <summary>
+        ///// 获取知识分类名称
+        ///// </summary>
+        ///// <returns></returns>
+        //public string GetKnowledgeTypeText(List<KnowledgeRelationTypeDto> items)
+        //{
 
-			if (KnowledgeType != null && KnowledgeType.Any())
-			{
-				var names = KnowledgeType.Select(x => x.KnowledgeTypeName).ToList();
-				return string.Join(",", names);
-			}
-			return "";
-		}
+        //	if (KnowledgeType != null && KnowledgeType.Any())
+        //	{
+        //		var names = KnowledgeType.Select(x => x.KnowledgeTypeName).ToList();
+        //		return string.Join(",", names);
+        //	}
+        //	return "";
+        //}
 
 
-		public HotspotDto HotspotType {  get; set; }
+        public HotspotDto HotspotType {  get; set; }
 
         /// <summary>
         /// 热点
@@ -142,18 +143,18 @@ namespace Hotline.Share.Dtos.Knowledge
         /// </summary>
         public string? Attribution { get; set; }
 
-		public WorkflowDto? Workflow { get; set; }
+		//public WorkflowDto? Workflow { get; set; }
 
 		/// <summary>
 		/// 来源部门
 		/// </summary>
 		public string? SourceOrganizeId { get; set; }
 
-		/// <summary>
-		/// 来源部门
-		/// </summary>
-		public OrgDto SourceOrganize { get; set; }
-	}
+        /// <summary>
+        /// 来源部门
+        /// </summary>
+        public OrgDto SourceOrganize { get; set; }
+    }
 
     /// <summary>
     /// 审核管理

+ 107 - 125
src/Hotline.Share/Dtos/Knowledge/KnowledgeDto.cs

@@ -12,6 +12,14 @@ using XF.Utility.EnumExtensions;
 
 namespace Hotline.Share.Dtos.Knowledge
 {
+    public class UpdateKnowledgeAndApproveDto : UpdateKnowledgeDto
+    {
+        /// <summary>
+        /// 更新意见
+        /// </summary>
+        public string Opinion { get; set; }
+    }
+
     /// <summary>
     /// 编辑
     /// </summary>
@@ -21,28 +29,22 @@ namespace Hotline.Share.Dtos.Knowledge
         /// ID
         /// </summary>
         public string Id { get; set; }
-
-        /// <summary>
-        /// 标签名称
-        /// </summary>
-        public List<string> Tags { get; set; } = new();
 	}
 
     /// <summary>
     /// 新增知识
     /// </summary>
-    public class AddKnowledgeDto : KnowledgeDto
+    public class AddKnowledgeDto
     {
-
-		/// <summary>
-		/// 知识Code
-		/// </summary>
-		public string Code { get; set; }
+        /// <summary>
+        /// 知识Code
+        /// </summary>
+        public string Code { get; set; }
 
         /// <summary>
-        /// 标签名称
+        /// 知识来源
         /// </summary>
-        public List<string> Tags { get; set; } = new();
+        public string? Source { get; set; }
 
         /// <summary>
         /// 知识归属
@@ -50,68 +52,73 @@ namespace Hotline.Share.Dtos.Knowledge
         public string? Attribution { get; set; }
 
         /// <summary>
-        /// 来源部门
+        /// 知识分类
         /// </summary>
-        public string? SourceOrganizeId { get; set; }
+        public List<KnowledgeRelationTypeDto> KnowledgeType { get; set; }
 
-	}
+        /// <summary>
+        /// 到期时间
+        /// </summary>
+        public DateTime? ExpiredTime { get; set; }
 
-    public class OffShelfKnowledgeDto
-	{
-		/// <summary>
-		/// ID
-		/// </summary>
-		public string Id { get; set; }
+        /// <summary>
+        /// 是否公开
+        /// </summary>
+        public bool IsPublic { get; set; }
 
-		/// <summary>
-		/// 下架原因
-		/// </summary>
-		public string Opinion { get; set; }
-	}
+        /// <summary>
+        /// 热点
+        /// </summary>
+        public string HotspotId { get; set; }
 
-    public class DeleteKnowledgeDto
-    {
-	    /// <summary>
-	    /// ID
-	    /// </summary>
-	    public string Id { get; set; }
+        /// <summary>
+        /// 外部数据(为前端提供级联功能)
+        /// </summary>
+        public string HotspotExternal { get; set; }
+        
+        /// <summary>
+        /// 标题
+        /// </summary>
+        public string Title { get; set; }
 
-	    /// <summary>
-	    /// 删除原因
-	    /// </summary>
-	    public string Opinion { get; set; }
-    }
+        /// <summary>
+        /// 关键词
+        /// </summary>
+        public List<string> Keywords { get; set; }
 
-	/// <summary>
-	/// 查询详情
-	/// </summary>
-	public class KnowledgeInfoDto : KnowledgeDto
-    {
         /// <summary>
-        /// ID
+        /// 内容
         /// </summary>
-        public string Id { get; set; }
+        public string Content { get; set; }
+
+        public List<FileDto> Files { get; set; }
 
         /// <summary>
-        /// 知识Code
+        /// 文档号
         /// </summary>
-        public string Code { get; set; }
+        public string? DocumentNo { get; set; }
 
         /// <summary>
-        /// 热点名称
+        /// 版本说明
         /// </summary>
-        public string HotspotName { get; set; }
+        public string? VersionDescription { get; set; }
 
         /// <summary>
-        /// 流程Id
+        /// 索引号
         /// </summary>
-        public string? WorkflowId { get; set; }
+        public string? IndexNo { get; set; }
 
         /// <summary>
-        /// 是否可办理
+        /// 文号
         /// </summary>
-        public bool? IsCanHandle { get; set; }
-	}
+        public string? FileNo { get; set; }
+
+        /// <summary>
+        /// 预案分类id
+        /// </summary>
+        public string PlanTypeId { get; set; }
+
+    }
 
     public record KnowledgeRelationTypeDto {
 
@@ -136,47 +143,22 @@ namespace Hotline.Share.Dtos.Knowledge
 	/// <summary>
 	/// 基础
 	/// </summary>
-	public class KnowledgeDto : KnowledgeBaseDto
+	public class KnowledgeDto : UpdateKnowledgeDto
 	{
-
-		/// <summary>
-		/// 知识来源
-		/// </summary>
-		public string? Source { get; set; }
-
-		/// <summary>
-		/// 知识分类
-		/// </summary>
-		public List<KnowledgeRelationTypeDto> KnowledgeType { get; set; }
+        /// <summary>
+        /// 来源部门
+        /// </summary>
+        public string? SourceOrganizeId { get; set; }
 
         /// <summary>
         /// 知识分类名称
         /// </summary>
-        public string KnowledgeTypeText => GetKnowledgeTypeText(KnowledgeType);
-
-		/// <summary>
-		/// 获取知识分类名称
-		/// </summary>
-		/// <returns></returns>
-		public string GetKnowledgeTypeText(List<KnowledgeRelationTypeDto> items)
-        {
-
-	        if (KnowledgeType != null && KnowledgeType.Any())
-	        {
-                var names = KnowledgeType.Select(x => x.KnowledgeTypeName).ToList();
-                return string.Join(",", names);
-			}
-	        return "";
-        }
-		/// <summary>
-		/// 热点
-		/// </summary>
-		public string HotspotId { get; set; }
+        public string KnowledgeTypeText { get; set; }
 
         /// <summary>
-        /// 标
+        /// 标签名称
         /// </summary>
-        public string Title { get; set; }
+        public List<string> Tags { get; set; } = new();
 
         /// <summary>
         /// 摘要
@@ -184,76 +166,76 @@ namespace Hotline.Share.Dtos.Knowledge
         public string Summary { get; set; }
 
         /// <summary>
-        /// 内容
+        /// 文档状态
         /// </summary>
-        public string Content { get; set; }
+        public EKnowledgeStatus Status { get; set; }
 
+        public string StatusText => Status.GetDescription();
+        
         /// <summary>
-        /// 是否公开
+        /// 知识归属
         /// </summary>
-        public bool IsPublic { get; set; }
+        public string? Attribution { get; set; }
 
         /// <summary>
-        /// 文档状态,默认草稿
+        /// 浏览量
         /// </summary>
-        public EKnowledgeStatus Status { get; set; } = EKnowledgeStatus.Drafts;
+        public int PageView { get; set; } = 0;
 
-        public string StatusText => Status.GetDescription();
+		/// <summary>
+		/// 搜索量
+		/// </summary>
+		public int? SearchNum { get; set; } = 0;
 
 		/// <summary>
-		/// 到期时间
+		/// 评分
 		/// </summary>
-		public DateTime? ExpiredTime { get; set; }
+		public decimal? Score { get; set; } = decimal.Zero;
 
         /// <summary>
-        /// 关键词
+        /// 评论数
         /// </summary>
-        public List<string> Keywords { get; set; }
+        public int? CommentNum { get; set; } = 0;
+
+        public DateTime? LastModificationTime { get; set; }
 
         /// <summary>
-        /// 附件
+        /// 热点名称
         /// </summary>
-        public List<string> Additions { get; set; }
+        public string HotspotName { get; set; }
+
+        public bool IsDeleted { get; set; }
 
         /// <summary>
-        /// 外部数据(为前端提供级联功能)
+        /// 删除时间
         /// </summary>
-        public string HotspotExternal { get; set; }
+        public DateTime? DeletionTime { get; set; }
+
 
         /// <summary>
-        /// 知识归属
+        /// 创建时间
         /// </summary>
-        public string? Attribution { get; set; }
+        public DateTime CreationTime { get; set; }
 
         /// <summary>
-        /// 浏览量
+        /// 组织Id
         /// </summary>
-        public int PageView { get; set; } = 0;
+        public string? CreatorOrgId { get; set; }
 
-		/// <summary>
-		/// 搜索量
-		/// </summary>
-		public int? SearchNum { get; set; } = 0;
 
-		/// <summary>
-		/// 评分
-		/// </summary>
-		public decimal? Score { get; set; } = decimal.Zero;
+        public string? CreatorOrgName { get; set; }
 
         /// <summary>
-        /// 评论数
+        /// 创建人
         /// </summary>
-        public int? CommentNum { get; set; } = 0;
+        public string? CreatorId { get; set; }
+
+        public string? CreatorName { get; set; }
 
         /// <summary>
         /// 来源部门
         /// </summary>
-        public string? SourceOrganizeId { get; set; }
-
-		/// <summary>
-		/// 来源部门
-		/// </summary>
-		public OrgDto? SourceOrganize { get; set; }
+        public OrgDto? SourceOrganize { get; set; }
 
         public KnowledgeCollectDto Collect { get; set; }
 
@@ -261,6 +243,7 @@ namespace Hotline.Share.Dtos.Knowledge
         /// 关联知识
         /// </summary>
         public List<string> Knowledges { get; set; }
+
 		/// <summary>
 		/// 关联知识对象
 		/// </summary>
@@ -271,11 +254,10 @@ namespace Hotline.Share.Dtos.Knowledge
 		/// </summary>
 		public List<KnowledgeWordDto> KeywordsDto { get; set; }
 
-        public List<FileDto> Files { get; set; }
-
         public List<FileJson>? FileJson { get; set; }
 
-	}
+        public List<KnowledgeTypeDto> KnowledgeTypes { get; set; }
+    }
 
     public class KnowledgeBaseDto {
 		public DateTime? LastModificationTime { get; set; }

+ 32 - 0
src/Hotline.Share/Dtos/Knowledge/KnowledgeInfoDto.cs

@@ -0,0 +1,32 @@
+namespace Hotline.Share.Dtos.Knowledge;
+
+/// <summary>
+/// 查询详情
+/// </summary>
+public class KnowledgeInfoDto : KnowledgeDto
+{
+    /// <summary>
+    /// ID
+    /// </summary>
+    public string Id { get; set; }
+
+    /// <summary>
+    /// 知识Code
+    /// </summary>
+    public string Code { get; set; }
+
+    /// <summary>
+    /// 热点名称
+    /// </summary>
+    public string HotspotName { get; set; }
+
+    /// <summary>
+    /// 流程Id
+    /// </summary>
+    public string? WorkflowId { get; set; }
+
+    /// <summary>
+    /// 是否可办理
+    /// </summary>
+    public bool? IsCanHandle { get; set; }
+}

+ 2 - 111
src/Hotline.Share/Dtos/Knowledge/KnowledgePagedDto.cs

@@ -1,4 +1,5 @@
-using Hotline.Share.Enums.KnowledgeBase;
+using Hotline.KnowledgeBase;
+using Hotline.Share.Enums.KnowledgeBase;
 using Hotline.Share.Requests;
 
 namespace Hotline.Share.Dtos.Knowledge
@@ -88,114 +89,4 @@ namespace Hotline.Share.Dtos.Knowledge
     /// <param name="StartApplyTime"></param>
     /// <param name="EndApplyTime"></param>
     public record KnowledgeDeletelPagedListDto(EKnowledgeWorkFlowStatus? EKnowledgeWorkFlowStatus, DateTime? StartApplyTime, DateTime? EndApplyTime) : PagedKeywordRequest;
-
-    /// <summary>
-    /// 
-    /// </summary>
-    /// <param name="CreateOrgId">部门</param>
-    /// <param name="HotspotId">热点</param>
-    /// <param name="KnowledgeTypeId">分类</param>
-    /// <param name="Status">状态</param>
-    /// <param name="CreationStartTime">创建开始</param>
-    /// <param name="CreationEndTime">创建结束</param>
-    /// <param name="StartOnShelfTime">上架时间-开始</param>
-    /// <param name="EndOnShelfTime">上架时间-结束</param>
-    /// <param name="StartOffShelfTime">下架时间-开始</param>
-    /// <param name="EndOffShelfTime">下架时间-结束</param>
-    //public record KnowPagedListDto(string? CreateOrgId, string? HotspotId, string? KnowledgeTypeId, EKnowledgeStatus? Status, DateTime? CreationStartTime, DateTime? CreationEndTime,
-    //  DateTime? StartOnShelfTime, DateTime? EndOnShelfTime, DateTime? StartOffShelfTime, DateTime? EndOffShelfTime) : PagedKeywordRequest;
-
-    public record KnowPagedListDto : PagedKeywordRequest
-    {
-        /// <summary>
-        /// 标题
-        /// </summary>
-        public string? Title { get; set; }
-
-        /// <summary>
-        /// 状态
-        /// </summary>
-        public EKnowledgeStatus? Status { get; set; }
-
-        /// <summary>
-        /// 草稿状态下的查询条件
-        /// </summary>
-        public EKnowledgeStatus? NewDraftsStatus { get; set; }
-
-        /// <summary>
-        /// 是否公开
-        /// </summary>
-        public bool? IsPublic { get; set; }
-
-        /// <summary>
-        /// 摘要
-        /// </summary>
-        public string? Summary { get; set; }
-
-        /// <summary>
-        /// 部门
-        /// </summary>
-        public string? CreateOrgId { get; set; }
-
-        /// <summary>
-        /// 热点
-        /// </summary>
-        public string? HotspotId { get; set; }
-
-        /// <summary>
-        /// 分类
-        /// </summary>
-        public string? KnowledgeTypeId { get; set; }
-
-        /// <summary>
-        /// 审批类型
-        /// </summary>
-        public string? ModuleCode { get; set; }
-
-        /// <summary>
-        ///   归属
-        /// </summary>
-        public string? Attribution { get; set; }
-
-        /// <summary>
-        /// 创建开始
-        /// </summary>
-        public DateTime? CreationStartTime { get; set; }
-
-        /// <summary>
-        /// 创建结束
-        /// </summary>
-        public DateTime? CreationEndTime { get; set; }
-
-        /// <summary>
-        /// 上架开始
-        /// </summary>
-        public DateTime? StartOnShelfTime { get; set; }
-
-        /// <summary>
-        /// 上架结束
-        /// </summary>
-        public DateTime? EndOnShelfTime { get; set; }
-
-        /// <summary>
-        /// 下架开始
-        /// </summary>
-        public DateTime? StartOffShelfTime { get; set; }
-
-        /// <summary>
-        /// 下架结束
-        /// </summary>
-        public DateTime? EndOffShelfTime { get; set; }
-
-        /// <summary>
-        /// 更新时间开始
-        /// </summary>
-        public DateTime? StartUpdateTime { get; set; }
-
-        /// <summary>
-        /// 更新时间结束
-        /// </summary>
-        public DateTime? EndUpdateTime { get; set; }
-
-    }
 }

+ 14 - 0
src/Hotline.Share/Dtos/Knowledge/OffShelfKnowledgeDto.cs

@@ -0,0 +1,14 @@
+namespace Hotline.Share.Dtos.Knowledge;
+
+public class OffShelfKnowledgeDto
+{
+    /// <summary>
+    /// ID
+    /// </summary>
+    public string Id { get; set; }
+
+    /// <summary>
+    /// 下架原因
+    /// </summary>
+    public string Opinion { get; set; }
+}

+ 11 - 0
src/Hotline.Share/Dtos/Knowledge/PublishBatchRequest.cs

@@ -0,0 +1,11 @@
+namespace Hotline.Share.Dtos.Knowledge;
+
+/// <summary>
+/// 批量更新公开状态
+/// </summary>
+public record PublishBatchRequest
+{
+    public List<string> KnowledgeIds { get; set; }
+
+    public bool IsPublic { get; set; }
+}

+ 28 - 0
src/Hotline.Share/Dtos/Knowledge/QueryKnowledgeApprovePagedRequest.cs

@@ -0,0 +1,28 @@
+using Hotline.KnowledgeBase;
+using Hotline.Share.Requests;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Dtos.Knowledge
+{
+    public record QueryKnowledgeApprovePagedRequest : PagedKeywordRequest
+    {
+        /// <summary>
+        /// 已审批
+        /// </summary>
+        public bool HasApproved { get; set; }
+
+        /// <summary>
+        /// 审核类型
+        /// </summary>
+        public EKnowledgeApproveType? KnowledgeApproveType { get; set; }
+
+        /// <summary>
+        /// 是否公开
+        /// </summary>
+        public bool? IsPublic { get; set; }
+    }
+}

+ 7 - 0
src/Hotline.Share/Dtos/Order/OrderBiDto.cs

@@ -531,6 +531,13 @@ namespace Hotline.Share.Dtos.Order
         /// 接办部门
         /// </summary>
         public string ActualHandleOrgName { get; set; }
+
+        /// <summary>
+        /// 是否省工单(空为全部  true为省工单 false为市工单)
+        /// </summary>
+        public bool IsProvinceOrder { get; set; }
+
+        public string IsProvinceText => IsProvinceOrder ? "省工单" : "市工单";
     }
 
 

+ 19 - 8
src/Hotline.Share/Dtos/Order/OrderDto.cs

@@ -2,6 +2,7 @@
 using Hotline.Share.Dtos.File;
 using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Share.Dtos.Hotspots;
+using Hotline.Share.Dtos.Settings;
 using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Settings;
@@ -91,9 +92,11 @@ namespace Hotline.Share.Dtos.Order
         /// <summary>
         /// 工单标签(自贡)
         /// </summary>
-        public string? OrderTag { get; set; }
+        public string? OrderTag  => OrderTags != null && OrderTags.Any()? string.Join(",", OrderTags.Select(x => x.DicDataName)) : string.Empty; 
 
-        public string? OrderTagCode { get; set; }
+        public string? OrderTagCode => OrderTags != null && OrderTags.Any() ? string.Join(",", OrderTags.Select(x => x.DicDataValue)) : string.Empty; 
+
+		public List<SystemDicDataOutDto>? OrderTags { get; set; }
 
         #region 流程信息
 
@@ -658,7 +661,7 @@ namespace Hotline.Share.Dtos.Order
         {
             try
             {
-                if (Status >= EOrderStatus.Filed && FiledTime != null)
+                if (Status >= EOrderStatus.Filed && FiledTime != null && ExpiredTime != null)
                 {
                     return Math.Round((ExpiredTime.Value - FiledTime.Value).TotalDays, 1) + "天";
                 }
@@ -1024,6 +1027,7 @@ namespace Hotline.Share.Dtos.Order
         public bool? IsReTransact { get; set; }
     }
 
+
     public class UpdateOrderDto : AddOrderDto
     {
         public string Id { get; set; }
@@ -1149,12 +1153,13 @@ namespace Hotline.Share.Dtos.Order
 
         public string Title { get; set; }
 
-        /// <summary>
-        /// 工单标签(自贡)
-        /// </summary>
-        public string? OrderTag { get; set; }
+        //      /// <summary>
+        //      /// 工单标签(自贡)
+        //      /// </summary>
+        //      public string? OrderTag => OrderTags != null && OrderTags.Any() ? string.Join(",", OrderTags.Select(x => x.DicDataName)) : string.Empty;
 
-        public string? OrderTagCode { get; set; }
+
+        //public string? OrderTagCode { get; set; }
 
         #region 热点
 
@@ -1431,6 +1436,12 @@ namespace Hotline.Share.Dtos.Order
         ///知识库引用
         /// </summary>
         public List<Kv>? KnowledgeQuote { get; set; }
+
+        /// <summary>
+        /// 工单标签
+        /// </summary>
+        public List<string>? Tags { get; set; }
+
     }
 
     public record CanLinkCallRecordOrderDto : PagedKeywordRequest

+ 69 - 0
src/Hotline.Share/Dtos/Order/OrderFlowDto.cs

@@ -5,6 +5,7 @@ using System.Text;
 using System.Threading.Tasks;
 using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Share.Dtos.TrCallCenter;
+using Hotline.Share.Enums.Settings;
 
 namespace Hotline.Share.Dtos.Order
 {
@@ -39,5 +40,73 @@ namespace Hotline.Share.Dtos.Order
         /// 非警情退回
         /// </summary>
         public bool IsNonPoliceReturn { get; set; }
+
+        /// <summary>
+        /// 是否存在延期申请
+        /// </summary>
+        public OrderSearchDelayDto? OrderSearchDelay { get; set; }
+    }
+
+    public class OrderSearchDelayDto
+    {
+        /// <summary>
+        /// 延期申请Id
+        /// </summary>
+        public string Id { get; set; }
+
+        /// <summary>
+        /// 流程ID
+        /// </summary>
+        public string? WorkflowId { get; set; }
+
+        /// <summary>
+        /// 工单Id
+        /// </summary>
+        public string OrderId { get; set; }
+
+        /// <summary>
+        /// 工单编号(冗余)
+        /// </summary>
+        public string No { get; set; }
+
+        /// <summary>
+        /// 申请部门Code
+        /// </summary>
+        public string ApplyOrgCode { get; set; }
+
+        /// <summary>
+        /// 申请部门名称
+        /// </summary>
+        public string ApplyOrgName { get; set; }
+
+        /// <summary>
+        /// 申请人
+        /// </summary>
+        public string EmployeeId { get; set; }
+
+        /// <summary>
+        /// 申请人名称
+        /// </summary>
+        public string? EmployeeName { get; set; }
+
+        /// <summary>
+        /// 延期申请时限
+        /// </summary>
+        public int DelayNum { get; set; }
+
+        /// <summary>
+        /// 延期申请单位
+        /// </summary>
+        public ETimeType DelayUnit { get; set; }
+
+        /// <summary>
+        /// 延期申请理由
+        /// </summary>
+        public string DelayReason { get; set; }
+
+        /// <summary>
+        /// 延期申请时间
+        /// </summary>
+        public DateTime ApplyDelayTime { get; set; }
     }
 }

+ 16 - 0
src/Hotline.Share/Dtos/Order/OrderRelationTagDto.cs

@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Dtos.Order
+{
+	public class OrderRelationTagDto
+	{
+		/// <summary>
+		/// 标签ID
+		/// </summary>
+		public string Id { get; set; }
+	}
+}

+ 10 - 0
src/Hotline.Share/Dtos/Order/OrderVisitDto.cs

@@ -1124,6 +1124,16 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         public string? VisitContent { get; set; }
 
+        /// <summary>
+        /// 部门是否联系
+        /// </summary>
+        public bool? IsContact { get; set; }
+
+        /// <summary>
+        /// 是否解决问题
+        /// </summary>
+        public bool? Volved { get; set; }
+
     }
 
     public class OrderVisitProvinceDto

+ 0 - 5
src/Hotline.Share/Dtos/Order/PublishedDto.cs

@@ -687,11 +687,6 @@ public record AboutToExpireListDto : PagedKeywordRequest
     /// 排序方式 // 0 升序 1 降序
     /// </summary>
     public int? SortRule { get; set; } = 0;
-
-    /// <summary>
-    /// 查询批次
-    /// </summary>
-    public int QueryIndex { get; set; }
 }
 
 public class PublishOrderAllDto

+ 6 - 1
src/Hotline.Share/Dtos/Order/QueryOrderDto.cs

@@ -704,7 +704,12 @@ namespace Hotline.Share.Dtos.Order
 		/// 来电电话
 		/// </summary>
 		public string? FromPhone { get; set; }
-	}
+
+        /// <summary>
+        /// 当前办理节点
+        /// </summary>
+        public string? CurrentStepName { get; set; }
+    }
 
     public record MayScreenListDto : PagedKeywordRequest
     {

+ 19 - 0
src/Hotline.Share/Dtos/Quality/BiQualityDto.cs

@@ -86,5 +86,24 @@ namespace Hotline.Share.Dtos.Quality
 
 	public class SeatsQualityGradeAnalyseDto 
 	{
+		public int NinetyGrade { get; set; }
+
+		public int EightyGrade { get; set; }
+
+		public int SeventyGrade { get; set; }
+
+		public int SixtyGrade { get; set; }
+
+		public int FiftyGrade { get; set; }
 	}
+
+	public class MonthQualityGradeDto
+	{
+		public string Month { get; set; }
+
+		public int Num { get; set; }
+	}
+
+
+
 }

+ 228 - 210
src/Hotline.Share/Dtos/Quality/QualityDto.cs

@@ -6,43 +6,43 @@ using XF.Utility.EnumExtensions;
 
 namespace Hotline.Share.Dtos.Quality
 {
-	public class QualityAddDto
-	{
-		/// <summary>
-		/// 工单ID
-		/// </summary>
-		public string OrderId { get; set; }
+    public class QualityAddDto
+    {
+        /// <summary>
+        /// 工单ID
+        /// </summary>
+        public string OrderId { get; set; }
 
-		/// <summary>
-		/// 回访ID
-		/// </summary>
-		public string? VisitId { get; set; }
+        /// <summary>
+        /// 回访ID
+        /// </summary>
+        public string? VisitId { get; set; }
 
-		/// <summary>
-		/// 质检方式
-		/// </summary>
-		public string? Mode { get; set; }
+        /// <summary>
+        /// 质检方式
+        /// </summary>
+        public string? Mode { get; set; }
 
 
-		/// <summary>
-		/// 质检分数
-		/// </summary>
-		public int? Grade { get; set; }
+        /// <summary>
+        /// 质检分数
+        /// </summary>
+        public int? Grade { get; set; }
 
-		/// <summary>
-		/// 质检评价
-		/// </summary>
-		public string? Content { get; set; }
+        /// <summary>
+        /// 质检评价
+        /// </summary>
+        public string? Content { get; set; }
 
-		/// <summary>
-		/// 质检状态
-		/// </summary>
-		public EQualityState State { get; set; }
+        /// <summary>
+        /// 质检状态
+        /// </summary>
+        public EQualityState State { get; set; }
 
-		/// <summary>
-		/// 质检来源
-		/// </summary>
-		public EQualitySource Source { get; set; }
+        /// <summary>
+        /// 质检来源
+        /// </summary>
+        public EQualitySource Source { get; set; }
 
 		/// <summary>
 		/// 质检明细
@@ -53,198 +53,216 @@ namespace Hotline.Share.Dtos.Quality
 	{
 		public string Id { get; set; }
 	}
-	public class DeleteQualityDto
-	{
-		public List<string> Ids { get; set; }
-	}
-	public class QualityDetailDto : QualityBaseDto
-	{
-		/// <summary>
-		/// 工单ID
-		/// </summary>
-		public string QualityId { get; set; }
 
-		/// <summary>
-		/// 扣分时间点- 秒
-		/// </summary>
-		public int Second { get; set; }
-
-		/// <summary>
-		/// 扣分时间点- 秒
-		/// </summary>
-		public int EndSecond { get; set; }
-
-		/// <summary>
-		/// 质检项目名称
-		/// </summary>
-		public string Name { get; set; }
-
-		/// <summary>
-		/// 扣分内容
-		/// </summary>
-		public string? Content { get; set; }
-
-		/// <summary>
-		/// 分值
-		/// </summary>
-		public int Grade { get; set; }
-
-		/// <summary>
-		/// 智能质检
-		/// </summary>
-		public bool? Intelligent { get; set; }
-
-		/// <summary>
-		/// 勾选
-		/// </summary>
-		public bool? Check { get; set; }
-	}
-	public class QualityDto : QualityBaseDto
+	public class TransferUpdate
 	{
 		/// <summary>
-		/// 工单ID
-		/// </summary>
-		public string OrderId { get; set; }
-
-		/// <summary>
-		/// 回访ID
-		/// </summary>
-		public string? VisitId { get; set; }
-
-		/// <summary>
-		/// 质检方式
-		/// </summary>
-		public string? Mode { get; set; }
-
-
-		/// <summary>
-		/// 质检分数
-		/// </summary>
-		public int Grade { get; set; }
-
-		/// <summary>
-		/// 质检评价
-		/// </summary>
-		public string? Content { get; set; }
-
-		/// <summary>
-		/// 质检人
-		/// </summary>
-		public string? UserName { get; set; }
-
-		/// <summary>
-		/// 质检时间
+		/// 质检ID
 		/// </summary>
-		public DateTime? QualityTime { get; set; }
-
-		/// <summary>
-		/// 质检状态
-		/// </summary>
-		public EQualityState State { get; set; }
-
-		public string StateText => State.GetDescription();
-
-		/// <summary>
-		/// 质检状态
-		/// </summary>
-		public EQualitySource Source { get; set; }
-
-		public string SourceText => Source.GetDescription();
-
-		/// <summary>
-		/// 
-		/// </summary>
-		public OrderDto Order { get; set; }
-
-
-		/// <summary>
-		/// 回访
-		/// </summary>
-		public OrderVisitDto Visit { get; set; }
-
-		/// <summary>
-		/// 质检明细
-		/// </summary>
-		public List<QualityDetailDto> QualityDetails { get; set; }
-
-		/// <summary>
-		/// 智能质检
-		/// </summary>
-		public bool AiQuality { get; set; }
-
-		/// <summary>
-		/// 转写状态
-		/// </summary>
-		public EQualityTransferState? TransferState { get; set; }
-
-		public string TransferStateText => TransferState.HasValue ? TransferState.GetDescription() : string.Empty;
-
+		public string Id { get; set; }
 		/// <summary>
 		/// 转写内容
 		/// </summary>
 		public List<Transfer>? Transfer { get; set; }
-
 	}
-	public class QualityBaseDto {
-		public DateTime? LastModificationTime { get; set; }
-
-		public bool IsDeleted { get; set; }
-
-		/// <summary>
-		/// 删除时间
-		/// </summary>
-		public DateTime? DeletionTime { get; set; }
-
-
-		/// <summary>
-		/// 创建时间
-		/// </summary>
-		public DateTime CreationTime { get; set; }
-
-		public string Id { get; set; }
-
-		/// <summary>
-		/// 组织Id
-		/// </summary>
-		public string? CreatorOrgId { get; set; }
-
-
-		public string? CreatorOrgName { get; set; }
-
-		/// <summary>
-		/// 创建人
-		/// </summary>
-		public string? CreatorId { get; set; }
-
-		public string? CreatorName { get; set; }
+	public class DeleteQualityDto
+	{
+		public List<string> Ids { get; set; }
 	}
-	public record QualityListDto : PagedKeywordRequest
+	public class QualityDetailDto : QualityBaseDto
 	{
 		/// <summary>
-		/// 质检状态
-		/// </summary>
-		public EQualityState? State { get; set; }
-
-		/// <summary>
-		/// 质检状态
-		/// </summary>
-		public EQualitySource? Source { get; set; }
-		public DateTime? CreationTimeStart { get; set; }
-		public DateTime? CreationTimeEnd { get; set; }
-
-		/// <summary>
-		/// 最大质检分数
-		/// </summary>
-		public int? MaxGrade {  get; set; }
-
-		/// <summary>
-		///// 最小质检分数
+		/// 工单ID
 		/// </summary>
-		public int? MinGrade { get; set; }
-	}
+		public string QualityId { get; set; }
 
-	public class AiQualityXTDto 
-	{
-		public string Id { get; set; }
-	}
+        /// <summary>
+        /// 扣分时间点- 秒
+        /// </summary>
+        public int Second { get; set; }
+
+        /// <summary>
+        /// 扣分时间点- 秒
+        /// </summary>
+        public int EndSecond { get; set; }
+
+        /// <summary>
+        /// 质检项目名称
+        /// </summary>
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 扣分内容
+        /// </summary>
+        public string? Content { get; set; }
+
+        /// <summary>
+        /// 分值
+        /// </summary>
+        public int Grade { get; set; }
+
+        /// <summary>
+        /// 智能质检
+        /// </summary>
+        public bool? Intelligent { get; set; }
+
+        /// <summary>
+        /// 勾选
+        /// </summary>
+        public bool? Check { get; set; }
+    }
+    public class QualityDto : QualityBaseDto
+    {
+        /// <summary>
+        /// 工单ID
+        /// </summary>
+        public string OrderId { get; set; }
+
+        /// <summary>
+        /// 回访ID
+        /// </summary>
+        public string? VisitId { get; set; }
+
+        /// <summary>
+        /// 质检方式
+        /// </summary>
+        public string? Mode { get; set; }
+
+
+        /// <summary>
+        /// 质检分数
+        /// </summary>
+        public int Grade { get; set; }
+
+        /// <summary>
+        /// 质检评价
+        /// </summary>
+        public string? Content { get; set; }
+
+        /// <summary>
+        /// 质检人
+        /// </summary>
+        public string? UserName { get; set; }
+
+        /// <summary>
+        /// 质检时间
+        /// </summary>
+        public DateTime? QualityTime { get; set; }
+
+        /// <summary>
+        /// 质检状态
+        /// </summary>
+        public EQualityState State { get; set; }
+
+        public string StateText => State.GetDescription();
+
+        /// <summary>
+        /// 质检状态
+        /// </summary>
+        public EQualitySource Source { get; set; }
+
+        public string SourceText => Source.GetDescription();
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public OrderDto Order { get; set; }
+
+
+        /// <summary>
+        /// 回访
+        /// </summary>
+        public OrderVisitDto Visit { get; set; }
+
+        /// <summary>
+        /// 质检明细
+        /// </summary>
+        public List<QualityDetailDto> QualityDetails { get; set; }
+
+        /// <summary>
+        /// 智能质检
+        /// </summary>
+        public bool AiQuality { get; set; }
+
+        /// <summary>
+        /// 转写状态
+        /// </summary>
+        public EQualityTransferState? TransferState { get; set; }
+
+        public string TransferStateText => TransferState.HasValue ? TransferState.GetDescription() : string.Empty;
+
+        /// <summary>
+        /// 转写内容
+        /// </summary>
+        public List<Transfer>? Transfer { get; set; }
+
+    }
+    public class QualityBaseDto
+    {
+        public DateTime? LastModificationTime { get; set; }
+
+        public bool IsDeleted { get; set; }
+
+        /// <summary>
+        /// 删除时间
+        /// </summary>
+        public DateTime? DeletionTime { get; set; }
+
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        public DateTime CreationTime { get; set; }
+
+        public string Id { get; set; }
+
+        /// <summary>
+        /// 组织Id
+        /// </summary>
+        public string? CreatorOrgId { get; set; }
+
+
+        public string? CreatorOrgName { get; set; }
+
+        /// <summary>
+        /// 创建人
+        /// </summary>
+        public string? CreatorId { get; set; }
+
+        public string? CreatorName { get; set; }
+    }
+    public record QualityListDto : PagedKeywordRequest
+    {
+        /// <summary>
+        /// 质检状态
+        /// </summary>
+        public EQualityState? State { get; set; }
+
+        /// <summary>
+        /// 质检状态
+        /// </summary>
+        public EQualitySource? Source { get; set; }
+        public DateTime? CreationTimeStart { get; set; }
+        public DateTime? CreationTimeEnd { get; set; }
+
+        /// <summary>
+        /// 最大质检分数
+        /// </summary>
+        public int? MaxGrade { get; set; }
+
+        /// <summary>
+        /// 最小质检分数
+        /// </summary>
+        public int? MinGrade { get; set; }
+
+        /// <summary>
+        /// 工单编码
+        /// </summary>
+        public string? No { get; set; }
+    }
+
+    public class AiQualityXTDto
+    {
+        public string Id { get; set; }
+    }
 }

+ 1 - 1
src/Hotline.Share/Dtos/QualityExportWord/QualityCertificate.cs

@@ -124,7 +124,7 @@ namespace Hotline.Share.Dtos.QualityExportWord
 		/// <summary>
 		/// 质检分数
 		/// </summary>
-		public int? Grade { get; set; }
+		public string Grade { get; set; }
 
 		/// <summary>
 		/// 质检评价

+ 16 - 0
src/Hotline.Share/Dtos/Snapshot/IndustryDto.cs

@@ -398,6 +398,11 @@ public class IndustryCaseItemOutDto : AddIndustryCaseDto
     /// 网络员发放红包金额(单位:元)
     /// </summary>
     public string GuiderReadPackAmountTxt => GuiderReadPackAmount.ToYuanFinance();
+
+    /// <summary>
+    /// 是否启用
+    /// </summary>
+    public string IsEnableTxt => IsEnable ? "启用" : "禁用";
 }
 
 public class UpdateIndustryCaseDto : AddIndustryCaseDto
@@ -466,6 +471,17 @@ public class SnapshotSMSTemplateItemsOutDto : AddSnapshotSMSTemplateInDto
     /// 行业名称
     /// </summary>
     public string IndustryName { get; set; }
+
+    /// <summary>
+    /// 是否启用
+    /// </summary>
+    public string IsEnableTxt => IsEnable ? "启用" : "禁用";
+
+    /// <summary>
+    /// 是否公用
+    /// </summary>
+    public string IsPublicTxt => IsPublic ? "是" : "否";
+
 }
 
 public class UpdateSnapshotSMSTemplateInDto : AddSnapshotSMSTemplateInDto

+ 45 - 2
src/Hotline.Share/Dtos/Snapshot/OrderDto.cs

@@ -103,9 +103,12 @@ public class AddSnapshotOrderInDto : Position
     public string IndustryId { get; set; }
 
     /// <summary>
-    /// 作业类型
+    /// 作业类型;
+    /// 0: 电焊;
+    /// 1: 气割;
+    /// 2: 其他;
     /// </summary>
-    public EJobType? JobType { get; set; }
+    public int? JobType { get; set; }
 
     /// <summary>
     /// 经营单位类别
@@ -535,6 +538,11 @@ public class SnapshotOrderGuiderAuditItemsOutDto
     /// </summary>
     public bool? IsIssued { get; set; }
 
+    /// <summary>
+    /// 网格员奖励发放结果
+    /// </summary>
+    public string? IsIssuedTxt => IsIssued.HasValue == false ? "" : IsIssued.Value ? "已发放" : "未发放";
+
     /// <summary>
     /// 区域
     /// </summary>
@@ -546,26 +554,51 @@ public class SnapshotOrderGuiderAuditItemsOutDto
     /// </summary>
     public bool? IsRectify { get; set; }
 
+    /// <summary>
+    /// 部门是否整改完成
+    /// </summary>
+    public string? IsRectifyTxt => IsRectify.HasValue == false ? "" : IsRectify.Value ? "是" : "否";
+
     /// <summary>
     /// 部门是否属实
     /// </summary>
     public bool? IsTruthDepartment { get; set; }
 
+    /// <summary>
+    /// 部门是否属实
+    /// </summary>
+    public string? IsTruthDepartmentTxt => IsTruthDepartment.HasValue == false ? "" : IsTruthDepartment.Value ? "是" : "否";
+
     /// <summary>
     /// 网格员是否属实
     /// </summary>
     public bool? IsTruth { get; set; }
 
+    /// <summary>
+    /// 网格员是否属实
+    /// </summary>
+    public string? IsTruthTxt => IsTruth.HasValue == false ? "" : IsTruth.Value ? "是" : "否";
+
     /// <summary>
     /// 是否重复
     /// </summary>
     public bool? IsRepetition { get; set; }
 
+    /// <summary>
+    /// 是否重复
+    /// </summary>
+    public string? IsRepetitionTxt => IsRepetition.HasValue == false ? "" : IsRepetition.Value ? "是" : "否";
+
     /// <summary>
     /// 网格员是否办理
     /// </summary>
     public bool? IsDeal { get; set; }
 
+    /// <summary>
+    /// 网格员是否办理
+    /// </summary>
+    public string? IsDealTxt => IsDeal.HasValue == false ? "" : IsDeal.Value ? "是" : "否";
+
     /// <summary>
     /// 网格E通编号
     /// </summary>
@@ -1167,6 +1200,11 @@ public class SignOrderSnapshotItemsOutDto
     /// </summary>
     public bool? IsSafetyDepartment { get; set; }
 
+    /// <summary>
+    /// 标注状态
+    /// </summary>
+    public string IsSafetyDepartmentTxt => IsSafetyDepartment == true ? "是" : "否";
+
     /// <summary>
     /// 回复内容;
     /// 网格员回复内容
@@ -1255,6 +1293,11 @@ public class GetOrderSnapshotPublishItemsOutDto
     /// 网格员是否属实
     /// </summary>
     public bool IsTruth { get; set; }
+
+    /// <summary>
+    /// 网格员是否属实
+    /// </summary>
+    public string IsTruthTxt => IsTruth ? "是" : "否";
 }
 
 public record GetOrderSnapshotPublishItemsInDto : PagedRequest

+ 2 - 0
src/Hotline.Share/Dtos/Snapshot/RedPackRecordDto.cs

@@ -4,6 +4,7 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
+using System.Text.Json.Serialization;
 using System.Threading.Tasks;
 using XF.Utility.EnumExtensions;
 
@@ -47,6 +48,7 @@ public class SnapshotRedPackRecordSendOutDto
     /// <summary>
     /// OpenId
     /// </summary>
+    [JsonPropertyName("wXOpenId")]
     public string WXOpenId { get; set; }
 
     /// <summary>

+ 28 - 1
src/Hotline.Share/Dtos/WebPortal/ArticleDetailsDto.cs

@@ -21,6 +21,11 @@ namespace Hotline.Share.Dtos.WebPortal
         /// 公告ID
         /// </summary>
         public string Id { get; set; }
+
+        /// <summary>
+        /// 分类名称
+        /// </summary>
+        public string TypeName { get; set; }
     }
 
     public class ArticleIdByNumDto
@@ -58,7 +63,7 @@ namespace Hotline.Share.Dtos.WebPortal
         /// <summary>
         /// 公告范围,不传默认查询门户网站  0:全部;1:微信小程序;2:门户网站;4:市民APP;8:部门APP
         /// </summary>
-        public string? PushRanges {  get; set; }
+        public string? PushRanges { get; set; }
     }
 
     /// <summary>
@@ -121,6 +126,11 @@ namespace Hotline.Share.Dtos.WebPortal
         /// 公告标题
         /// </summary>
         public string NoticeTitle { get; set; }
+
+        /// <summary>
+        /// 分类
+        /// </summary>
+        public string NoticeTypeName { get; set; }
     }
 
     /// <summary>
@@ -190,6 +200,23 @@ namespace Hotline.Share.Dtos.WebPortal
         public IReadOnlyList<ArticleListDto> data { get; set; }
     }
 
+    public class ArticleDetailDataDto
+    {
+        /// <summary>
+        /// 总条数
+        /// </summary>
+        public int Page { get; set; }
+
+        /// <summary>
+        /// 总页数
+        /// </summary>
+        public string NoticeID { get; set; }
+        public string NoticeTitle { get; set; }
+
+        public string NoticeTypeName { get; set; }
+        public DateTime NoticeCreateDate { get; set; }
+    }
+
     /// <summary>
     /// 公告列表
     /// </summary>

+ 14 - 0
src/Hotline.Share/Dtos/WebPortal/WaitVisitListDataDto.cs

@@ -57,5 +57,19 @@
         /// </summary>
         public string? SatisfactionCode { get; set; }
 
+        /// <summary>
+        /// 部门是否联系
+        /// </summary>
+        public bool? IsContact { get; set; }
+
+        /// <summary>
+        /// 是否解决问题
+        /// </summary>
+        public bool? Volved { get; set; }
+
+        /// <summary>
+        /// 不满意原因
+        /// </summary>
+        public List<Kv>? OrgNoSatisfiedReason { get; set; }
     }
 }

+ 33 - 0
src/Hotline.Share/Enums/FlowEngine/EBusinessType.cs

@@ -28,9 +28,42 @@ public enum EBusinessType
     [Description("部门领导节点")]
     DepartmentLeader = 3,
 
+    /// <summary>
+    /// 中心班长
+    /// </summary>
+    [Description("中心班长")]
+    CenterMonitor = 4,
+
+    /// <summary>
+    /// 中心领导
+    /// </summary>
+    [Description("中心领导")]
+    CenterLeader = 5,
+
     /// <summary>
     /// 归档节点
     /// </summary>
     [Description("归档节点")]
     File = 99,
+
+    [Description("其他")]
+    Unknown = 100,
+
+    /// <summary>
+    /// 发布
+    /// </summary>
+    [Description("发布节点")]
+    Publish = 110,
+
+    /// <summary>
+    /// 回访
+    /// </summary>
+    [Description("回访节点")]
+    Visit = 120,
+
+    /// <summary>
+    /// 结束(为了与老系统保持一致,额外添加在回访后的节点)
+    /// </summary>
+    [Description("结束节点(兼容老系统习惯)")]
+    TrashEnd = 130,
 }

+ 18 - 0
src/Hotline.Share/Enums/KnowledgeBase/EKnowledgeApproveStatus.cs

@@ -0,0 +1,18 @@
+using System.ComponentModel;
+
+namespace Hotline.KnowledgeBase;
+
+/// <summary>
+/// 审核状态
+/// </summary>
+public enum EKnowledgeApproveStatus
+{
+    [Description("待审批")]
+    Unhandle = 0,
+
+    [Description("通过")]
+    Successed = 1,
+
+    [Description("不通过")]
+    Failed = 2,
+}

+ 21 - 0
src/Hotline.Share/Enums/KnowledgeBase/EKnowledgeApproveType.cs

@@ -0,0 +1,21 @@
+using System.ComponentModel;
+
+namespace Hotline.KnowledgeBase;
+
+/// <summary>
+/// 审核类型
+/// </summary>
+public enum EKnowledgeApproveType
+{
+    [Description("新增审核")]
+    Add = 0,
+
+    [Description("更新审核")]
+    Update = 1,
+
+    [Description("删除审核")]
+    Delete = 2,
+
+    [Description("下架审核")]
+    OffShelf = 3,
+}

+ 88 - 25
src/Hotline.Share/Enums/KnowledgeBase/EKnowledgeStatus.cs

@@ -7,11 +7,11 @@ namespace Hotline.Share.Enums.KnowledgeBase;
 /// </summary>
 public enum EKnowledgeStatus
 {
-	/// <summary>
-	/// 全部
-	/// </summary>
-	[Description("全部")]
-	All = -1,
+	///// <summary>
+	///// 全部
+	///// </summary>
+	//[Description("全部")]
+	//All = -1,
 
 	/// <summary>
 	/// 待提交
@@ -25,11 +25,11 @@ public enum EKnowledgeStatus
     [Description("审批中")]
     Auditing = 1,
 
-    /// <summary>
-    /// 失败
-    /// </summary>
-    [Description("失败")]
-    Failed = 2,
+    ///// <summary>
+    ///// 失败
+    ///// </summary>
+    //[Description("失败")]
+    //Failed = 2,
 
     /// <summary>
     /// 已上架
@@ -49,21 +49,84 @@ public enum EKnowledgeStatus
 	[Description("已驳回")]
     Revert = 5,
 
-	/// <summary>
-	/// 已过期
-	/// </summary>
-	[Description("已过期")]
-	Overdue = 6,
+    /// <summary>
+    /// 已过期
+    /// </summary>
+    [Description("已过期")]
+    Overdue = 6,
 
-	/// <summary>
-	/// 下架审批中
-	/// </summary>
-	[Description("下架审批中")]
-	OffShelfAudit = 7,
+    ///// <summary>
+    ///// 下架审批中
+    ///// </summary>
+    //[Description("下架审批中")]
+    //OffShelfAudit = 7,
 
-	/// <summary>
-	/// 草稿
-	/// </summary>
-	[Description("草稿")]
-	NewDrafts = 8,
+    ///// <summary>
+    ///// 草稿
+    ///// </summary>
+    //[Description("草稿")]
+    //NewDrafts = 8,
+}
+
+/// <summary>
+/// 知识列表查询状态(兼容以前实现方案提供给前端入参)
+/// </summary>
+public enum EKnowledgeStatusRequest
+{
+    /// <summary>
+    /// 已上架
+    /// </summary>
+    [Description("已上架")]
+    OnShelf = 3,
+
+    /// <summary>
+    /// 已下架
+    /// </summary>
+    [Description("已下架")]
+    OffShelf = 4,
+
+    /// <summary>
+    /// 审批中
+    /// </summary>
+    [Description("审核中")]
+    Auditing = 1,
+
+    /// <summary>
+    /// 待提交
+    /// </summary>
+    [Description("草稿")]
+    Drafts = 8,
+
+    /// <summary>
+    /// 全部
+    /// </summary>
+    [Description("全部")]
+    All = -1,
+}
+
+/// <summary>
+/// 草稿状态查询(兼容以前实现方案提供给前端入参)
+/// </summary>
+public enum EKnowledgeDraftTypeRequest
+{
+    /* new KeyValuePair<int, string>(-1, "全部"),
+       new KeyValuePair<int, string>(0, "待提交"),
+       new KeyValuePair<int, string>(5, "审核不通过"),*/
+    /// <summary>
+    /// 全部
+    /// </summary>
+    [Description("全部")]
+    All = -1,
+
+    /// <summary>
+    /// 待提交
+    /// </summary>
+    [Description("待提交")]
+    UnApproval = 0,
+
+    /// <summary>
+    /// 审核不通过
+    /// </summary>
+    [Description("审核不通过")]
+    Failed = 5
 }

+ 2 - 2
src/Hotline.Share/Enums/Quality/EQuality.cs

@@ -61,13 +61,13 @@ namespace Hotline.Share.Enums.Quality
 		/// 转写成功
 		/// </summary>
 		[Description("转写成功")]
-		Succeed = 1,
+		Succeed = 2,
 
 		/// <summary>
 		/// 转写失败
 		/// </summary>
 		[Description("转写失败")]
-		Lose = 1,
+		Lose = 3,
 	}
 
 	public enum EQualityTransferType {

+ 0 - 29
src/Hotline.Share/Enums/Snapshot/EJobType.cs

@@ -1,29 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Hotline.Share.Enums.Snapshot;
-public enum EJobType
-{
-
-    /// <summary>
-    /// 电焊
-    /// </summary>
-    [Description("电焊")]
-    Electric,
-
-    /// <summary>
-    /// 气割
-    /// </summary>
-    [Description("气割")]
-    Gas,
-
-    /// <summary>
-    /// 其他
-    /// </summary>
-    [Description("其他")]
-    Othoer
-}

+ 1 - 1
src/Hotline.Share/Hotline.Share.csproj

@@ -7,7 +7,7 @@
     <GenerateDocumentationFile>True</GenerateDocumentationFile>
     <NoWarn>$(NoWarn);1591;8618;</NoWarn>
     <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
-    <Version>1.0.114</Version>
+    <Version>1.0.117</Version>
   </PropertyGroup>
 
   <ItemGroup>

+ 82 - 6
src/Hotline.Share/Requests/PagedKeywordRequest.cs

@@ -59,6 +59,82 @@ public record PagedKeywordSonRequest : PagedRequest
     /// 回访方式
     /// </summary>
     public EVisitType? VisitType { get; set; }
+
+    /// <summary>
+    /// 工单标题
+    /// </summary>
+    public string? Title { get; set; }
+
+    /// <summary>
+    /// 工单编码
+    /// </summary>
+    public string? No { get; set; }
+
+    /// <summary>
+    /// 受理开始时间
+    /// </summary>
+    public DateTime? CreationTimeStart { get; set; }
+
+    /// <summary>
+    /// 受理结束时间
+    /// </summary>
+    public DateTime? CreationTimeEnd { get; set; }
+
+    /// <summary>
+    /// 部门分类
+    /// </summary>
+    public EOrgVisitStatisticsType? OrgVisitStatisticsType { get; set; }
+
+    /// <summary>
+    /// 来源渠道
+    /// </summary>
+    public string Channel { get; set; }
+
+    /// <summary>
+    /// 回访人
+    /// </summary>
+    public string? VisitUser { get; set; }
+
+    /// <summary>
+    /// 办件态度类型(1:办件结果; 2:办事态度)
+    /// 默认 办件结果;
+    /// </summary>
+    public EAttitudeType AttitudeType { get; set; } = EAttitudeType.ProcessingResult;
+
+    /// <summary>
+    /// 办件结果
+    /// </summary>
+    public string? OrgProcessingResults { get; set; }
+
+    /// <summary>
+    /// 热点分类
+    /// </summary>
+    public string Hotspot { get; set; }
+
+    /// <summary>
+    /// 回访部门
+    /// </summary>
+    public string OrgId { get; set; }
+
+    /// <summary>
+    /// 办结开始时间
+    /// </summary>
+    public DateTime? ActualHandleTimeStart { get; set; }
+
+    /// <summary>
+    /// 办结结束时间
+    /// </summary>
+    public DateTime? ActualHandleTimeEnd { get; set; }
+
+    /// <summary>
+    /// 是否省工单(空为全部  true为省工单 false为市工单)
+    /// </summary>
+    public bool? IsProvinceOrder { get; set; }
+
+    /// <summary>
+    /// 内容检索(回访内容)
+    /// </summary>
+    public string? ContentRetrieval { get; set; }
 }
 public record VisitAndHotspotPagedKeywordRequest : PagedKeywordRequest
 {
@@ -1402,16 +1478,16 @@ public record SeatSatisfactionOrderVisitRequest : PagedKeywordRequest
 
     public string? Title { get; set; }
 
-    public string? No {  get; set; }
+    public string? No { get; set; }
 }
 
-public record QualityWorkAnalysisRequest :PagedKeywordRequest
+public record QualityWorkAnalysisRequest : PagedKeywordRequest
 {
 
-	/// <summary>
-	/// 最大质检分数
-	/// </summary>
-	public int? MaxGrade { get; set; }
+    /// <summary>
+    /// 最大质检分数
+    /// </summary>
+    public int? MaxGrade { get; set; }
 
     /// <summary>
     ///// 最小质检分数

+ 20 - 0
src/Hotline.Share/Tools/MethodInfoExtensions.cs

@@ -0,0 +1,20 @@
+using Hotline.Share.Attributes;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Tools;
+public static class MethodInfoExtensions
+{
+
+    public static string GetFileName(this MethodInfo? value)
+    {
+        if (value == null) return string.Empty;
+        var att = value.GetCustomAttribute<ExportExcelAttribute>();
+        if (att == null) return string.Empty;
+        return att.FileName;
+    }
+}

+ 1 - 1
src/Hotline/FlowEngine/WorkflowModules/WorkflowModuleConsts.cs

@@ -27,7 +27,7 @@ public class WorkflowModuleConsts
     /// <summary>
     /// 新增知识审批
     /// </summary>
-    public const string KnowledgeAdd = "KnowledgeAdd";
+    public const string KnowledgeAdd = "AddKnowledgeAsync";
 
     /// <summary>
     /// 知识更新

+ 1 - 1
src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs

@@ -1106,7 +1106,7 @@ namespace Hotline.FlowEngine.Workflows
             if (prevStep == null)
                 throw UserFriendlyException.SameMessage("未查询到前一节点");
 
-            if (prevStep.IsCountersignEndStep)
+            while (prevStep.IsCountersignEndStep)
             {
                 countersignStartStep = workflow.Steps.FirstOrDefault(d => d.Id == prevStep.CountersignStartStepId);
                 prevStep = countersignStartStep ?? throw new UserFriendlyException("未查询到对应会签开始节点");

+ 26 - 7
src/Hotline/KnowledgeBase/IKnowledgeDomainService.cs

@@ -15,13 +15,6 @@ namespace Hotline.KnowledgeBase
         /// <returns></returns>
         Task<Knowledge> KnowledgeInfo(string Id, CancellationToken cancellationToken);
 
-        /// <summary>
-        /// 知识库-新增
-        /// </summary>
-        /// <param name="dto"></param>
-        /// <returns></returns>
-        Task<string> KnowledgeAdd(AddKnowledgeDto dto, CancellationToken cancellationToken);
-
         /// <summary>
         /// 知识库-修改
         /// </summary>
@@ -76,5 +69,31 @@ namespace Hotline.KnowledgeBase
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
         Task EndWorkKnowledge(Workflow workflow, CancellationToken cancellationToken);
+
+        /////
+
+        /// <summary>
+        /// 知识库-新增
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        Task<Knowledge> AddKnowledgeAsync(AddKnowledgeDto dto, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 插入知识审批数据
+        /// </summary>
+        Task<KnowledgeApprove> AddKnowledgeApproveAsync(EKnowledgeApproveType knowledgeApproveType, Knowledge kn, string? createOpinion,
+            CancellationToken cancellation);
+
+        /// <summary>
+        /// 更新
+        /// </summary>
+        Task<Knowledge> UpdateKnowledgeAsync(UpdateKnowledgeDto dto, CancellationToken cancellation);
+
+        /// <summary>
+        /// 下架知识
+        /// </summary>
+        Task<Knowledge> OffShelfAsync(OffShelfKnowledgeDto dto, CancellationToken cancellation);
     }
 }

+ 198 - 149
src/Hotline/KnowledgeBase/Knowledge.cs

@@ -9,6 +9,7 @@ using Hotline.Share.Dtos.File;
 using XF.Domain.Repository;
 using Hotline.Share.Dtos;
 using Hotline.Orders;
+using Hotline.Identity.Accounts;
 
 namespace Hotline.KnowledgeBase;
 
@@ -17,35 +18,34 @@ namespace Hotline.KnowledgeBase;
 /// </summary>
 [Description("知识库文档")]
 [SugarIndex("index_knowledge_hotspotId", nameof(Knowledge.HotspotId), OrderByType.Desc)]
-[SugarIndex("index_knowledge_attribution", nameof(Knowledge.Attribution), OrderByType.Desc)]
 [SugarIndex("index_knowledge_creatorOrgId", nameof(Knowledge.CreatorOrgId), OrderByType.Desc)]
 public class Knowledge : WorkflowEntity//   WorkflowEntity  FullStateEntity
 {
-	///// <summary>
-	///// 知识分类
-	///// </summary>
-	//[SugarColumn(ColumnDescription = "知识分类")]
-	//public string KnowledgeTypeId { get; set; }
+    ///// <summary>
+    ///// 知识分类
+    ///// </summary>
+    //[SugarColumn(ColumnDescription = "知识分类")]
+    //public string KnowledgeTypeId { get; set; }
 
 
 
-	/// <summary>
-	/// 热点
-	/// </summary>
-	[SugarColumn(ColumnDescription = "热点")]
-	public string? HotspotId { get; set; }
+    /// <summary>
+    /// 热点
+    /// </summary>
+    [SugarColumn(ColumnDescription = "热点")]
+    public string? HotspotId { get; set; }
 
-	/// <summary>
-	/// 知识编号
-	/// </summary>
-	[SugarColumn(ColumnDescription = "知识编号")]
-	public string? Code { get; set; }
+    /// <summary>
+    /// 知识编号
+    /// </summary>
+    [SugarColumn(ColumnDescription = "知识编号")]
+    public string? Code { get; set; }
 
-	/// <summary>
-	/// 标题
-	/// </summary>
-	[SugarColumn(ColumnDescription = "标题")]
-	public string Title { get; set; }
+    /// <summary>
+    /// 标题
+    /// </summary>
+    [SugarColumn(ColumnDescription = "标题")]
+    public string Title { get; set; }
 
     /// <summary>
     /// 摘要
@@ -59,41 +59,41 @@ public class Knowledge : WorkflowEntity//   WorkflowEntity  FullStateEntity
     [SugarColumn(ColumnDataType = "text", ColumnDescription = "内容")]
     public string Content { get; set; }
 
-	/// <summary>
-	/// 浏览量
-	/// </summary>
-	[SugarColumn(ColumnDescription = "浏览量")]
-	public int PageView { get; set; } = 0;
-
-	/// <summary>
-	/// 是否公开
-	/// </summary>
-	[SugarColumn(ColumnDescription = "是否公开")]
-	public bool? IsPublic { get; set; }
-
-	/// <summary>
-	/// 文档状态
-	/// </summary>
-	[SugarColumn(ColumnDescription = "文档状态")]
-	public EKnowledgeStatus Status { get; set; }
-
-	/// <summary>
-	/// 上架时间
-	/// </summary>
-	[SugarColumn(ColumnDescription = "上架时间")]
-	public DateTime? OnShelfTime { get; set; }
-
-	/// <summary>
-	/// 下架时间
-	/// </summary>
-	[SugarColumn(ColumnDescription = "下架时间")]
-	public DateTime? OffShelfTime { get; set; }
-
-	/// <summary>
-	/// 过期时间
-	/// </summary>
-	[SugarColumn(ColumnDescription = "过期时间")]
-	public DateTime? ExpiredTime { get; set; }
+    /// <summary>
+    /// 浏览量
+    /// </summary>
+    [SugarColumn(ColumnDescription = "浏览量")]
+    public int PageView { get; set; } = 0;
+
+    /// <summary>
+    /// 是否公开
+    /// </summary>
+    [SugarColumn(ColumnDescription = "是否公开")]
+    public bool? IsPublic { get; set; }
+
+    /// <summary>
+    /// 文档状态
+    /// </summary>
+    [SugarColumn(ColumnDescription = "文档状态")]
+    public EKnowledgeStatus Status { get; set; }
+
+    /// <summary>
+    /// 上架时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "上架时间")]
+    public DateTime? OnShelfTime { get; set; }
+
+    /// <summary>
+    /// 下架时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "下架时间")]
+    public DateTime? OffShelfTime { get; set; }
+
+    /// <summary>
+    /// 过期时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "过期时间")]
+    public DateTime? ExpiredTime { get; set; }
 
     /// <summary>
     /// 关键词
@@ -101,11 +101,11 @@ public class Knowledge : WorkflowEntity//   WorkflowEntity  FullStateEntity
     [SugarColumn(ColumnDataType = "json", IsJson = true, IsNullable = true, ColumnDescription = "关键词")]
     public List<string>? Keywords { get; set; }
 
-	/// <summary>
-	/// 版本号
-	/// </summary>
-	[SugarColumn(ColumnDescription = "版本号")]
-	public int? Version { get; set; } = 0;
+    /// <summary>
+    /// 版本号
+    /// </summary>
+    [SugarColumn(ColumnDescription = "版本号")]
+    public int? Version { get; set; } = 0;
 
     /// <summary>
     /// 外部数据(为前端提供级联功能)
@@ -122,31 +122,25 @@ public class Knowledge : WorkflowEntity//   WorkflowEntity  FullStateEntity
     /// <summary>
     /// 关联知识
     /// </summary>
-    [SugarColumn(ColumnDataType = "json", ColumnDescription = "关联知识", IsJson = true,IsNullable =true)]
+    [SugarColumn(ColumnDataType = "json", ColumnDescription = "关联知识", IsJson = true, IsNullable = true)]
     public List<string> Knowledges { get; set; }
 
-	///// <summary>
-	///// 分类
-	///// </summary>
-	//[Navigate(NavigateType.OneToOne, nameof(KnowledgeTypeId))]//一对一 
-	//public KnowledgeType KnowledgeType { get; set; }
-
-	///// <summary>
-	///// 知识分类
-	///// </summary>
-	//[SugarColumn(ColumnDataType = "json", IsJson = true, IsNullable = true, ColumnDescription = "知识分类")]
-	//public List<Kvs>? KnowledgeType { get; set; }
-
-	/// <summary>
-	/// 知识库类型关联ID
-	/// </summary>
-	[Navigate(NavigateType.OneToMany, nameof(KnowledgeRelationType.KnowledgeId))]
-	public List<KnowledgeRelationType> KnowledgeType { get; set; }
-
-	/// <summary>
-	/// 部门
-	/// </summary>
-	[Navigate(NavigateType.OneToOne, nameof(CreatorOrgId))]//一对一 
+    /// <summary>
+    /// 知识库类型关联ID
+    /// </summary>
+    [Navigate(NavigateType.OneToMany, nameof(KnowledgeRelationType.KnowledgeId))]
+    public List<KnowledgeRelationType> KnowledgeType { get; set; }
+
+    /// <summary>
+    /// 知识库分类
+    /// </summary>
+    [Navigate(typeof(KnowledgeRelationType), nameof(KnowledgeRelationType.KnowledgeId), nameof(KnowledgeRelationType.KnowledgeTypeId))]
+    public List<KnowledgeType> KnowledgeTypes { get; set; }
+
+    /// <summary>
+    /// 部门
+    /// </summary>
+    [Navigate(NavigateType.OneToOne, nameof(CreatorOrgId))]//一对一 
     public SystemOrganize SystemOrganize { get; set; }
 
     /// <summary>
@@ -161,69 +155,124 @@ public class Knowledge : WorkflowEntity//   WorkflowEntity  FullStateEntity
     [Navigate(NavigateType.OneToOne, nameof(HotspotId))]//一对一 
     public Hotspot HotspotType { get; set; }
 
-	/// <summary>
-	/// 知识归属
-	/// </summary>
-	[SugarColumn(ColumnDescription = "知识归属")]
-	public string? Attribution { get; set; }
-
-	/// <summary>
-	/// 搜索量
-	/// </summary>
-	[SugarColumn(ColumnDescription = "搜索量")]
-	public int? SearchNum { get; set; } = 0;
-
-	/// <summary>
-	/// 评分
-	/// </summary>
-	[SugarColumn(ColumnDescription = "评分")]
-	public decimal? Score { get; set; } = decimal.Zero;
-
-	/// <summary>
-	/// 评论数
-	/// </summary>
-	[SugarColumn(ColumnDescription = "评论数")]
-	public int? CommentNum { get; set; } = 0;
-
-	/// <summary>
-	/// 来源部门
-	/// </summary>
-	[SugarColumn(ColumnDescription = "来源部门")]
-	public string? SourceOrganizeId { get; set; }
-
-	/// <summary>
-	/// 是否更新
-	/// </summary>
-	[SugarColumn(ColumnDescription = "是否更新")]
-	public bool? Renewaln { get; set; } = false;
-
-	/// <summary>
-	/// 来源部门
-	/// </summary>
-	[Navigate(NavigateType.OneToOne, nameof(SourceOrganizeId))]//一对一 
-	public SystemOrganize SourceOrganize { get; set; }
-
-	[Navigate(NavigateType.OneToOne, nameof(WorkflowId))]
-	public Workflow Workflow { get; set; }
-
-	/// <summary>
-	/// 是否可办理
-	/// </summary>
-	[SugarColumn(IsIgnore = true)]
-	public bool? IsCanHandle { get; set; }
-
-	[SugarColumn(ColumnDataType = "json", IsJson = true, IsNullable = true)]
-	public List<FileJson>? FileJson { get; set; }
-
-	/// <summary>
-	/// 收藏数量
-	/// </summary>
-	[SugarColumn(ColumnDescription = "收藏数量")]
-	public int? CollectCount { get; set; } = 0;
-
-	/// <summary>
-	/// 知识来源
-	/// </summary>
-	[SugarColumn(ColumnDescription = "知识来源")]
-	public string? Source { get; set; }
+    /// <summary>
+    /// 知识归属
+    /// </summary>
+    [SugarColumn(ColumnDescription = "知识归属")]
+    public string? Attribution { get; set; }
+
+    /// <summary>
+    /// 搜索量
+    /// </summary>
+    [SugarColumn(ColumnDescription = "搜索量")]
+    public int? SearchNum { get; set; } = 0;
+
+    /// <summary>
+    /// 评分
+    /// </summary>
+    [SugarColumn(ColumnDescription = "评分")]
+    public decimal? Score { get; set; } = decimal.Zero;
+
+    /// <summary>
+    /// 评论数
+    /// </summary>
+    [SugarColumn(ColumnDescription = "评论数")]
+    public int? CommentNum { get; set; } = 0;
+
+    /// <summary>
+    /// 来源部门
+    /// </summary>
+    [SugarColumn(ColumnDescription = "来源部门")]
+    public string? SourceOrganizeId { get; set; }
+
+    /// <summary>
+    /// 是否更新
+    /// </summary>
+    [SugarColumn(ColumnDescription = "是否更新")]
+    public bool? Renewaln { get; set; } = false;
+
+    /// <summary>
+    /// 文档号
+    /// </summary>
+    public string? DocumentNo { get; set; }
+
+    /// <summary>
+    /// 版本说明
+    /// </summary>
+    public string? VersionDescription { get; set; }
+
+    /// <summary>
+    /// 索引号
+    /// </summary>
+    public string? IndexNo { get; set; }
+
+    /// <summary>
+    /// 文号
+    /// </summary>
+    public string? FileNo { get; set; }
+
+    /// <summary>
+    /// 预案分类id
+    /// </summary>
+    public string? PlanTypeId { get; set; }
+
+
+    /// <summary>
+    /// 来源部门
+    /// </summary>
+    [Navigate(NavigateType.OneToOne, nameof(SourceOrganizeId))]//一对一 
+    public SystemOrganize SourceOrganize { get; set; }
+
+    [Navigate(NavigateType.OneToOne, nameof(WorkflowId))]
+    public Workflow Workflow { get; set; }
+
+    /// <summary>
+    /// 是否可办理
+    /// </summary>
+    [SugarColumn(IsIgnore = true)]
+    public bool? IsCanHandle { get; set; }
+
+    [SugarColumn(ColumnDataType = "json", IsJson = true, IsNullable = true)]
+    public List<FileJson>? FileJson { get; set; }
+
+    /// <summary>
+    /// 收藏数量
+    /// </summary>
+    [SugarColumn(ColumnDescription = "收藏数量")]
+    public int? CollectCount { get; set; } = 0;
+
+    /// <summary>
+    /// 知识来源
+    /// </summary>
+    [SugarColumn(ColumnDescription = "知识来源")]
+    public string? Source { get; set; }
+
+    /// <summary>
+    /// 知识库类型关联ID
+    /// </summary>
+    [Navigate(NavigateType.OneToMany, nameof(KnowledgeApprove.KnowledgeId))]
+    public List<KnowledgeApprove> KnowledgeApproves { get; set; }
+
+    #region method
+
+    /// <summary>
+    /// 上架
+    /// </summary>
+    public void OnShelf()
+    {
+        Status = EKnowledgeStatus.OnShelf;
+        OnShelfTime = System.DateTime.Now;
+    }
+
+    /// <summary>
+    /// 下架
+    /// </summary>
+    public void OffShelf()
+    {
+        Status = EKnowledgeStatus.OffShelf;
+        OffShelfTime = System.DateTime.Now;
+        OnShelfTime = null;
+    }
+
+    #endregion
 }

+ 80 - 0
src/Hotline/KnowledgeBase/KnowledgeApprove.cs

@@ -0,0 +1,80 @@
+using Hotline.Orders;
+using Hotline.Settings;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Hotline.Users;
+using XF.Domain.Repository;
+
+namespace Hotline.KnowledgeBase
+{
+    [Description("知识审核")]
+    [SugarIndex("index_order_creationtime", nameof(KnowledgeApprove.CreationTime), OrderByType.Desc)]
+    public class KnowledgeApprove : CreationEntity
+    {
+        /// <summary>
+        /// 知识Id
+        /// </summary>
+        public string KnowledgeId { get; set; }
+
+        /// <summary>
+        /// 审核类型
+        /// </summary>
+        public EKnowledgeApproveType KnowledgeApproveType { get; set; }
+
+        /// <summary>
+        /// 审核状态
+        /// </summary>
+        public EKnowledgeApproveStatus KnowledgeApproveStatus { get; set; }
+
+        /// <summary>
+        /// 审批发起意见
+        /// </summary>
+        public string? CreateOpinion { get; set; }
+
+        /// <summary>
+        /// 审批人Id
+        /// </summary>
+        public string? ApproverId { get; set; }
+
+        /// <summary>
+        /// 审核意见
+        /// </summary>
+        public string? ApproveOpinion { get; set; }
+
+        /// <summary>
+        /// 审核时间
+        /// </summary>
+        public DateTime? ApproveTime { get; set; }
+
+        [Navigate(NavigateType.OneToOne, nameof(KnowledgeId))]
+        public Knowledge Knowledge { get; set; }
+
+        ///// <summary>
+        ///// 发起人
+        ///// </summary>
+        //[Navigate(NavigateType.OneToOne, nameof(CreatorId))]
+        //public User Creator { get; set; }
+
+        /// <summary>
+        /// 审批人
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, nameof(ApproverId))]
+        public User Approver { get; set; }
+
+        #region method
+
+        public void Approve(string approverId, string approveOpinion, EKnowledgeApproveStatus status)
+        {
+            ApproverId = approverId;
+            ApproveOpinion = approveOpinion;
+            KnowledgeApproveStatus = status;
+            ApproveTime = DateTime.Now;
+        }
+        #endregion
+    }
+}

+ 175 - 52
src/Hotline/KnowledgeBase/KnowledgeDomainService.cs

@@ -1,5 +1,7 @@
 using DotNetCore.CAP;
 using Hotline.Caching.Interfaces;
+using Hotline.Configurations;
+using Hotline.File;
 using Hotline.FlowEngine;
 using Hotline.FlowEngine.WorkflowModules;
 using Hotline.FlowEngine.Workflows;
@@ -7,9 +9,14 @@ using Hotline.Settings;
 using Hotline.Share.Dtos.Knowledge;
 using Hotline.Share.Enums.KnowledgeBase;
 using Hotline.Share.Mq;
+using Hotline.Share.Tools;
 using MapsterMapper;
+using Microsoft.AspNetCore.Builder.Extensions;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Options;
 using SqlSugar;
 using System;
+using XF.Domain.Authentications;
 using XF.Domain.Dependency;
 using XF.Domain.Entities;
 using XF.Domain.Exceptions;
@@ -25,28 +32,22 @@ namespace Hotline.KnowledgeBase
         private readonly IKnowledgeWorkFlowRepository _knowledgeWorkFlowRepository;
         private readonly ICapPublisher _capPublisher;
         private readonly ISystemSettingCacheManager _systemSettingCacheManager;
-        private readonly IRepository<KnowledgeRelationType> _relationTypeRepository;
         private readonly IRepository<KnowledgeType> _knowledgeTypeRepository;
+        private readonly ISessionContext _sessionContext;
+        private readonly IFileRepository _fileRepository;
+        private readonly IRepository<KnowledgeApprove> _knowledgeApproveRepository;
+        private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
 
-        /// <summary>
-        /// 
-        /// </summary>
-        /// <param name="knowledgeRepository"></param>
-        /// <param name="mapper"></param>
-        /// <param name="knowledgePVRepository"></param>
-        /// <param name="knowledgeWorkFlowRepository"></param>
-        /// <param name="capPublisher"></param>
-        /// <param name="systemSettingCacheManager"></param>
-        /// <param name="relationTypeRepository"></param>
-        /// <param name="knowledgeTypeRepository"></param>
         public KnowledgeDomainService(IKnowledgeRepository knowledgeRepository,
             IMapper mapper,
             IRepository<KnowledgePv> knowledgePVRepository,
             IKnowledgeWorkFlowRepository knowledgeWorkFlowRepository,
             ICapPublisher capPublisher, ISystemSettingCacheManager systemSettingCacheManager,
-            IRepository<KnowledgeRelationType> relationTypeRepository,
-            IRepository<KnowledgeType> knowledgeTypeRepository
-            )
+            IRepository<KnowledgeType> knowledgeTypeRepository,
+            ISessionContext sessionContext,
+            IFileRepository fileRepository,
+            IRepository<KnowledgeApprove> knowledgeApproveRepository,
+            IOptionsSnapshot<AppConfiguration> appOptions)
         {
             _knowledgeRepository = knowledgeRepository;
             _mapper = mapper;
@@ -54,8 +55,11 @@ namespace Hotline.KnowledgeBase
             _knowledgeWorkFlowRepository = knowledgeWorkFlowRepository;
             _capPublisher = capPublisher;
             _systemSettingCacheManager = systemSettingCacheManager;
-            _relationTypeRepository = relationTypeRepository;
             _knowledgeTypeRepository = knowledgeTypeRepository;
+            _sessionContext = sessionContext;
+            _fileRepository = fileRepository;
+            _knowledgeApproveRepository = knowledgeApproveRepository;
+            _appOptions = appOptions;
         }
 
         /// <summary>
@@ -65,10 +69,11 @@ namespace Hotline.KnowledgeBase
         /// <returns></returns>
         public async Task<Knowledge> KnowledgeInfo(string Id, CancellationToken cancellationToken)
         {
-            var know = await _knowledgeRepository.Queryable(false, false, false)
-                .Includes(x => x.Workflow, d => d.Steps)
+            var know = await _knowledgeRepository.Queryable()
+                //.Includes(x => x.Workflow, d => d.Steps)
                 .Includes(x => x.SourceOrganize)
-                .Includes(x => x.KnowledgeType).FirstAsync(p => p.Id == Id);
+                .Includes(x => x.KnowledgeTypes)
+                .FirstAsync(p => p.Id == Id, cancellationToken);
             if (know is null)
                 throw UserFriendlyException.SameMessage("知识查询失败!");
             return know;
@@ -99,6 +104,7 @@ namespace Hotline.KnowledgeBase
             {
                 CreationTime = DateTime.Now,
                 KnowledgeCode = know.Code,
+                KnowledgeId = know.Id,
                 BrowseTime = browseTime
             };
             //浏览记录写入
@@ -179,24 +185,6 @@ namespace Hotline.KnowledgeBase
             }
         }
 
-        /// <summary>
-        /// 知识库-新增
-        /// </summary>
-        /// <param name="dto"></param>
-        /// <returns></returns>
-        public async Task<string> KnowledgeAdd(AddKnowledgeDto dto, CancellationToken cancellationToken)
-        {
-            var kn = _mapper.Map<Knowledge>(dto);
-
-            //Code为空,从新生成Code
-            if (string.IsNullOrEmpty(kn.Code))
-                kn.Code = Convert.ToInt64((DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds).ToString();
-
-            kn.Status = EKnowledgeStatus.Auditing;
-            return await _knowledgeRepository.AddAsync(kn, cancellationToken);
-
-        }
-
         /// <summary>
         /// 知识库-修改
         /// </summary>
@@ -374,21 +362,21 @@ namespace Hotline.KnowledgeBase
             {
                 var isEfficientlyAccomplish = false;
                 //根据知识ID查询所有的知识分类
-                var typeList = await _relationTypeRepository.Queryable().Where(p => p.KnowledgeId == knowledge.Id).ToListAsync(cancellationToken);
-                if (typeList != null && typeList.Any())
-                {
-                    //遍历知识分类
-                    foreach (var item in typeList)
-                    {
-                        //查询知识分类是否满足高效办成一件事
-                        if (await _knowledgeTypeRepository.AnyAsync(p => p.Id == item.KnowledgeTypeId && p.SpliceName.StartsWith(cancelPublishOrderEnabled), cancellationToken))
-                        {
-                            //满足,跳出遍历
-                            isEfficientlyAccomplish = true;
-                            break;
-                        }
-                    }
-                }
+                //var typeList = await _relationTypeRepository.Queryable().Where(p => p.KnowledgeId == knowledge.Id).ToListAsync(cancellationToken);
+                //if (typeList != null && typeList.Any())
+                //{
+                //    //遍历知识分类
+                //    foreach (var item in typeList)
+                //    {
+                //        //查询知识分类是否满足高效办成一件事
+                //        if (await _knowledgeTypeRepository.AnyAsync(p => p.Id == item.KnowledgeTypeId && p.SpliceName.StartsWith(cancelPublishOrderEnabled), cancellationToken))
+                //        {
+                //            //满足,跳出遍历
+                //            isEfficientlyAccomplish = true;
+                //            break;
+                //        }
+                //    }
+                //}
                 //是高效,处理默认值
                 if (isEfficientlyAccomplish)
                 {
@@ -409,5 +397,140 @@ namespace Hotline.KnowledgeBase
                 await _capPublisher.PublishAsync(EventNames.HotlineKnowledgeRemove, pushKnowledge, cancellationToken: cancellationToken);
             #endregion
         }
+
+        /// <summary>
+        /// 知识库-新增
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        public async Task<Knowledge> AddKnowledgeAsync(AddKnowledgeDto dto, CancellationToken cancellationToken)
+        {
+            var kn = _mapper.Map<Knowledge>(dto);
+            kn.SourceOrganizeId = _sessionContext.RequiredOrgId;
+            var any = await _knowledgeRepository.Queryable().Where(x => x.Status == EKnowledgeStatus.OnShelf && x.Title == kn.Title).AnyAsync();
+            if (any) throw UserFriendlyException.SameMessage("当前知识标题存在重复标题!");
+
+            //Code为空,从新生成Code
+            if (string.IsNullOrEmpty(kn.Code))
+                kn.Code = Convert.ToInt64((DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds).ToString();
+
+            kn.Status = EKnowledgeStatus.Drafts;
+            kn.InitId();
+            if (dto.Files.NotNullOrEmpty()) kn.FileJson = await _fileRepository.AddFileAsync(dto.Files, kn.Id, "", cancellationToken);
+
+            if (dto.KnowledgeType.Any())
+            {
+                var knTypes = _mapper.Map<List<KnowledgeType>>(dto.KnowledgeType);
+                kn.KnowledgeTypes = knTypes;
+            }
+
+            await _knowledgeRepository.AddNav(kn)
+                .Include(d => d.KnowledgeTypes)
+                .ExecuteCommandAsync();
+
+            return kn;
+        }
+
+        /// <summary>
+        /// 插入知识审批数据
+        /// </summary>
+        public async Task<KnowledgeApprove> AddKnowledgeApproveAsync(EKnowledgeApproveType knowledgeApproveType, Knowledge kn, string? createOpinion,
+            CancellationToken cancellation)
+        {
+            var approve = new KnowledgeApprove
+            {
+                KnowledgeId = kn.Id,
+                KnowledgeApproveType = knowledgeApproveType,
+                KnowledgeApproveStatus = EKnowledgeApproveStatus.Unhandle,
+                CreateOpinion = createOpinion,
+            };
+
+            await _knowledgeApproveRepository.AddAsync(approve, cancellation);
+
+            kn.Status = EKnowledgeStatus.Auditing;
+            await _knowledgeRepository.UpdateAsync(kn, cancellation);
+
+            return approve;
+        }
+
+        public async Task<Knowledge> UpdateKnowledgeAsync(UpdateKnowledgeDto dto, CancellationToken cancellation)
+        {
+            var knowledge = await _knowledgeRepository.GetAsync(d => d.Id == dto.Id, cancellation);
+            if (knowledge == null)
+                throw UserFriendlyException.SameMessage("无效知识编号");
+            if ((knowledge.Status == EKnowledgeStatus.OnShelf || knowledge.Status == EKnowledgeStatus.Auditing)
+                && (knowledge.ExpiredTime.HasValue && knowledge.ExpiredTime.Value > DateTime.Now))
+                throw UserFriendlyException.SameMessage("知识库数据不可修改");
+            var any = await _knowledgeRepository.Queryable()
+                .AnyAsync(x => x.Status == EKnowledgeStatus.OnShelf && x.Title == dto.Title && x.Id != dto.Id, cancellation);
+            if (any) throw UserFriendlyException.SameMessage("当前知识标题已存在!");
+
+            _mapper.Map(dto, knowledge);
+            if (dto.Files.Any())
+                knowledge.FileJson = await _fileRepository.AddFileAsync(dto.Files, knowledge.Id, "", cancellation);
+            else
+                knowledge.FileJson = new List<Share.Dtos.File.FileJson>();
+            knowledge.Renewaln = knowledge.Status != EKnowledgeStatus.Drafts;
+            if (_appOptions.Value.IsYiBin)
+            {
+                //临时处理 修改后提交修改基本信息
+                if (!_sessionContext.OrgIsCenter)
+                {
+                    knowledge.Attribution = "部门知识库";
+                }
+                knowledge.CreatorId = _sessionContext.UserId;
+                knowledge.CreatorName = _sessionContext.UserName;
+                knowledge.CreatorOrgId = _sessionContext.OrgId;
+                knowledge.CreatorOrgName = _sessionContext.OrgName;
+                knowledge.CreatorOrgLevel = _sessionContext.OrgLevel;
+            }
+
+            if (dto.KnowledgeType.Any())
+            {
+                knowledge.KnowledgeTypes = dto.KnowledgeType.Select(d => new KnowledgeType
+                {
+                    Id = d.KnowledgeTypeId,
+                }).ToList();
+                await _knowledgeRepository.UpdateNav(knowledge)
+                    .Include(d => d.KnowledgeTypes, new UpdateNavOptions
+                    {
+                        ManyToManyIsUpdateA = true
+                    })
+                    .ExecuteCommandAsync();
+            }
+            else
+            {
+                await _knowledgeRepository.UpdateAsync(knowledge, cancellation);
+            }
+
+            return knowledge;
+        }
+
+        /// <summary>
+        /// 下架知识
+        /// </summary>
+        public async Task<Knowledge> OffShelfAsync(OffShelfKnowledgeDto dto, CancellationToken cancellation)
+        {
+            var kn = await _knowledgeRepository.Queryable()
+                .Includes(d => d.User)
+                .Includes(d => d.HotspotType)
+                .Includes(d => d.SystemOrganize)
+                .FirstAsync(d => d.Id == dto.Id, cancellation);
+            if (kn is null) throw new UserFriendlyException("无效知识id");
+            if (kn.Status == EKnowledgeStatus.OffShelf) return kn;
+            if (kn.Status != EKnowledgeStatus.OnShelf) throw new UserFriendlyException("知识下架失败");
+
+            kn.OffShelf();
+
+            await _knowledgeRepository.UpdateAsync(kn, cancellation);
+
+            var pushKnowledge = _mapper.Map<KnowledgeSendDto>(kn);
+            pushKnowledge.CategoryCode = "01";
+            pushKnowledge.CategoryName = "公共服务";
+            //推省上
+            await _capPublisher.PublishAsync(EventNames.HotlineKnowledgeRemove, pushKnowledge, cancellationToken: cancellation);
+
+            return kn;
+        }
     }
 }

+ 46 - 0
src/Hotline/KnowledgeBase/KnowledgeImportTemplate.cs

@@ -0,0 +1,46 @@
+using MiniExcelLibs.Attributes;
+
+namespace Hotline.KnowledgeBase
+{
+    public class KnowledgeImportTemplate
+    {
+        /// <summary>
+        /// 知识归属
+        /// </summary>
+        [ExcelColumnName("知识归属(中心知识库/部门知识库)")]
+        public string? Attribution { get; set; }
+
+        [ExcelColumnName("一级分类")]
+        public string? KnowledgeTypeLevelOne { get; set; }
+
+        //[ExcelColumnName("二级分类")]
+        //public string? KnowledgeTypeLevelTwo { get; set; }
+
+        //[ExcelColumnName("三级分类")]
+        //public string? KnowledgeTypeLevelThree { get; set; }
+
+        [ExcelColumnName("失效时间")]
+        public DateTime? ExpiredTime { get; set; }
+
+        /// <summary>
+        /// 是否公开
+        /// </summary>
+        [ExcelColumnName("是否公开(是/否)")]
+        public string IsPublic { get; set; }
+
+        //[ExcelColumnName("热点分类")]
+        //public string? HotspotName { get; set; }
+
+        /// <summary>
+        /// 标题
+        /// </summary>
+        [ExcelColumnName("标题")]
+        public string Title { get; set; }
+
+        /// <summary>
+        /// 内容
+        /// </summary>
+        [ExcelColumnName("内容")]
+        public string Content { get; set; }
+    }
+}

+ 6 - 0
src/Hotline/KnowledgeBase/KnowledgePV.cs

@@ -8,6 +8,12 @@ namespace Hotline.KnowledgeBase;
 /// </summary>
 public class KnowledgePv : CreationEntity
 {
+    /// <summary>
+    /// 知识Code
+    /// </summary>
+    [SugarColumn(ColumnDescription = "知识Id")]
+    public string? KnowledgeId { get; set; }
+
     /// <summary>
     /// 知识Code
     /// </summary>

+ 15 - 12
src/Hotline/KnowledgeBase/KnowledgeRelationType.cs

@@ -5,6 +5,7 @@ using System.ComponentModel;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using XF.Domain.Entities;
 using XF.Domain.Repository;
 
 namespace Hotline.KnowledgeBase
@@ -13,20 +14,22 @@ namespace Hotline.KnowledgeBase
 	/// 知识库关联类型
 	/// </summary>
 	[Description("知识库关联类型")]
-	[SugarIndex("index_knowledgeRelationType_knowledgeId", nameof(KnowledgeRelationType.KnowledgeId), OrderByType.Desc)]
-	[SugarIndex("index_knowledgeRelationType_typeId", nameof(KnowledgeRelationType.KnowledgeTypeId), OrderByType.Desc)]
+	//[SugarIndex("index_knowledgeRelationType_knowledgeId", nameof(KnowledgeRelationType.KnowledgeId), OrderByType.Desc)]
+	//[SugarIndex("index_knowledgeRelationType_typeId", nameof(KnowledgeRelationType.KnowledgeTypeId), OrderByType.Desc)]
 	[SugarIndex("index_knowledgeRelationType_spliceName", nameof(KnowledgeRelationType.KnowledgeTypeSpliceName), OrderByType.Desc)]
-	public class KnowledgeRelationType : FullStateEntity
-	{
-		/// <summary>
-		/// 知识库ID
-		/// </summary>
-		public string KnowledgeId { get; set; }
+	public class KnowledgeRelationType : ITable, IEntity
+    {
+        /// <summary>
+        /// 知识库ID
+        /// </summary>
+        [SugarColumn(IsPrimaryKey = true)]
+        public string KnowledgeId { get; set; }
 
-		/// <summary>
-		/// 知识库类型ID
-		/// </summary>
-		public string KnowledgeTypeId { get; set; }
+        /// <summary>
+        /// 知识库类型ID
+        /// </summary>
+        [SugarColumn(IsPrimaryKey = true)]
+        public string KnowledgeTypeId { get; set; }
 
 		/// <summary>
 		/// 知识库类型名称

+ 8 - 1
src/Hotline/KnowledgeBase/KnowledgeType.cs

@@ -1,5 +1,6 @@
 using SqlSugar;
 using System.ComponentModel;
+using Hotline.Settings;
 using XF.Domain.Repository;
 
 namespace Hotline.KnowledgeBase;
@@ -49,5 +50,11 @@ public class KnowledgeType : FullStateEntity
     /// 类型关联机构
     /// </summary>
     [Navigate(NavigateType.OneToMany, nameof(KnowledgeTypeOrg.TypeId))]
-    public List<KnowledgeTypeOrg>  KnowledgeTypeOrgs { get; set; }
+    public List<KnowledgeTypeOrg> KnowledgeTypeOrgs { get; set; }
+
+    [Navigate(typeof(KnowledgeTypeOrg), nameof(KnowledgeTypeOrg.TypeId), nameof(KnowledgeTypeOrg.OrgId))]
+    public List<SystemOrganize> Orgs { get; set; }
+
+    [Navigate(typeof(KnowledgeRelationType), nameof(KnowledgeRelationType.KnowledgeTypeId), nameof(KnowledgeRelationType.KnowledgeId))]
+    public List<Knowledge> Knowledges { get; set; }
 }

+ 63 - 43
src/Hotline/KnowledgeBase/KnowledgeTypeDomainService.cs

@@ -3,6 +3,7 @@ using Hotline.Share.Enums.KnowledgeBase;
 using MapsterMapper;
 using SqlSugar;
 using System.Collections.Generic;
+using Hotline.Settings;
 using XF.Domain.Dependency;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
@@ -15,8 +16,7 @@ namespace Hotline.KnowledgeBase
         /// 
         /// </summary>
         private readonly IRepository<KnowledgeType> _knowledgeTypeRepository;
-        private readonly IRepository<KnowledgeTypeOrg> _knowledgeTypeOrgRepository;
-		private readonly IKnowledgeRepository _knowledgeRepository;
+        private readonly IKnowledgeRepository _knowledgeRepository;
         private readonly IMapper _mapper;
 
         /// <summary>
@@ -24,14 +24,12 @@ namespace Hotline.KnowledgeBase
         /// </summary>
         /// <param name="knowledgeTypeRepository"></param>
         /// <param name="mapper"></param>
-        public KnowledgeTypeDomainService(IRepository<KnowledgeType> knowledgeTypeRepository, IMapper mapper, IKnowledgeRepository knowledgeRepository, IRepository<KnowledgeTypeOrg> knowledgeTypeOrgRepository)
+        public KnowledgeTypeDomainService(IRepository<KnowledgeType> knowledgeTypeRepository, IMapper mapper, IKnowledgeRepository knowledgeRepository)
         {
             _knowledgeTypeRepository = knowledgeTypeRepository;
             _mapper = mapper;
             _knowledgeRepository = knowledgeRepository;
-            _knowledgeTypeOrgRepository = knowledgeTypeOrgRepository;
-
-		}
+        }
 
 
         /// <summary>
@@ -49,14 +47,24 @@ namespace Hotline.KnowledgeBase
             string FullName = await GetFullName(type.ParentId);
             //处理全称,如果为第一级直接用全称,否则获取全称后拼接名称
             type.SpliceName = string.IsNullOrEmpty(FullName) ? dto.Name : FullName + "-" + dto.Name;
-            var id = await _knowledgeTypeRepository.AddAsync(type, cancellationToken);
+            //var id = await _knowledgeTypeRepository.AddAsync(type, cancellationToken);
+
             if (dto.TypeOrgDtos != null && dto.TypeOrgDtos.Any())
             {
-				List<KnowledgeTypeOrg> orgs = _mapper.Map<List<KnowledgeTypeOrg>>(dto.TypeOrgDtos);
-                orgs.ForEach(x => x.TypeId = type.Id);
-                await _knowledgeTypeOrgRepository.AddRangeAsync(orgs, cancellationToken);
-			}
-			return id;
+                //List<KnowledgeTypeOrg> orgs = _mapper.Map<List<KnowledgeTypeOrg>>(dto.TypeOrgDtos);
+                //            orgs.ForEach(x => x.TypeId = type.Id);
+                //            await _knowledgeTypeOrgRepository.AddRangeAsync(orgs, cancellationToken);
+
+                type.Orgs = dto.TypeOrgDtos.Select(d => new SystemOrganize { Id = d.OrgId }).ToList();
+                await _knowledgeTypeRepository.AddNav(type)
+                    .Include(d => d.Orgs)
+                    .ExecuteCommandAsync();
+            }
+            else
+            {
+                await _knowledgeTypeRepository.AddAsync(type, cancellationToken);
+            }
+            return type.Id;
         }
 
         /// <summary>
@@ -67,33 +75,45 @@ namespace Hotline.KnowledgeBase
         /// <returns></returns>
         public async Task UpdateType(UpdateKnowledgeTypeDto dto, CancellationToken cancellationToken)
         {
-	        //查询原有数据
-	        var type = await _knowledgeTypeRepository.GetAsync(dto.Id, cancellationToken);
-	        if (type is null)
-		        throw UserFriendlyException.SameMessage("编辑失败!");
-	        bool result = type.Name != dto.Name || type.ParentId != dto.ParentId;
-	        //是否更改分类名称或者层级
-	        //转换
-	        _mapper.Map(dto, type);
-	        //如果更改了名称或者修改了层级,则修改全称,未更改不修改
-	        if (result)
-	        {
-		        string FullName = await GetFullName(type.ParentId);//获取分类名称全称
-		        type.SpliceName = string.IsNullOrEmpty(FullName) ? dto.Name : FullName + "-" + dto.Name;//处理全称,如果为第一级直接用全称,否则获取全称后拼接名称
-	        }
-	        //修改数据
-	        await _knowledgeTypeRepository.UpdateAsync(type, cancellationToken);
-	        //如果修改了名称,对应修改子分类全称
-	        if (result)
-		        await UpdateChildNode(type.Id);
-	        await _knowledgeTypeOrgRepository.RemoveAsync(x => x.TypeId == type.Id, false, cancellationToken);
-			if (dto.TypeOrgDtos != null && dto.TypeOrgDtos.Any())
-	        {
-		        List<KnowledgeTypeOrg> orgs = _mapper.Map<List<KnowledgeTypeOrg>>(dto.TypeOrgDtos);
-		        orgs.ForEach(x => x.TypeId = type.Id);
-				await _knowledgeTypeOrgRepository.AddRangeAsync(orgs, cancellationToken);
-	        }
-		}
+            //查询原有数据
+            var type = await _knowledgeTypeRepository.GetAsync(dto.Id, cancellationToken);
+            if (type is null)
+                throw UserFriendlyException.SameMessage("编辑失败!");
+            bool result = type.Name != dto.Name || type.ParentId != dto.ParentId;
+            //是否更改分类名称或者层级
+            //转换
+            _mapper.Map(dto, type);
+            //如果更改了名称或者修改了层级,则修改全称,未更改不修改
+            if (result)
+            {
+                string FullName = await GetFullName(type.ParentId);//获取分类名称全称
+                type.SpliceName = string.IsNullOrEmpty(FullName) ? dto.Name : FullName + "-" + dto.Name;//处理全称,如果为第一级直接用全称,否则获取全称后拼接名称
+            }
+            ////修改数据
+            //await _knowledgeTypeRepository.UpdateAsync(type, cancellationToken);
+            //如果修改了名称,对应修改子分类全称
+            if (result)
+                await UpdateChildNode(type.Id);
+            //      await _knowledgeTypeOrgRepository.RemoveAsync(x => x.TypeId == type.Id, false, cancellationToken);
+            //if (dto.TypeOrgDtos != null && dto.TypeOrgDtos.Any())
+            //      {
+            //       List<KnowledgeTypeOrg> orgs = _mapper.Map<List<KnowledgeTypeOrg>>(dto.TypeOrgDtos);
+            //       orgs.ForEach(x => x.TypeId = type.Id);
+            //	await _knowledgeTypeOrgRepository.AddRangeAsync(orgs, cancellationToken);
+            //      }
+
+            if (dto.TypeOrgDtos != null && dto.TypeOrgDtos.Any())
+            {
+                type.Orgs = dto.TypeOrgDtos.Select(d => new SystemOrganize { Id = d.OrgId }).ToList();
+                await _knowledgeTypeRepository.UpdateNav(type)
+                    .Include(d => d.Orgs)
+                    .ExecuteCommandAsync();
+            }
+            else
+            {
+                await _knowledgeTypeRepository.UpdateAsync(type, cancellationToken);
+            }
+        }
 
         /// <summary>
         ///  知识分类-新增、编辑初始化
@@ -188,8 +208,8 @@ namespace Hotline.KnowledgeBase
                         //下架知识
                         //查询当前分类下的知识(查询条件:此分类下/未删除/已上架)
                         //var knowList = _knowledgeRepository.Queryable().Where(p => SqlFunc.JsonListObjectAny(p.KnowledgeType,"Key", item.Id) && p.Status == EKnowledgeStatus.OnShelf && p.IsDeleted == false).ToList();
-                        var knowList = _knowledgeRepository.Queryable().Where(p => p.KnowledgeType.Any(t=>t.KnowledgeId == item.Id) && p.Status == EKnowledgeStatus.OnShelf && p.IsDeleted == false).ToList();
-						if (knowList != null && knowList.Count > 0)
+                        var knowList = _knowledgeRepository.Queryable().Where(p => p.KnowledgeType.Any(t => t.KnowledgeId == item.Id) && p.Status == EKnowledgeStatus.OnShelf && p.IsDeleted == false).ToList();
+                        if (knowList != null && knowList.Count > 0)
                         {
                             //修改知识为下架状态
                             foreach (var itemKnow in knowList)
@@ -227,8 +247,8 @@ namespace Hotline.KnowledgeBase
 
             //查询是否有知识分类
             //var checkKnowledge = await _knowledgeRepository.CountAsync(p => SqlFunc.JsonListObjectAny(p.KnowledgeType, "Key", Id), cancellationToken);//&& p.IsDeleted == false
-            var checkKnowledge = await _knowledgeRepository.CountAsync(p => p.KnowledgeType.Any(t=>t.KnowledgeId == Id), cancellationToken);//&& p.IsDeleted == false
-			if (checkKnowledge > 0)
+            var checkKnowledge = await _knowledgeRepository.CountAsync(p => p.KnowledgeType.Any(t => t.KnowledgeId == Id), cancellationToken);//&& p.IsDeleted == false
+            if (checkKnowledge > 0)
                 throw UserFriendlyException.SameMessage("分类存在知识!");
 
             //删除操作

+ 3 - 2
src/Hotline/KnowledgeBase/KnowledgeTypeOrg.cs

@@ -5,6 +5,7 @@ using System.ComponentModel;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using XF.Domain.Entities;
 using XF.Domain.Repository;
 
 namespace Hotline.KnowledgeBase
@@ -15,8 +16,8 @@ namespace Hotline.KnowledgeBase
 	[Description("知识分类关联机构")]
 	[SugarIndex("index_knowledgeTypeOrg_typeId", nameof(KnowledgeTypeOrg.TypeId), OrderByType.Desc)]
 	[SugarIndex("index_knowledgeTypeOrg_orgId", nameof(KnowledgeTypeOrg.OrgId), OrderByType.Desc)]
-	public class KnowledgeTypeOrg : FullStateEntity
-	{
+	public class KnowledgeTypeOrg : ITable, IEntity//: FullStateEntity
+    {
 		/// <summary>
 		/// 知识分类ID
 		/// </summary>

+ 24 - 12
src/Hotline/Orders/Order.cs

@@ -16,6 +16,7 @@ using XF.Domain.Exceptions;
 using XF.Domain.Extensions;
 using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
+using Hotline.KnowledgeBase;
 
 namespace Hotline.Orders
 {
@@ -172,21 +173,32 @@ namespace Hotline.Orders
         [SugarColumn(ColumnDescription = "标题")]
         public string Title { get; set; }
 
-        /// <summary>
-        /// 工单标签(自贡)
-        /// </summary>
-        [SugarColumn(ColumnDescription = "工单标签")]
-        public string? OrderTag { get; set; }
+		#region 工单标签(自贡)
+		/// <summary>
+		/// 工单标签(自贡)-- 弃用
+		/// </summary>
+		[SugarColumn(ColumnDescription = "工单标签-- 弃用")]
+		public string? OrderTag { get; set; }
 
-        [SugarColumn(ColumnDescription = "工单标签代码")]
-        public string? OrderTagCode { get; set; }
+		[SugarColumn(ColumnDescription = "工单标签代码-- 弃用")]
+		public string? OrderTagCode { get; set; }
 
-        #region 热点
 
-        /// <summary>
-        /// 热点
-        /// </summary>
-        [SugarColumn(ColumnDescription = "热点ID")]
+
+		/// <summary>
+		/// 工单标签
+		/// </summary>
+		[Navigate(typeof(OrderRelationTag), nameof(OrderRelationTag.OrderId), nameof(OrderRelationTag.TagId))]
+		public List<SystemDicData> OrderTags { get; set; }
+
+		#endregion
+
+		#region 热点
+
+		/// <summary>
+		/// 热点
+		/// </summary>
+		[SugarColumn(ColumnDescription = "热点ID")]
         public string? HotspotId { get; set; }
 
         //public string? HotspotCode { get; set; }

+ 24 - 0
src/Hotline/Orders/OrderRelationTag.cs

@@ -0,0 +1,24 @@
+using Hotline.KnowledgeBase;
+using SqlSugar;
+using System.ComponentModel;
+using XF.Domain.Entities;
+
+namespace Hotline.Orders
+{
+	[Description("工单关联标签")]
+	public class OrderRelationTag : ITable, IEntity
+	{
+
+		/// <summary>
+		/// 工单ID
+		/// </summary>
+		[SugarColumn(IsPrimaryKey = true)]
+		public string OrderId { get; set; }
+
+		/// <summary>
+		/// 标签ID
+		/// </summary>
+		[SugarColumn(IsPrimaryKey = true)]
+		public string TagId { get; set; }
+	}
+}

+ 14 - 0
src/Hotline/Pdf/IPdfManager.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Pdf
+{
+    public interface IPdfManager
+    {
+        void GeneratePdf(string title, string content, Stream stream);
+        void GeneratePdf(string title, string content, string path);
+    }
+}

+ 2 - 2
src/Hotline/SeedData/SystemDicDataSeedData.cs

@@ -120,8 +120,8 @@ public class SystemDicDataSeedData : ISeedData<SystemDicData>
         if (dicTypeCode == SysDicTypeConsts.JobType)
         {
             return [
-                new() { Id = "08dc3c1e-470f-4a7d-8433-3fed9bb59c95", DicDataValue = "dianhan", DicDataName = "电焊"},
-                new() { Id = "08dc3cc4-aa8b-41ad-8807-ba1571e00628", DicDataValue = "qige", DicDataName = "气割"},
+                new() { Id = "08dc3c1e-470f-4a7d-8433-3fed9bb59c95", DicDataValue = "0", DicDataName = "电焊"},
+                new() { Id = "08dc3cc4-aa8b-41ad-8807-ba1571e00628", DicDataValue = "1", DicDataName = "气割"},
                 ];
         }
         if (dicTypeCode == SysDicTypeConsts.BusinessUnitType)

+ 4 - 2
src/Hotline/Settings/TimeLimitDomain/ExpireTimeSupplier/WorkDaySupplier.cs

@@ -186,6 +186,7 @@ public class WorkDaySupplier : IExpireTimeSupplier, IScopeDependency
                 //计算一天多少个工作小时
                 var durationType1 = WorkEndTime - WorkBeginTime;
                 double workMinutesType1 = durationType1.TotalMinutes;
+                
 
                 #region 计算期满时间
                 if (!isWorkSend)
@@ -245,9 +246,9 @@ public class WorkDaySupplier : IExpireTimeSupplier, IScopeDependency
 
                 var dayIndex = 1;
                 var configCount = 1;
-                while (true)
+                while(true)
                 {
-                    if (startTime.AddDays(dayIndex).Date != beginTime.Date)
+                    if (startTime.AddDays(dayIndex).Date!= beginTime.Date)
                     {
                         if (await IsWorkDay(startTime.AddDays(dayIndex)))
                         {
@@ -266,6 +267,7 @@ public class WorkDaySupplier : IExpireTimeSupplier, IScopeDependency
                 double totalWorkMinutesOneType1 = (workMinutesType1 * configCount) * (timeConfig.PercentageOne / 100.00);
 
 
+
                 //计算即将超期时间
                 while (true)
                 {

+ 1 - 1
src/Hotline/Settings/TimeLimits/TimeLimit.cs

@@ -75,7 +75,7 @@ namespace Hotline.Settings.TimeLimits
                 new TimeLimitBaseDataModel("d6100e08-b1ab-11ed-ad8e-005056a2d053", "工单类型", "Order", ETimeLimitType.DicData, TimeLimitBaseDataConsts.OrderType),
                 new TimeLimitBaseDataModel("f21424ff-b1ab-11ed-ad8e-005056a2d053", "证件类型", "Order", ETimeLimitType.DicData, TimeLimitBaseDataConsts.LicenceType),
                 new TimeLimitBaseDataModel("bda69be9-b1b2-11ed-ad8e-005056a2d053", "紧急程度", "Order", ETimeLimitType.Enum, TimeLimitBaseDataConsts.EmergencyLevel),
-                new TimeLimitBaseDataModel("cd3b29dd-b1bf-11ed-ad8e-005056a2d053","默认参数","ZengBie,KnowledgeAdd",ETimeLimitType.DefaultTime,TimeLimitBaseDataConsts.DefaultTime)
+                new TimeLimitBaseDataModel("cd3b29dd-b1bf-11ed-ad8e-005056a2d053","默认参数","ZengBie,AddKnowledgeAsync",ETimeLimitType.DefaultTime,TimeLimitBaseDataConsts.DefaultTime)
             };
         }
     }

+ 6 - 1
src/Hotline/Snapshot/OrderSnapshot.cs

@@ -45,7 +45,12 @@ public class OrderSnapshot : CreationSoftDeleteEntity
     /// 作业类型
     /// </summary>
     [SugarColumn(ColumnDescription = "作业类型")]
-    public EJobType? JobType { get; set; }
+    public int? JobType { get; set; }
+
+    /// <summary>
+    /// 作业类型
+    /// </summary>
+    public string? JobTypeName { get; set; }
 
     /// <summary>
     /// 经营单位类别

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików