Browse Source

merge kn to test

xf 3 months ago
parent
commit
068e0eb8f7
42 changed files with 2074 additions and 779 deletions
  1. 7 8
      Hotline.sln
  2. 93 74
      src/Hotline.Api/Controllers/KnowledgeCommonController.cs
  3. 499 64
      src/Hotline.Api/Controllers/KnowledgeController.cs
  4. 0 3
      src/Hotline.Api/Controllers/WebPortalController.cs
  5. 4 1
      src/Hotline.Api/StartupExtensions.cs
  6. 18 14
      src/Hotline.Application.Tests/Application/KnowApplicationTest.cs
  7. 3 1
      src/Hotline.Application.Tests/TestBase.cs
  8. 1 1
      src/Hotline.Application/ExportExcel/ExportApplication.cs
  9. 1 1
      src/Hotline.Application/ExportWord/WordHelper.cs
  10. 1 0
      src/Hotline.Application/Hotline.Application.csproj
  11. 128 55
      src/Hotline.Application/Knowledge/KnowApplication.cs
  12. 39 2
      src/Hotline.Application/Mappers/KnowledgeMapperConfigs.cs
  13. 17 0
      src/Hotline.Pdf/Hotline.Pdf.csproj
  14. 52 0
      src/Hotline.Pdf/QuestPdfManager.cs
  15. 16 0
      src/Hotline.Pdf/QuestPdfManagerStartupExtensions.cs
  16. 19 0
      src/Hotline.Share/Dtos/Knowledge/ApproveBatchRequest.cs
  17. 14 0
      src/Hotline.Share/Dtos/Knowledge/DeleteKnowledgeDto.cs
  18. 15 0
      src/Hotline.Share/Dtos/Knowledge/ExportKnowledgeRequest.cs
  19. 98 0
      src/Hotline.Share/Dtos/Knowledge/KnowPagedListDto.cs
  20. 74 0
      src/Hotline.Share/Dtos/Knowledge/KnowledgeApproveDto.cs
  21. 29 28
      src/Hotline.Share/Dtos/Knowledge/KnowledgeDataDto.cs
  22. 107 125
      src/Hotline.Share/Dtos/Knowledge/KnowledgeDto.cs
  23. 32 0
      src/Hotline.Share/Dtos/Knowledge/KnowledgeInfoDto.cs
  24. 2 111
      src/Hotline.Share/Dtos/Knowledge/KnowledgePagedDto.cs
  25. 14 0
      src/Hotline.Share/Dtos/Knowledge/OffShelfKnowledgeDto.cs
  26. 11 0
      src/Hotline.Share/Dtos/Knowledge/PublishBatchRequest.cs
  27. 28 0
      src/Hotline.Share/Dtos/Knowledge/QueryKnowledgeApprovePagedRequest.cs
  28. 18 0
      src/Hotline.Share/Enums/KnowledgeBase/EKnowledgeApproveStatus.cs
  29. 21 0
      src/Hotline.Share/Enums/KnowledgeBase/EKnowledgeApproveType.cs
  30. 88 25
      src/Hotline.Share/Enums/KnowledgeBase/EKnowledgeStatus.cs
  31. 1 1
      src/Hotline/FlowEngine/WorkflowModules/WorkflowModuleConsts.cs
  32. 26 7
      src/Hotline/KnowledgeBase/IKnowledgeDomainService.cs
  33. 198 148
      src/Hotline/KnowledgeBase/Knowledge.cs
  34. 80 0
      src/Hotline/KnowledgeBase/KnowledgeApprove.cs
  35. 174 52
      src/Hotline/KnowledgeBase/KnowledgeDomainService.cs
  36. 46 0
      src/Hotline/KnowledgeBase/KnowledgeImportTemplate.cs
  37. 15 12
      src/Hotline/KnowledgeBase/KnowledgeRelationType.cs
  38. 4 0
      src/Hotline/KnowledgeBase/KnowledgeType.cs
  39. 63 43
      src/Hotline/KnowledgeBase/KnowledgeTypeDomainService.cs
  40. 3 2
      src/Hotline/KnowledgeBase/KnowledgeTypeOrg.cs
  41. 14 0
      src/Hotline/Pdf/IPdfManager.cs
  42. 1 1
      src/Hotline/Settings/TimeLimits/TimeLimit.cs

+ 7 - 8
Hotline.sln

@@ -63,6 +63,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TianQue.Sdk", "src\TianQue.
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hotline.Ai.XingTang", "src\Hotline.Ai.XingTang\Hotline.Ai.XingTang.csproj", "{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hotline.Pdf", "src\Hotline.Pdf\Hotline.Pdf.csproj", "{3AB75B51-A69D-4145-A564-1D9D1695992E}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -157,14 +159,10 @@ Global
 		{75215667-65AF-4B7B-85E7-3140239B30CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{75215667-65AF-4B7B-85E7-3140239B30CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{75215667-65AF-4B7B-85E7-3140239B30CC}.Release|Any CPU.Build.0 = Release|Any CPU
-		{6CF27647-D0E0-4D17-80FB-3EE57864A2B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{6CF27647-D0E0-4D17-80FB-3EE57864A2B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{6CF27647-D0E0-4D17-80FB-3EE57864A2B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{6CF27647-D0E0-4D17-80FB-3EE57864A2B4}.Release|Any CPU.Build.0 = Release|Any CPU
-		{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{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
@@ -195,6 +193,7 @@ Global
 		{9F99C272-5BC2-452C-9D97-BC756AF04669} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{37784861-ABC0-41F4-87B4-2E08A89A2C42} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{75215667-65AF-4B7B-85E7-3140239B30CC} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
+		{3AB75B51-A69D-4145-A564-1D9D1695992E} = {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}
 	EndGlobalSection

+ 93 - 74
src/Hotline.Api/Controllers/KnowledgeCommonController.cs

@@ -24,19 +24,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 +45,7 @@ namespace Hotline.Api.Controllers
             _sessionContext = sessionContext;
             _systemOrganizeRepository = systemOrganizeRepository;
             _hotspotTypeRepository = hotspotTypeRepository;
-		}
+        }
         #endregion
 
         #region 知识分类
@@ -93,8 +93,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("查询失败!");
@@ -140,23 +140,42 @@ namespace Hotline.Api.Controllers
         /// <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)
         {
-            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 query = _knowledgeTypeRepository.Queryable();
+            if (!_sessionContext.OrgIsCenter)
+                query.Where(x => x.Orgs.Any(s => s.Id == _sessionContext.RequiredOrgId));
+            return await query.WhereIF(IsEnable.HasValue, x => x.IsEnable == IsEnable)
+                 .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)
+                     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);
+                 })
+                 .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 +185,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 +214,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 +239,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

+ 499 - 64
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,28 @@ 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 Hotline.Share.Requests;
 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 System.Threading;
+using Hotline.Caching.Interfaces;
+using Hotline.Import;
+using Hotline.Caching.Services;
+using Hotline.Share.Enums.Order;
+using MiniExcelLibs;
+using DocumentFormat.OpenXml.Wordprocessing;
+using Hotline.Pdf;
+using Microsoft.AspNetCore.Authorization;
 
 namespace Hotline.Api.Controllers
 {
@@ -57,6 +57,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 +85,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 +92,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 +122,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 +150,6 @@ namespace Hotline.Api.Controllers
             _systemOrganizeRepository = systemOrganizeRepository;
             _fileRepository = fileRepository;
             _capPublisher = capPublisher;
-            _knowledgeRelationTypeRepository = knowledgeRelationTypeRepository;
             _bulletinApplication = bulletinApplication;
             _knowledgeCollectGroupRepository = knowledgeCollectGroupRepository;
             _exportApplication = exportApplication;
@@ -155,6 +158,9 @@ namespace Hotline.Api.Controllers
             _knowledgePvepository = knowledgePvepository;
             _knowledgeWordRepository = knowledgeWordRepository;
             _knowledgeHotWordRepository = knowledgeHotWordRepository;
+            _knowledgeApproRepository = knowledgeApproRepository;
+            this._systemSettingCacheManager = _systemSettingCacheManager;
+            _pdfManager = pdfManager;
             _appOptions = appOptions;
         }
 
@@ -190,7 +196,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 +214,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 +265,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 +279,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 +382,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 +411,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 +569,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 +624,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 +644,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 +680,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 +702,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)
@@ -684,6 +823,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,14 +831,16 @@ 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
@@ -821,6 +963,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 && d.ParentId == null)
+                .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 +1211,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 +1895,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)
+                .OrderBy(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
     }
 }

+ 0 - 3
src/Hotline.Api/Controllers/WebPortalController.cs

@@ -52,7 +52,6 @@ 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;
@@ -77,7 +76,6 @@ namespace Hotline.Api.Controllers
             IRepository<OrderVisitDetail> orderVisitDetailRepository,
             IBulletinApplication bulletinApplication,
             IRepository<OldPublicData> oldPublicDataRepository,
-            IRepository<KnowledgeRelationType> knowledgeRelationTypeRepository,
             IRepository<KnowledgeType> knowledgeTypeRepository,
             IRepository<Knowledge> knowledgeRepository,
             ISystemDicDataCacheManager systemDicDataCacheManager,
@@ -103,7 +101,6 @@ namespace Hotline.Api.Controllers
             _orderVisitDetailRepository = orderVisitDetailRepository;
             _bulletinApplication = bulletinApplication;
             _oldPublicDataRepository = oldPublicDataRepository;
-            _knowledgeRelationTypeRepository = knowledgeRelationTypeRepository;
             _knowledgeTypeRepository = knowledgeTypeRepository;
             _knowledgeRepository = knowledgeRepository;
             _systemDicDataCacheManager = systemDicDataCacheManager;

+ 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();
 

+ 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]

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

+ 1 - 1
src/Hotline.Application/ExportExcel/ExportApplication.cs

@@ -42,7 +42,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
             };
         }
 

+ 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();

+ 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" />

+ 128 - 55
src/Hotline.Application/Knowledge/KnowApplication.cs

@@ -38,15 +38,15 @@ namespace Hotline.Application.Knowledge
         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)
         {
             _knowledgeRepository = knowledgeRepository;
             _knowledgeApplyRepository = knowledgeApplyRepository;
@@ -57,9 +57,9 @@ namespace Hotline.Application.Knowledge
             _knowledgeWordRepository = knowledgeWordRepository;
             _knowledgePvepository = knowledgePvepository;
             _knowledgeHotWordRepository = knowledgeHotWordRepository;
-			_appOptions  = appOptions;
+            _appOptions = appOptions;
 
-		}
+        }
 
         /// <summary>
         /// 知识库查询
@@ -68,17 +68,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 +90,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 +198,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));
         }
@@ -238,7 +311,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 +340,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,7 +416,7 @@ namespace Hotline.Application.Knowledge
 
             if (!_sessionContext.OrgIsCenter)
             {
-	            dto.Attribution = "部门知识库";
+                dto.Attribution = "部门知识库";
             }
 
             var sugar = _knowledgeRepository
@@ -387,13 +460,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
     }
 }

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

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

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

+ 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>
     /// 知识更新

+ 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 - 148
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;
 
@@ -21,31 +22,31 @@ namespace Hotline.KnowledgeBase;
 [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 +60,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 +102,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 +123,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 +156,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
+    }
+}

+ 174 - 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;
@@ -179,24 +184,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 +361,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 +396,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; }
+    }
+}

+ 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>
 		/// 知识库类型名称

+ 4 - 0
src/Hotline/KnowledgeBase/KnowledgeType.cs

@@ -1,5 +1,6 @@
 using SqlSugar;
 using System.ComponentModel;
+using Hotline.Settings;
 using XF.Domain.Repository;
 
 namespace Hotline.KnowledgeBase;
@@ -50,4 +51,7 @@ public class KnowledgeType : FullStateEntity
     /// </summary>
     [Navigate(NavigateType.OneToMany, nameof(KnowledgeTypeOrg.TypeId))]
     public List<KnowledgeTypeOrg>  KnowledgeTypeOrgs { get; set; }
+
+    [Navigate(typeof(KnowledgeTypeOrg), nameof(KnowledgeTypeOrg.TypeId), nameof(KnowledgeTypeOrg.OrgId))]
+    public List<SystemOrganize> Orgs { 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>

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

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