ソースを参照

Merge branch 'master' of http://110.188.24.182:10023/Fengwo/hotline

xf 1 年間 前
コミット
ec49fd8667

+ 1 - 1
src/Hotline.Api/Controllers/IPPbxController.cs

@@ -94,7 +94,7 @@ namespace Hotline.Api.Controllers
         [HttpPost("on-duty")]
         public async Task<TrOnDutyResponseDto> OnDuty([FromBody] TrOnDutyDto dto)
         {
-            return await _userDomainService.TrOnDutyAsync(_sessionContext.RequiredUserId, dto.TelId, dto.TelNo,dto.TelPwd,dto.Description, HttpContext.RequestAborted);
+            return await _userDomainService.TrOnDutyAsync(_sessionContext.RequiredUserId,dto.TelNo, HttpContext.RequestAborted);
         }
 
         /// <summary>

+ 128 - 48
src/Hotline.Api/Controllers/KnowledgeController.cs

@@ -3,8 +3,8 @@ using Hotline.Application.Knowledge;
 using Hotline.FlowEngine.WorkflowModules;
 using Hotline.KnowledgeBase;
 using Hotline.KnowledgeBase.Notifies;
-using Hotline.Orders;
 using Hotline.Permissions;
+using Hotline.Quality;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Repository.SqlSugar.Ts;
 using Hotline.Settings;
@@ -13,11 +13,14 @@ using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.Knowledge;
 using Hotline.Share.Dtos.Order;
+using Hotline.Share.Dtos.Quality;
 using Hotline.Share.Enums.KnowledgeBase;
 using Hotline.Users;
 using MapsterMapper;
 using MediatR;
 using Microsoft.AspNetCore.Mvc;
+using Org.BouncyCastle.Utilities;
+using Polly.Caching;
 using SqlSugar;
 using XF.Domain.Authentications;
 using XF.Domain.Exceptions;
@@ -47,6 +50,7 @@ namespace Hotline.Api.Controllers
 		private readonly IRepository<KnowledgeCorrection> _knowledgeCorrectionRepository;
 		private readonly IRepository<KnowledgeCollect> _knowledgeCollectRepository;
 		private readonly ISystemDomainService _systemDomainService;
+		private readonly IRepository<KnowledgeComment> _knowledgeCommentRepository;
 
 
 		public KnowledgeController(
@@ -66,7 +70,8 @@ namespace Hotline.Api.Controllers
 		   IRepository<KnowledgeQuestions> knowledgeQuestionsRepository,
 		   IRepository<KnowledgeCorrection> knowledgeCorrectionRepository,
 		   IRepository<KnowledgeCollect> knowledgeCollectRepository,
-		   ISystemDomainService systemDomainService
+		   ISystemDomainService systemDomainService,
+		   IRepository<KnowledgeComment> knowledgeCommentRepository
 		   )
 		{
 			_knowledgeRepository = knowledgeRepository;
@@ -86,6 +91,7 @@ namespace Hotline.Api.Controllers
 			_knowledgeCorrectionRepository = knowledgeCorrectionRepository;
 			_knowledgeCollectRepository = knowledgeCollectRepository;
 			_systemDomainService = systemDomainService;
+			_knowledgeCommentRepository = knowledgeCommentRepository;
 		}
 
 		#endregion
@@ -109,13 +115,15 @@ namespace Hotline.Api.Controllers
 
 			kn.Status = EKnowledgeStatus.Drafts;
 			var id = await _knowledgeRepository.AddAsync(kn, HttpContext.RequestAborted);
-			if (addDto.Tags.Any()) await _repositoryts.AddVectorAsync(id, DateTime.Now, addDto.Tags, HttpContext.RequestAborted);
-			if (dto.Workflow != null)
+			if (dto.Workflow != null &&  !string.IsNullOrEmpty(id))
 			{
 				var startDto = _mapper.Map<StartWorkflowDto>(dto.Workflow);
 				startDto.DefinitionModuleCode = WorkflowModuleConsts.KnowledgeAdd;
 				startDto.Title = "知识库新增";
 				await StartFlow(id, WorkflowModuleConsts.KnowledgeAdd, EKnowledgeApplyType.Add, startDto);
+				var knowledge = await _knowledgeRepository.GetAsync(id);
+				knowledge.Status = EKnowledgeStatus.Auditing;
+				await _knowledgeRepository.UpdateAsync(knowledge, HttpContext.RequestAborted);
 			}
 			return id;
 		}
@@ -164,6 +172,17 @@ namespace Hotline.Api.Controllers
 				throw UserFriendlyException.SameMessage("知识上架失败");
 		}
 
+		/// <summary>
+		/// 知识库-标题
+		/// </summary>
+		/// <param name="title"></param>
+		/// <returns></returns>
+		[HttpGet("{Title}")]
+		public async Task<bool> KnowledgeTitle(string Title) {
+			var count = await _knowledgeRepository.Queryable().Where(x => x.Title == Title).CountAsync();
+			return count > 0;
+		}
+
 		/// <summary>
 		/// 知识库-修改
 		/// </summary>
@@ -180,15 +199,16 @@ namespace Hotline.Api.Controllers
 			if (knowledge.Status == EKnowledgeStatus.OnShelf || knowledge.Status == EKnowledgeStatus.Auditing)
 				throw UserFriendlyException.SameMessage("知识库数据不可修改");
 			_mapper.Map(dto.Data, knowledge);
-			if (update.Tags.Any()) await _repositoryts.UpdateVectorAsync(update.Id, update.Tags, HttpContext.RequestAborted);
-			await _knowledgeRepository.UpdateAsync(knowledge, HttpContext.RequestAborted);
+			//if (update.Tags.Any()) await _repositoryts.UpdateVectorAsync(update.Id, update.Tags, HttpContext.RequestAborted);
 			if (dto.Workflow != null)
 			{
 				var startDto = _mapper.Map<StartWorkflowDto>(dto.Workflow);
 				startDto.DefinitionModuleCode = WorkflowModuleConsts.KnowledgeAdd;
 				startDto.Title = "知识库修改";
 				await StartFlow(update.Id, WorkflowModuleConsts.KnowledgeUpdate, EKnowledgeApplyType.Update, startDto);
+				knowledge.Status = EKnowledgeStatus.Auditing;
 			}
+			await _knowledgeRepository.UpdateAsync(knowledge, HttpContext.RequestAborted);
 		}
 
 		/// <summary>
@@ -205,11 +225,13 @@ namespace Hotline.Api.Controllers
 			if (knowledge == null) return;
 			if (knowledge.Status == EKnowledgeStatus.OnShelf || knowledge.Status == EKnowledgeStatus.Auditing)
 				throw UserFriendlyException.SameMessage("知识库数据不可删除");
-			await _knowledgeRepository.RemoveAsync(knowledge, false, HttpContext.RequestAborted);
+			//await _knowledgeRepository.RemoveAsync(knowledge, false, HttpContext.RequestAborted);
+			knowledge.Status = EKnowledgeStatus.Auditing;
+			await _knowledgeRepository.UpdateAsync(knowledge, HttpContext.RequestAborted);
 			var startDto = _mapper.Map<StartWorkflowDto>(dto.Workflow);
 			startDto.DefinitionModuleCode = WorkflowModuleConsts.KnowledgeDelete;
 			startDto.Title = "知识库删除";
-			await StartFlow(delete.Id, WorkflowModuleConsts.KnowledgeDelete, EKnowledgeApplyType.Add, startDto);
+			await StartFlow(delete.Id, WorkflowModuleConsts.KnowledgeDelete, EKnowledgeApplyType.Delete, startDto);
 		}
 
 		/// <summary>
@@ -254,7 +276,6 @@ namespace Hotline.Api.Controllers
 			var knowledge = await _knowledgeDomainService.KnowledgeInfo(Id, HttpContext.RequestAborted);
 			if (knowledge is null)
 				throw UserFriendlyException.SameMessage("知识查询失败!");
-
 			//转化
 			var knowledgeShowInfoDto = _mapper.Map<KnowledgeInfoDto>(knowledge);
 
@@ -271,7 +292,14 @@ namespace Hotline.Api.Controllers
 			var collect = await _knowledgeCollectRepository.GetAsync(x => x.KnowledgeId == Id && x.CreatorId == _sessionContext.UserId);
 			if (collect != null)
 				knowledgeShowInfoDto.Collect = _mapper.Map<KnowledgeCollectDto>(collect);
-
+			//关联知识
+			var knowledges = await _knowledgeRepository.Queryable().In(x=>x.Id,knowledge.Knowledges).ToListAsync();
+			if (knowledges.Any())
+				knowledgeShowInfoDto.KnowledgeDtos = _mapper.Map<List<KnowledgeDto>>(knowledges);
+			//关键词
+			var knowledgeWords= await _knowledgeWrodRepository.Queryable().In(x => x.Id, knowledge.Keywords).ToListAsync();
+			if (knowledgeWords.Any())
+				knowledgeShowInfoDto.KeywordsDto = _mapper.Map<List<KnowledgeWordDto>>(knowledgeWords);
 			if (IsAddPv == true)
 				_mediator.Publish(new GetKnowledgeInfoNotify(knowledge));
 			return knowledgeShowInfoDto;
@@ -356,13 +384,18 @@ namespace Hotline.Api.Controllers
 		[HttpGet]
 		public async Task<PagedDto<KnowledgeDataDto>> GetKnowList([FromQuery] KnowPagedListDto pagedDto)
 		{
-			var type = new KnowledgeType();
-			var hotspot = new Hotspot();
-			var organize = new SystemOrganize();
+			var typeSpliceName = string.Empty;
+			var hotspotHotSpotFullName = string.Empty;
 			if (!string.IsNullOrEmpty(pagedDto.KnowledgeTypeId))
-				type = await _knowledgeTypeRepository.GetAsync(x => x.Id == pagedDto.KnowledgeTypeId);
+			{
+				var type = await _knowledgeTypeRepository.GetAsync(x => x.Id == pagedDto.KnowledgeTypeId);
+				typeSpliceName = type?.SpliceName;
+			}
 			if (!string.IsNullOrEmpty(pagedDto.HotspotId))
-				hotspot = await _hotspotTypeRepository.GetAsync(x => x.Id == pagedDto.HotspotId);
+			{
+				var hotspot = await _hotspotTypeRepository.GetAsync(x => x.Id == pagedDto.HotspotId);
+				hotspotHotSpotFullName = hotspot?.HotSpotFullName;
+			}
 			var (total, temp) = await _knowledgeRepository.Queryable()
 				.Includes(x => x.User)
 				.Includes(x => x.SystemOrganize)
@@ -373,8 +406,8 @@ namespace Hotline.Api.Controllers
 				.WhereIF(pagedDto.Status.HasValue, x => x.Status == pagedDto.Status)
 				.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(pagedDto.KnowledgeTypeId), x => type != null && x.KnowledgeType.SpliceName.EndsWith(type.SpliceName))
-				.WhereIF(!string.IsNullOrEmpty(pagedDto.HotspotId), x => hotspot != null && x.HotspotType.HotSpotFullName.EndsWith(hotspot.HotSpotFullName))
+				.WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.SpliceName.EndsWith(typeSpliceName!))
+				.WhereIF(!string.IsNullOrEmpty(hotspotHotSpotFullName), x => x.HotspotType.HotSpotFullName.EndsWith(hotspotHotSpotFullName!))
 				.WhereIF(!string.IsNullOrEmpty(pagedDto.CreateOrgId), x => x.HotspotType.CreatorId != null && x.HotspotType.CreatorId.EndsWith(pagedDto.CreateOrgId!))
 				.OrderByDescending(d => d.CreationTime)
 				//转分页数据
@@ -392,13 +425,18 @@ namespace Hotline.Api.Controllers
 		[HttpGet("knowretrieval")]
 		public async Task<PagedDto<KnowledgeRetrievalDataDto>> KnowRetrieval([FromQuery] KnowledgeRetrievalPagedListDto pagedDto)
 		{
-			var type = new KnowledgeType();
-			var hotspot = new Hotspot();
-			var organize = new SystemOrganize();
+			var typeSpliceName = string.Empty;
+			var hotspotHotSpotFullName = string.Empty;
 			if (!string.IsNullOrEmpty(pagedDto.KnowledgeTypeId))
-				type = await _knowledgeTypeRepository.GetAsync(x => x.Id == pagedDto.KnowledgeTypeId);
+			{
+				var type = await _knowledgeTypeRepository.GetAsync(x => x.Id == pagedDto.KnowledgeTypeId);
+				typeSpliceName = type?.SpliceName;
+			}
 			if (!string.IsNullOrEmpty(pagedDto.HotspotId))
-				hotspot = await _hotspotTypeRepository.GetAsync(x => x.Id == pagedDto.HotspotId);
+			{
+				var hotspot = await _hotspotTypeRepository.GetAsync(x => x.Id == pagedDto.HotspotId);
+				hotspotHotSpotFullName = hotspot?.HotSpotFullName;
+			}
 			var sugar = _knowledgeRepository
 				.Queryable()
 				.Includes(x => x.User)
@@ -409,9 +447,9 @@ namespace Hotline.Api.Controllers
 				.WhereIF(pagedDto.RetrievalType == EKnowledgeRetrievalType.All && !string.IsNullOrEmpty(pagedDto.Keyword), d => d.Title.Contains(pagedDto.Keyword!) || d.Content.Contains(pagedDto.Keyword!))// || d.Additions.Contains(pagedDto.Keyword)
 				.WhereIF(pagedDto.RetrievalType == EKnowledgeRetrievalType.Title && !string.IsNullOrEmpty(pagedDto.Keyword), d => d.Title.Contains(pagedDto.Keyword!))
 				.WhereIF(pagedDto.RetrievalType == EKnowledgeRetrievalType.Content && !string.IsNullOrEmpty(pagedDto.Keyword), d => d.Content.Contains(pagedDto.Keyword!))
-				.WhereIF(pagedDto.RetrievalType == EKnowledgeRetrievalType.Summary && !string.IsNullOrEmpty(pagedDto.Keyword), d => d.Summary.Contains(pagedDto.Keyword!))
-				.WhereIF(!string.IsNullOrEmpty(pagedDto.KnowledgeTypeId), x => type != null && x.KnowledgeType.SpliceName.EndsWith(type.SpliceName))
-				.WhereIF(!string.IsNullOrEmpty(pagedDto.HotspotId), x => hotspot != null && x.HotspotType.HotSpotFullName.EndsWith(hotspot.HotSpotFullName))
+				.WhereIF(pagedDto.RetrievalType == EKnowledgeRetrievalType.Summary && !string.IsNullOrEmpty(pagedDto.Keyword), d => d.Summary != null && d.Summary.Contains(pagedDto.Keyword!))
+				.WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.SpliceName.EndsWith(typeSpliceName!))
+				.WhereIF(!string.IsNullOrEmpty(hotspotHotSpotFullName), x => x.HotspotType.HotSpotFullName.EndsWith(hotspotHotSpotFullName!))
 				.WhereIF(!string.IsNullOrEmpty(pagedDto.CreateOrgId), x => x.HotspotType.CreatorId != null && x.HotspotType.CreatorId.EndsWith(pagedDto.CreateOrgId!))
 				.WhereIF(!string.IsNullOrEmpty(pagedDto.Attribution), x => x.Attribution == pagedDto.Attribution!);
 			switch (pagedDto.Sort)
@@ -525,16 +563,6 @@ namespace Hotline.Api.Controllers
 			return new PagedDto<KnowledgeApprovalDataDto>(total, _mapper.Map<IReadOnlyList<KnowledgeApprovalDataDto>>(items));
 		}
 
-		/// <summary>
-		/// 获取知识热点词
-		/// </summary>
-		/// <returns></returns>
-		[HttpGet("getkeyword")]
-		public async Task<List<string>> GetKeyWord()
-		{
-			return await _knowledgeDomainService.GetKeyWord();
-		}
-
 		/// <summary>
 		/// 工单受理知识检索
 		/// </summary>
@@ -968,8 +996,8 @@ namespace Hotline.Api.Controllers
 				await _knowledgeCollectRepository.UpdateAsync(collect, HttpContext.RequestAborted);
 			}
 			else {
-				var questions = _mapper.Map<KnowledgeCollect>(dto);
-				await _knowledgeCollectRepository.AddAsync(questions, HttpContext.RequestAborted);
+				var collectNew = _mapper.Map<KnowledgeCollect>(dto);
+				await _knowledgeCollectRepository.AddAsync(collectNew, HttpContext.RequestAborted);
 			}
 		}
 
@@ -989,23 +1017,75 @@ namespace Hotline.Api.Controllers
 					throw UserFriendlyException.SameMessage("当前知识已经评分");
 				collect.Score = dto.Score;
 				await _knowledgeCollectRepository.UpdateAsync(collect, HttpContext.RequestAborted);
-				//计算总分
-				var sugar = _knowledgeCollectRepository.Queryable().Where(x => x.KnowledgeId == dto.KnowledgeId);
-				var count = await sugar.CountAsync();
-				var collects = await sugar.SumAsync(x=>x.Score);
-				var score = collects / count;
-				var knowledge = await _knowledgeRepository.GetAsync(x => x.Id == dto.KnowledgeId);
-				if (knowledge != null)
-				{
-					knowledge.Score = score; 
-					await _knowledgeRepository.UpdateAsync(knowledge, HttpContext.RequestAborted);
-				}
 			}
 			else
 			{
 				var questions = _mapper.Map<KnowledgeCollect>(dto);
 				await _knowledgeCollectRepository.AddAsync(questions, HttpContext.RequestAborted);
 			}
+			//计算总分
+			var sugar = _knowledgeCollectRepository.Queryable().Where(x => x.KnowledgeId == dto.KnowledgeId);
+			var count = await sugar.CountAsync();
+			var collects = await sugar.SumAsync(x => x.Score);
+			var score = collects / count;
+			var knowledge = await _knowledgeRepository.GetAsync(x => x.Id == dto.KnowledgeId);
+			if (knowledge != null)
+			{
+				knowledge.Score = decimal.Round(score.Value,1);
+				await _knowledgeRepository.UpdateAsync(knowledge, HttpContext.RequestAborted);
+			}
+		}
+		#endregion
+
+		#region 知识评论
+
+		//[Permission(EPermission.AddKnowledgeCollect)]
+		[HttpPost("knowledge_comment")]
+		public async Task Add([FromBody] KnowledgeCommentAddDto dto)
+		{
+			var model = _mapper.Map<KnowledgeComment>(dto);
+			await _knowledgeCommentRepository.AddAsync(model, HttpContext.RequestAborted);
+			if (!string.IsNullOrEmpty(dto.ReplyId))
+			{
+				var comment = await _knowledgeCommentRepository.GetAsync(dto.ReplyId);
+				if (comment != null) 
+				{
+					comment.ReplyNum++;
+					await _knowledgeCommentRepository.UpdateAsync(comment, HttpContext.RequestAborted);
+				}
+			}
+		}
+
+		//[Permission(EPermission.DeleteQualityItem)]
+		[HttpDelete("itemBatch")]
+		public async Task Delete([FromBody] KnowledgeCommentDeleteDto dto)
+		{
+			await _knowledgeCommentRepository.RemoveAsync(x => x.Id == dto.Id);
+		}
+
+
+		//[Permission(EPermission.UpdateQualityItem)]
+		[HttpPut("item")]
+		public async Task Update([FromBody] KnowledgeCommentUpdateDto dto)
+		{
+			var comment = await _knowledgeCommentRepository.GetAsync(dto.Id, HttpContext.RequestAborted);
+			if (comment is null)
+				throw UserFriendlyException.SameMessage("无效评论");
+			_mapper.Map(dto, comment);
+			await _knowledgeCommentRepository.UpdateAsync(comment, HttpContext.RequestAborted);
+		}
+
+		//[Permission(EPermission.QualityItemList)]
+		[HttpGet("item/list")]
+		public async Task<List<KnowledgeCommentDto>> List([FromQuery] KnowledgeCommentListDto dto)
+		{
+			var comments = await _knowledgeCommentRepository.Queryable()
+				.WhereIF(!string.IsNullOrEmpty(dto.KnowledgeId), x => x.KnowledgeId == dto.KnowledgeId)
+				.WhereIF(!string.IsNullOrEmpty(dto.ReplyId), x => x.ReplyId == dto.ReplyId)
+				.WhereIF(dto.All.HasValue && dto.All == false, x => x.CreatorId == _sessionContext.UserId)
+				.OrderByDescending(x => x.CreationTime)
+				.ToListAsync();
+			return new List<KnowledgeCommentDto>(_mapper.Map<IReadOnlyList<KnowledgeCommentDto>>(comments));
 		}
 		#endregion
 	}

+ 1 - 1
src/Hotline.Application/Handlers/FlowEngine/EndWorkflowHandler.cs

@@ -78,7 +78,7 @@ public class EndWorkflowHandler : INotificationHandler<EndWorkflowNotify>
         switch (workflow.ModuleCode)
         {
             case WorkflowModuleConsts.KnowledgeAdd://新增知识库
-            //case WorkflowModuleConsts.KnowledgeUpdate://修改知识库
+            case WorkflowModuleConsts.KnowledgeUpdate://修改知识库
             case WorkflowModuleConsts.KnowledgeDelete://删除知识库
                 await _knowledgeDomainService.EndWorkKnowledge(workflow, cancellationToken);
                 break;

+ 133 - 0
src/Hotline.Share/Dtos/Knowledge/KnowledgeCommentDto.cs

@@ -0,0 +1,133 @@
+using Hotline.Share.Enums.Quality;
+using Hotline.Share.Requests;
+
+namespace Hotline.Share.Dtos.Knowledge
+{
+	public class KnowledgeCommentDto : KnowledgeCommentBaseDto
+	{
+		/// <summary>
+		/// 知识库ID
+		/// </summary>
+		public string KnowledgeId { get; set; }
+
+		/// <summary>
+		/// 知识库
+		/// </summary>
+		public KnowledgeDto Knowledge { get; set; }
+
+		/// <summary>
+		/// 评论内容
+		/// </summary>
+		public string? Content { get; set; }
+
+		/// <summary>
+		/// 回复ID
+		/// </summary>
+		public string? ReplyId { get; set; }
+
+		/// <summary>
+		/// 匿名
+		/// </summary>
+		public bool Cryptonym { get; set; }
+
+		/// <summary>
+		/// 回复数
+		/// </summary>
+		public int ReplyNum { get; set; } = 0;
+	}
+
+	public class KnowledgeCommentAddDto
+	{
+		/// <summary>
+		/// 知识库ID
+		/// </summary>
+		public string KnowledgeId { get; set; }
+
+		/// <summary>
+		/// 知识库
+		/// </summary>
+		public KnowledgeDto Knowledge { get; set; }
+
+		/// <summary>
+		/// 评论内容
+		/// </summary>
+		public string? Content { get; set; }
+
+		/// <summary>
+		/// 回复ID
+		/// </summary>
+		public string? ReplyId { get; set; }
+
+		/// <summary>
+		/// 匿名
+		/// </summary>
+		public bool Cryptonym { get; set; }
+
+		/// <summary>
+		/// 回复数
+		/// </summary>
+		public int ReplyNum { get; set; } = 0;
+	}
+
+	public class KnowledgeCommentDeleteDto
+	{
+		public string Id { get; set; }
+	}
+
+	public class KnowledgeCommentUpdateDto : KnowledgeCommentAddDto
+	{
+		public string Id { get; set; }
+	}
+	public class KnowledgeCommentBaseDto
+	{
+		public DateTime? LastModificationTime { get; set; }
+
+		public bool IsDeleted { get; set; }
+
+		/// <summary>
+		/// 删除时间
+		/// </summary>
+		public DateTime? DeletionTime { get; set; }
+
+
+		/// <summary>
+		/// 创建时间
+		/// </summary>
+		public DateTime CreationTime { get; set; }
+
+		public string Id { get; set; }
+
+		/// <summary>
+		/// 组织Id
+		/// </summary>
+		public string? CreatorOrgId { get; set; }
+
+
+		public string? CreatorOrgName { get; set; }
+
+		/// <summary>
+		/// 创建人
+		/// </summary>
+		public string? CreatorId { get; set; }
+
+		public string? CreatorName { get; set; }
+	}
+
+	public record KnowledgeCommentListDto : PagedKeywordRequest
+	{
+		/// <summary>
+		/// 知识库ID
+		/// </summary>
+		public string KnowledgeId { get; set; }
+
+		/// <summary>
+		/// 回复ID
+		/// </summary>
+		public string? ReplyId { get; set; }
+
+		/// <summary>
+		/// 查询全部
+		/// </summary>
+		public bool? All { get; set; }
+	}
+}

+ 15 - 6
src/Hotline.Share/Dtos/Knowledge/KnowledgeDto.cs

@@ -130,18 +130,13 @@ namespace Hotline.Share.Dtos.Knowledge
         /// <summary>
         /// 关键词
         /// </summary>
-        public string Keywords { get; set; }
+        public List<string> Keywords { get; set; }
 
         /// <summary>
         /// 附件
         /// </summary>
         public List<string> Additions { get; set; }
 
-        /// <summary>
-        /// 关联知识
-        /// </summary>
-        public List<string> Knowledges { get; set; }
-
         /// <summary>
         /// 外部数据(为前端提供级联功能)
         /// </summary>
@@ -186,6 +181,20 @@ namespace Hotline.Share.Dtos.Knowledge
 
         public KnowledgeCollectDto Collect { get; set; }
 
+        /// <summary>
+        /// 关联知识
+        /// </summary>
+        public List<string> Knowledges { get; set; }
+		/// <summary>
+		/// 关联知识对象
+		/// </summary>
+		public List<KnowledgeDto> KnowledgeDtos { get; set; }
+
+		/// <summary>
+		/// 关键词
+		/// </summary>
+		public List<KnowledgeWordDto> KeywordsDto { get; set; }
+
 	}
 
     public record KnowledgeBaseDto {

+ 0 - 5
src/Hotline.Share/Dtos/TrCallCenter/TrTelDao.cs

@@ -11,8 +11,6 @@ namespace Hotline.Share.Dtos.TrCallCenter
         public string TelNo { get; set; }
 
         public string Description { get; set; }
-
-        public string Password { get; set; }
     }
 
     #endregion
@@ -62,10 +60,7 @@ namespace Hotline.Share.Dtos.TrCallCenter
 
     public class TrOnDutyDto
     {
-        public string? TelId { get; set; }
         public string? TelNo { get; set; }
-        public string? TelPwd { get; set; }
-        public string? Description { get; set; }
     }
 
     public class TrOnDutyResponseDto

+ 14 - 0
src/Hotline/Caching/Interfaces/IUserCacheManager.cs

@@ -11,6 +11,13 @@ namespace Hotline.Caching.Interfaces
         /// <returns></returns>
         Work GetWorkByUser(string userId);
 
+        /// <summary>
+        /// 查询用户当前工作记录(不抛异常)
+        /// </summary>
+        /// <param name="userId"></param>
+        /// <returns></returns>
+        Work? GetWorkByUserNoExp(string userId);
+
         /// <summary>
         /// 查询分机当前工作记录
         /// </summary>
@@ -18,6 +25,13 @@ namespace Hotline.Caching.Interfaces
         /// <returns></returns>
         Work GetWorkByTel(string telNo);
 
+        /// <summary>
+        /// 查询分机当前工作记录
+        /// </summary>
+        /// <param name="telNo"></param>
+        /// <returns></returns>
+        Work? GetWorkByTelNoExp(string telNo);
+
         /// <summary>
         /// 查询分机是否处于工作
         /// </summary>

+ 33 - 0
src/Hotline/Caching/Services/UserCacheManager.cs

@@ -1,5 +1,6 @@
 using Hotline.Caching.Interfaces;
 using Hotline.Users;
+using System.Drawing.Printing;
 using XF.Domain.Cache;
 using XF.Domain.Dependency;
 using XF.Domain.Exceptions;
@@ -37,6 +38,22 @@ public class UserCacheManager : IUserCacheManager, IScopeDependency
         return work;
     }
 
+    /// <summary>
+    /// 查询用户当前工作记录(不抛异常)
+    /// </summary>
+    /// <param name="userId"></param>
+    /// <returns></returns>
+    public Work? GetWorkByUserNoExp(string userId)
+    {
+        var work = _cacheWork.GetOrSet(Work.GetKey(KeyMode.UserId, userId), k =>
+        {
+            var dbWork = _workRepository.GetCurrentWorkByUserAsync(userId)
+                .GetAwaiter().GetResult();
+            return dbWork;
+        });
+        return work;
+    }
+
     /// <summary>
     /// 查询分机当前工作记录
     /// </summary>
@@ -56,6 +73,22 @@ public class UserCacheManager : IUserCacheManager, IScopeDependency
         return work;
     }
 
+    /// <summary>
+    /// 查询分机当前工作记录
+    /// </summary>
+    /// <param name="telNo"></param>
+    /// <returns></returns>
+    public Work? GetWorkByTelNoExp(string telNo)
+    {
+        var work = _cacheWork.GetOrSet(Work.GetKey(KeyMode.TelNo, telNo), k =>
+        {
+            var dbWork = _workRepository.GetCurrentWorkByTelAsync(telNo)
+                .GetAwaiter().GetResult();
+            return dbWork;
+        });
+        return work;
+    }
+
     /// <summary>
     /// 查询分机是否处于工作
     /// </summary>

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

@@ -59,12 +59,6 @@ namespace Hotline.KnowledgeBase
         /// <returns></returns>
         Task UpdateWorkAssign(FlowAssignInfo assignInfo, string? knowledgeFlowId, CancellationToken cancellationToken);
 
-        /// <summary>
-        /// 获取关键词
-        /// </summary>
-        /// <returns></returns>
-        Task<List<string>> GetKeyWord();
-
         /// <summary>
         /// 流程终止,处理知识状态
         /// </summary>

+ 2 - 2
src/Hotline/KnowledgeBase/Knowledge.cs

@@ -89,8 +89,8 @@ public class Knowledge : WorkflowEntity//   WorkflowEntity  FullStateEntity
     /// <summary>
     /// 关键词
     /// </summary>
-    [SugarColumn(IsNullable = true, ColumnDescription = "关键词")]
-    public string Keywords { get; set; }
+    [SugarColumn(ColumnDataType = "json", IsJson = true, IsNullable = true, ColumnDescription = "关键词")]
+    public List<string> Keywords { get; set; }
 
 	/// <summary>
 	/// 版本号

+ 52 - 0
src/Hotline/KnowledgeBase/KnowledgeComment.cs

@@ -0,0 +1,52 @@
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Repository;
+
+namespace Hotline.KnowledgeBase
+{
+	[Description("知识库评论")]
+	public class KnowledgeComment : FullStateEntity
+	{
+		/// <summary>
+		/// 知识库ID
+		/// </summary>
+		[SugarColumn(ColumnDescription = "知识库ID")]
+		public string KnowledgeId { get; set; }
+
+		/// <summary>
+		/// 知识库
+		/// </summary>
+		[Navigate(NavigateType.OneToOne, nameof(KnowledgeId))]
+		public Knowledge Knowledge { get; set; }
+
+		/// <summary>
+		/// 评论内容
+		/// </summary>
+		[SugarColumn(ColumnDescription = "评论内容", ColumnDataType = "varchar(2000)")]
+		public string? Content { get; set; }
+
+		/// <summary>
+		/// 回复ID
+		/// </summary>
+		[SugarColumn(ColumnDescription = "回复ID")]
+		public string? ReplyId { get; set; }
+
+		/// <summary>
+		/// 匿名
+		/// </summary>
+		[SugarColumn(ColumnDescription = "匿名")]
+		public bool Cryptonym { get; set; }= false;
+
+		/// <summary>
+		/// 回复数
+		/// </summary>
+		[SugarColumn(ColumnDescription = "回复数")]
+		public int ReplyNum { get; set; } = 0;
+
+	}
+}

+ 0 - 37
src/Hotline/KnowledgeBase/KnowledgeDomainService.cs

@@ -198,43 +198,6 @@ namespace Hotline.KnowledgeBase
 
         }
 
-        /// <summary>
-        /// 获取关键词
-        /// </summary>
-        /// <returns></returns>
-        public async Task<List<string>> GetKeyWord()
-        {
-            var temp = await _knowledgeRepository
-              .Queryable()
-              .Select((p) => new
-              {
-                  index = SqlFunc.RowNumber($"{p.Version} desc ", $"{p.Code}"),
-                  p.Version,
-                  p.PageView,
-                  p.Keywords,
-                  p.Status
-              })
-           //将结果合并成一个表
-           .MergeTable()
-           //取第一条数据
-           .Where(d => d.index == 1 && d.Status == EKnowledgeStatus.OnShelf && d.Keywords != null && d.Keywords != "")
-           //根据点击量在排序
-           .OrderByDescending(p => p.PageView)
-           .Take(10)//取前10条
-           .ToListAsync();
-
-            //将数据组装到集合
-            List<string> strList = new();
-            temp.ForEach(p =>
-            {
-                if (!string.IsNullOrEmpty(p.Keywords))
-                    strList.AddRange(p.Keywords.Split(',').ToList());//添加数据
-            });
-
-            //取10条数据返回
-            return strList.Distinct().Take(10).ToList();
-        }
-
         /// <summary>
         /// 流程终止,处理知识状态
         /// </summary>

+ 26 - 0
src/Hotline/Permissions/EPermission.cs

@@ -952,6 +952,32 @@ namespace Hotline.Permissions
 		AddKnowledgeScore = 400902,
 		#endregion
 
+		#region 知识评论
+		/// <summary>
+		/// 知识提问列表
+		/// </summary>
+		[Display(GroupName = "KnowledgeComment", Name = "知识提问列表", Description = "知识提问列表")]
+		KnowledgeCommentList = 401000,
+
+		/// <summary>
+		/// 新增知识提问
+		/// </summary>
+		[Display(GroupName = "KnowledgeComment", Name = "新增知识提问", Description = "新增知识提问")]
+		AddKnowledgeComment = 401001,
+
+		/// <summary>
+		/// 删除知识提问
+		/// </summary>
+		[Display(GroupName = "KnowledgeComment", Name = "删除知识提问", Description = "删除知识提问")]
+		DeleteKnowledgeComment = 401002,
+
+		/// <summary>
+		/// 修改知识提问
+		/// </summary>
+		[Display(GroupName = "KnowledgeComment", Name = "修改知识提问", Description = "修改知识提问")]
+		UpdateKnowledgeComment = 401003,
+		#endregion
+
 		#endregion
 
 		#region 业务管理(500)

+ 1 - 3
src/Hotline/Users/IUserDomainService.cs

@@ -28,12 +28,10 @@ namespace Hotline.Users
         /// 上班
         /// </summary>
         /// <param name="userId"></param>
-        /// <param name="TelId"></param>
         /// <param name="TelNo"></param>
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
-
-        Task<TrOnDutyResponseDto> TrOnDutyAsync(string userId, string? TelId, string? TelNo,string? TelPwd,string? Description, CancellationToken cancellationToken);
+        Task<TrOnDutyResponseDto> TrOnDutyAsync(string userId,  string TelNo, CancellationToken cancellationToken);
 
         /// <summary>
         /// 下班

+ 36 - 45
src/Hotline/Users/UserDomainService.cs

@@ -1,10 +1,13 @@
 using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Devices;
 using Hotline.CallCenter.Tels;
+using Hotline.Share.Dtos.CallCenter;
 using Hotline.Share.Dtos.TrCallCenter;
 using Hotline.Share.Dtos.Users;
 using MapsterMapper;
+using System.Security.Cryptography;
 using Tr.Sdk;
+using XF.Domain.Authentications;
 using XF.Domain.Cache;
 using XF.Domain.Constants;
 using XF.Domain.Dependency;
@@ -24,6 +27,7 @@ namespace Hotline.Users
         private readonly IMapper _mapper;
         private readonly ISystemSettingCacheManager _systemSettingCacheManager;
         private readonly ITrClient _trClient;
+        private readonly ISessionContext _sessionContext;
 
         public UserDomainService(
             IRepository<User> userRepository,
@@ -34,7 +38,9 @@ namespace Hotline.Users
             IMapper mapper,
             ITelCacheManager telCacheManager,
             ISystemSettingCacheManager systemSettingCacheManager,
-            ITrClient trClient)
+            ITrClient trClient,
+            ISessionContext sessionContext
+            )
         {
             _userRepository = userRepository;
             _workRepository = workRepository;
@@ -45,6 +51,7 @@ namespace Hotline.Users
             _telCacheManager = telCacheManager;
             _systemSettingCacheManager = systemSettingCacheManager;
             _trClient = trClient;
+            _sessionContext = sessionContext;
         }
 
         /// <summary>
@@ -119,63 +126,48 @@ namespace Hotline.Users
         /// 上班
         /// </summary>
         /// <param name="userId"></param>
-        /// <param name="TelId"></param>
-        /// <param name="TelNo"></param>
+        /// <param name="telNo"></param>
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
-        public async Task<TrOnDutyResponseDto> TrOnDutyAsync(string userId, string? TelId, string? TelNo, string? TelPwd, string? Description, CancellationToken cancellationToken)
+        public async Task<TrOnDutyResponseDto> TrOnDutyAsync(string userId, string telNo, CancellationToken cancellationToken)
         {
-            var workingTel = await _workRepository.GetCurrentWorkByUserAsync(userId, cancellationToken);
-            if (workingTel is not null)
+            var work = _userCacheManager.GetWorkByUserNoExp(userId);
+            if(work is not null)
             {
-                return new TrOnDutyResponseDto() { TelNo = workingTel.TelNo, TelPwd = workingTel.TelPwd, Description = workingTel.Description };
+                if(work.TelNo == telNo)
+                {
+                    return new TrOnDutyResponseDto() { TelNo = work.TelNo, TelPwd = work.TelPwd, Description = work.Description };
+                }
+                else
+                {
+                    throw UserFriendlyException.SameMessage("当前用户已签入其他分机");
+                }
             }
 
-            //检查配置
-            bool IsNeedTelNo = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.IsNeedTelNo).SettingValue[0]);
-            if (IsNeedTelNo && string.IsNullOrEmpty(TelNo))
+            var telWork = _userCacheManager.GetWorkByTelNoExp(telNo);
+            if (telWork is not null)
             {
-                throw UserFriendlyException.SameMessage("当前配置需要选择分机号签入");
+                throw UserFriendlyException.SameMessage("当前分机已被占用");
             }
 
 
-
-
-            var user = await _userRepository.GetAsync(userId, cancellationToken);
-            if (user is null)
-                throw UserFriendlyException.SameMessage("无效的用户编号");
-
             bool IsTelNeedVerify = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.IsTelNeedVerify).SettingValue[0]);
-            var rsp = new TrOnDutyResponseDto();
-            if (!IsNeedTelNo)
-            {
-                TelNo = user.DefaultTelNo;
-                var telModel = await _trClient.QueryTelsAsync(new Tr.Sdk.Tels.QueryTelRequest() { TelNo = TelNo }, cancellationToken);
-                TelId = telModel[0].Id;
-                rsp.TelPwd = telModel[0].Password;
-                rsp.Description = telModel[0].Description;
-            }
 
-
-            rsp.TelNo = TelNo;
-
-
-            var telIsWorking = await _workRepository.AnyAsync(d => d.TelId == TelId && !d.EndTime.HasValue, cancellationToken);
-            if (telIsWorking)
-                throw UserFriendlyException.SameMessage("当前分机已被占用");
-
-            var work = new Work(userId, user.Name, TelId, TelNo, rsp.TelPwd, rsp.Description);
-            await _workRepository.AddAsync(work, cancellationToken);
-
-            if (IsTelNeedVerify)
+            var telModel = await _trClient.QueryTelsAsync(new Tr.Sdk.Tels.QueryTelRequest() { TelNo = telNo }, cancellationToken);
+            if (telModel!=null && telModel.Count>0)
             {
-                rsp.TelPwd = string.Empty;
+                work = new Work(userId, _sessionContext.UserName, telModel[0].Id, telNo, telModel[0].Password, telModel[0].Description);
+                await _workRepository.AddAsync(work, cancellationToken);
+                if (IsTelNeedVerify)
+                {
+                    return new TrOnDutyResponseDto() { TelNo = telNo, TelPwd = "", Description = telModel[0].Description };
+                }
+                else
+                {
+                    return new TrOnDutyResponseDto() { TelNo = telNo, TelPwd = telModel[0].Password, Description = telModel[0].Description };
+                }
             }
-            return rsp;
-        }
-
-        public async Task<TrOnDutyResponseDto> TrOnDutyAsync(string userId, string telNo, CancellationToken cancellationToken)
-        {
+            throw UserFriendlyException.SameMessage("签入异常,未查询到对应分机信息");
             /*
              *var work = 缓存.get(userId);
              * if(work.TelNo == telNo)
@@ -192,7 +184,6 @@ namespace Hotline.Users
              * 根据配置 return new();
              *
              */
-            throw new NotImplementedException();
         }