Переглянути джерело

Merge branch 'release' of http://git.12345lm.cn/Fengwo/hotline into release

Dun.Jason 6 місяців тому
батько
коміт
ace2217dcf

+ 18 - 74
src/Hotline.Api/Controllers/KnowledgeController.cs

@@ -330,7 +330,7 @@ namespace Hotline.Api.Controllers
         /// <returns></returns>
         [HttpPost("batch_audit")]
         public async Task KnowledgeBatchAuditAsync()
-        { 
+        {
             // TODO: qcy 批量审核 
         }
 
@@ -340,8 +340,8 @@ namespace Hotline.Api.Controllers
         /// <param name="title"></param>
         /// <returns></returns>
         [HttpGet("participle")]
-        public async Task<IList<KnowledgeWordOutDto>> KnowledgeKeyWord([FromQuery]string title)
-        { 
+        public async Task<IList<KnowledgeWordOutDto>> KnowledgeKeyWord([FromQuery] string title)
+        {
             return await _knowApplication.TitleParticiple(title);
         }
 
@@ -620,6 +620,17 @@ namespace Hotline.Api.Controllers
             return _exportApplication.GetExcelFile(dto, items, "知识明细导出");
         }
 
+        /// <summary>
+        /// 来电弹窗知识检索
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("knowretrieval")]
+        public async Task<PagedDto<KnowledgeRetrievalDataDto>> KnowRetrievalWindow([FromBody] KnowledgeRetrievalPagedListDto dto)
+        {
+            return (await _knowApplication.KnowRetrievalAsync(dto)).ToPaged();
+        }
+
         /// <summary>
         /// 知识检索
         /// </summary>
@@ -628,75 +639,7 @@ namespace Hotline.Api.Controllers
         [HttpGet("knowretrieval")]
         public async Task<PagedDto<KnowledgeRetrievalDataDto>> KnowRetrieval([FromQuery] KnowledgeRetrievalPagedListDto dto)
         {
-            var typeSpliceName = string.Empty;
-            var hotspotHotSpotFullName = string.Empty;
-            if (!string.IsNullOrEmpty(dto.KnowledgeTypeId))
-            {
-                var type = await _knowledgeTypeRepository.GetAsync(x => x.Id == dto.KnowledgeTypeId);
-                typeSpliceName = type?.SpliceName;
-            }
-            if (!string.IsNullOrEmpty(dto.HotspotId))
-            {
-                var hotspot = await _hotspotTypeRepository.GetAsync(x => x.Id == dto.HotspotId);
-                hotspotHotSpotFullName = hotspot?.HotSpotFullName;
-            }
-
-            var sugar = _knowledgeRepository
-                .Queryable(false, false, false)
-                .Includes(x => x.User)
-                .Includes(x => x.SystemOrganize)
-                .Includes(x => x.HotspotType)
-                .Where(x => x.IsDeleted == false)
-                .Where(x => x.Status == EKnowledgeStatus.OnShelf)
-                .Where(x => x.KnowledgeType.Any(t => t.KnowledgeType.KnowledgeTypeOrgs.Any(to => to.OrgId == _sessionContext.RequiredOrgId) || t.KnowledgeType.KnowledgeTypeOrgs.Any() == false))
-                // .WhereIF(dto.RetrievalType == EKnowledgeRetrievalType.All && !string.IsNullOrEmpty(dto.Keyword), d => d.Title.Contains(dto.Keyword!) || d.Content.Contains(dto.Keyword!))
-                //.WhereIF(dto.RetrievalType == EKnowledgeRetrievalType.Title && !string.IsNullOrEmpty(dto.Keyword), d => d.Title.Contains(dto.Keyword!))
-                //.WhereIF(dto.RetrievalType == EKnowledgeRetrievalType.Content && !string.IsNullOrEmpty(dto.Keyword), d => d.Content.Contains(dto.Keyword!))
-                //.WhereIF(dto.RetrievalType == EKnowledgeRetrievalType.Summary && !string.IsNullOrEmpty(dto.Keyword), d => d.Summary != null && d.Summary.Contains(dto.Keyword!))
-                //.WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => SqlFunc.JsonLike(x.KnowledgeType, typeSpliceName))
-                .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.EndsWith(typeSpliceName)))
-                .WhereIF(!string.IsNullOrEmpty(hotspotHotSpotFullName), x => x.HotspotType.HotSpotFullName.EndsWith(hotspotHotSpotFullName!))
-                .WhereIF(!string.IsNullOrEmpty(dto.HotspotName), x => x.HotspotType.HotSpotFullName.EndsWith(dto.HotspotName!))
-                .WhereIF(!string.IsNullOrEmpty(dto.CreateOrgId), x => x.CreatorOrgId != null && x.CreatorOrgId.EndsWith(dto.CreateOrgId!))
-                .WhereIF(!string.IsNullOrEmpty(dto.Attribution), x => x.Attribution == dto.Attribution!);
-            if (dto.Keyword.NotNullOrEmpty())
-            {
-                var keywords = dto.Keyword!.SplitKeywords();
-                var exp = Expressionable.Create<Knowledge>();
-                foreach (var keyword in keywords)
-                {
-                    if (dto.RetrievalType == EKnowledgeRetrievalType.All)
-                        exp.Or(m => m.Title.Contains(keyword) || m.Content.Contains(keyword));
-                    if (dto.RetrievalType == EKnowledgeRetrievalType.Title)
-                        exp.Or(m => m.Title.Contains(keyword));
-                    if (dto.RetrievalType == EKnowledgeRetrievalType.Content)
-                        exp.Or(m => m.Content.Contains(keyword));
-                    if (dto.RetrievalType == EKnowledgeRetrievalType.Summary)
-                        exp.Or(m => m.Summary != null && m.Summary.Contains(keyword));
-                    if (dto.RetrievalType == EKnowledgeRetrievalType.KeyWord)
-                    {
-                        var keywordEntity = await _knowledgeWordRepository.GetAsync(m => m.Tag == keyword && m.IsEnable == 0);
-                        if (keywordEntity is null) continue;
-                        exp.Or(m => SqlFunc.JsonArrayAny(m.Keywords, keywordEntity.Id));
-                    }
-                }
-                sugar.Where(exp.ToExpression());
-            }
-
-            switch (dto.Sort)
-            {
-                case "2":
-                    sugar = sugar.OrderByDescending(p => p.CollectCount);
-                    break;
-                case "3":
-                    sugar = sugar.OrderByDescending(p => p.CreationTime);
-                    break;
-                default:
-                    sugar = sugar.OrderByDescending(p => p.PageView);
-                    break;
-            }
-            var (total, temp) = await sugar.ToPagedListAsync(dto.PageIndex, dto.PageSize);
-            return new PagedDto<KnowledgeRetrievalDataDto>(total, _mapper.Map<IReadOnlyList<KnowledgeRetrievalDataDto>>(temp));
+            return (await _knowApplication.KnowRetrievalAsync(dto)).ToPaged();
         }
 
         /// <summary>
@@ -707,7 +650,7 @@ namespace Hotline.Api.Controllers
         public async Task<Dictionary<string, dynamic>> GetKnowretrievalBaseData()
         {
             return _baseDataApplication
-                .KnowledgeRetrievalType(new[] { 3, 4})
+                .KnowledgeRetrievalType([3, 4])
                 .Build();
         }
 
@@ -957,7 +900,7 @@ namespace Hotline.Api.Controllers
             dto.DefinitionModuleCode = moduleCode;
             dto.Title = knowledge.Title;
             return await _workflowApplication.StartWorkflowAsync(dto, id, cancellationToken: HttpContext.RequestAborted);
-		}
+        }
         #endregion
 
         #region 知识库词库
@@ -1017,6 +960,7 @@ namespace Hotline.Api.Controllers
             var (total, items) = await _knowledgeWrodRepository.Queryable()
                 .WhereIF(!string.IsNullOrEmpty(dto.Tag), x => x.Tag == dto.Tag!)
                 .WhereIF(!string.IsNullOrEmpty(dto.Classify), x => x.Classify == dto.Classify!)
+                .WhereIF(dto.IsEnable.HasValue, x => x.IsEnable == dto.IsEnable)
                 .WhereIF(!string.IsNullOrEmpty(dto.Synonym), x => x.Synonym != null && x.Synonym.Contains(dto.Synonym!))
                 .OrderByDescending(x => x.CreationTime)
                 .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);

+ 117 - 10
src/Hotline.Application.Contracts/Validators/Order/AddOrderDtoValidator.cs

@@ -1,26 +1,133 @@
 using FluentValidation;
-using Hotline.Orders;
 using Hotline.Share.Dtos.Order;
 
 namespace Hotline.Application.Contracts.Validators.Order;
 
 public class AddOrderDtoValidator : AbstractValidator<AddOrderDto>
 {
+    //新增验证
     public AddOrderDtoValidator()
     {
         RuleFor(d => d.Content).NotEmpty().WithMessage("请填写工单内容");
 
         #region 医疗服务中心受理单
 
-        //RuleFor(d => d.OrderExtension.MedName).Length(0, 200).WithMessage("医院名称最多200字符");
-        //RuleFor(d => d.OrderExtension.MedAddress).Length(0, 500).WithMessage("医院地址最多500字符");
-        //RuleFor(d => d.OrderExtension.MedDepartment).Length(0, 100).WithMessage("科室最多200字符");
-        //RuleFor(d => d.OrderExtension.MedDoctor).Length(0, 50).WithMessage("医生最多200字符");
-        //RuleFor(d => d.OrderExtension.MedPatient).Length(0, 200).WithMessage("病人姓名最多200字符");
-        //RuleFor(d => d.OrderExtension.MedNo).Length(0, 50).WithMessage("就诊卡号最多200字符");
-        //RuleFor(d => d.OrderExtension.MedIdNo).Length(0, 50).WithMessage("身份证号最多200字符");
+        RuleFor(d => d.OrderExtension.MedName).MaximumLength(200).When(d=>d.OrderExtension!=null).WithMessage("医院名称最多200字符");
+        RuleFor(d => d.OrderExtension.MedAddress).MaximumLength(500).When(d=>d.OrderExtension!=null).WithMessage("医院地址最多500字符");
+        RuleFor(d => d.OrderExtension.MedDepartment).MaximumLength(100).When(d=>d.OrderExtension!=null).WithMessage("科室最多200字符");
+        RuleFor(d => d.OrderExtension.MedDoctor).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("医生最多200字符");
+        RuleFor(d => d.OrderExtension.MedPatient).MaximumLength(200).When(d=>d.OrderExtension!=null).WithMessage("病人姓名最多200字符");
+        RuleFor(d => d.OrderExtension.MedNo).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("就诊卡号最多200字符");
+        RuleFor(d => d.OrderExtension.MedIdNo).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("身份证号最多200字符");
         #endregion
 
-        //RuleFor(d => d.OrderExtension.ExchTv).Length(0, 200).WithMessage("医院名称最多200字符");
+        #region 电视购物及商铺购买退换货中心受理单
+        RuleFor(d => d.OrderExtension.ExchTv).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("电视台(商铺)最多50字符");
+        RuleFor(d => d.OrderExtension.ExchProduct).MaximumLength(200).When(d=>d.OrderExtension!=null).WithMessage("产品名称最多200字符");
+        RuleFor(d => d.OrderExtension.ExchName).MaximumLength(200).When(d=>d.OrderExtension!=null).WithMessage("商家名称最多200字符");
+        RuleFor(d => d.OrderExtension.ExchAddress).MaximumLength(500).When(d=>d.OrderExtension!=null).WithMessage("商家地址最多500字符");
+        RuleFor(d => d.OrderExtension.ExchConsignee).MaximumLength(200).When(d=>d.OrderExtension!=null).WithMessage("收货人最多200字符");
+        RuleFor(d => d.OrderExtension.ExchConsigneeAddress).MaximumLength(200).When(d=>d.OrderExtension!=null).WithMessage("收货地址最多200字符");
+        #endregion
+
+        #region 高速公路投诉中心受理单
+        RuleFor(d => d.OrderExtension.ExpwyEntrance).MaximumLength(200).When(d=>d.OrderExtension!=null).WithMessage("高速入口最多200字符");
+        RuleFor(d => d.OrderExtension.ExpwyExit).MaximumLength(200).When(d=>d.OrderExtension!=null).WithMessage("高速出口最多200字符");
+        RuleFor(d => d.OrderExtension.ExpwyNo).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("高速入口最多50字符");
+        #endregion
+
+        #region 电视台虚假广告中心受理单
+        RuleFor(d => d.OrderExtension.AdTv).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("电视台最多50字符");
+        RuleFor(d => d.OrderExtension.AdProduct).MaximumLength(200).When(d=>d.OrderExtension!=null).WithMessage("产品名称最多200字符");
+        #endregion
+
+        #region 四川政务服务网技术中心受理单
+
+        RuleFor(d => d.OrderExtension.ZwfwwAccount).MaximumLength(20).When(d=>d.OrderExtension!=null).WithMessage("服务账号最多20字符");
+        RuleFor(d => d.OrderExtension.ZwfwwContact).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("联系方式最多50字符");
+        #endregion
+
+        #region 四川省12366热线诉求交办单
+        RuleFor(d => d.OrderExtension.Location12366Sq).MaximumLength(500).When(d=>d.OrderExtension!=null).WithMessage("主管税务机关最多500字符");
+        RuleFor(d => d.OrderExtension.Info12366Sq).MaximumLength(200).When(d=>d.OrderExtension!=null).WithMessage("被投诉单位或个人信息最多200字符");
+        RuleFor(d => d.OrderExtension.Type12366Sq).MaximumLength(10).When(d=>d.OrderExtension!=null).WithMessage("投诉类型最多10字符");
+        #endregion
+
+        #region 四川省12366热线咨询交办单
+        RuleFor(d => d.OrderExtension.Location12366Zx).MaximumLength(500).When(d=>d.OrderExtension!=null).WithMessage("主管税务机关最多500字符");
+        RuleFor(d => d.OrderExtension.Info12366Zx).MaximumLength(200).When(d=>d.OrderExtension!=null).WithMessage("被投诉单位或个人信息最多200字符");
+        #endregion
+
+        #region 12328服务监督中心受理单
+        RuleFor(d => d.OrderExtension.AcceptType12328).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("受理方式最多50字符");
+        RuleFor(d => d.OrderExtension.Client12328).MaximumLength(200).When(d=>d.OrderExtension!=null).WithMessage("客户最多200字符");
+        #endregion
+
+        #region 邮政业消费者申诉受理单
+        RuleFor(d => d.OrderExtension.MailClaimantName).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("申诉人姓名最多50字符");
+        RuleFor(d => d.OrderExtension.MailClaimantPhone).MaximumLength(12).When(d=>d.OrderExtension!=null).WithMessage("申诉人电话最多12字符");
+        RuleFor(d => d.OrderExtension.MailClaimantEnterprise).MaximumLength(200).When(d=>d.OrderExtension!=null).WithMessage("申诉企业最多200字符");
+        RuleFor(d => d.OrderExtension.MailSenderName).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("寄件人姓名最多50字符");
+        RuleFor(d => d.OrderExtension.MailSenderPhone).MaximumLength(12).When(d=>d.OrderExtension!=null).WithMessage("寄件人电话最多12字符");
+        RuleFor(d => d.OrderExtension.MailSenderAddress).MaximumLength(500).When(d=>d.OrderExtension!=null).WithMessage("寄件人地址最多500字符");
+        RuleFor(d => d.OrderExtension.MailReceiverName).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("收件人姓名最多50字符");
+        RuleFor(d => d.OrderExtension.MailReceiverPhone).MaximumLength(12).When(d=>d.OrderExtension!=null).WithMessage("收件人电话最多12字符");
+        RuleFor(d => d.OrderExtension.MailReceiverAddress).MaximumLength(500).When(d=>d.OrderExtension!=null).WithMessage("收件人地址最多500字符");
+        #endregion
+
+        #region 环保举报业务受理单
+        RuleFor(d => d.OrderExtension.EpEmail).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("邮箱最多50字符");
+        RuleFor(d => d.OrderExtension.EpAddress).MaximumLength(500).When(d=>d.OrderExtension!=null).WithMessage("通讯地址最多500字符");
+        RuleFor(d => d.OrderExtension.EpObject).MaximumLength(200).When(d=>d.OrderExtension!=null).WithMessage("举报对象最多200字符");
+        RuleFor(d => d.OrderExtension.EpObjectAddress).MaximumLength(200).When(d=>d.OrderExtension!=null).WithMessage("详细地址最多200字符");
+        RuleFor(d => d.OrderExtension.EpIndustryType).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("行业类型最多50字符");
+        RuleFor(d => d.OrderExtension.EpKeyPoint).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("重点要素最多50字符");
+        #endregion
+
+        #region 投诉人信息
+        RuleFor(d => d.OrderExtension.LicenceTypeCode).MaximumLength(10).When(d => d.OrderExtension != null).WithMessage("证件类型最多10字符");
+        RuleFor(d => d.OrderExtension.LicenceType).MaximumLength(50).When(d => d.OrderExtension != null).WithMessage("证件类型名称最多50字符");
+        RuleFor(d => d.OrderExtension.IdentityTypeCode).MaximumLength(4).When(d => d.OrderExtension != null).WithMessage("提供方类型最多4字符");
+        RuleFor(d => d.OrderExtension.IdentityType).MaximumLength(50).When(d => d.OrderExtension != null).WithMessage("提供方类型名称最多50字符");
+        RuleFor(d => d.OrderExtension.IdentityCode).MaximumLength(10).When(d => d.OrderExtension != null).WithMessage("提供方身份最多10字符");
+        RuleFor(d => d.OrderExtension.Identity).MaximumLength(50).When(d => d.OrderExtension != null).WithMessage("提供方身份名称最多50字符");
+        RuleFor(d => d.OrderExtension.NationalityCode).MaximumLength(1).When(d => d.OrderExtension != null).WithMessage("国籍最多1字符");
+        RuleFor(d => d.OrderExtension.Nationality).MaximumLength(50).When(d => d.OrderExtension != null).WithMessage("国籍名称最多50字符");
+        RuleFor(d => d.OrderExtension.NationCode).MaximumLength(4).When(d => d.OrderExtension != null).WithMessage("民族最多4字符");
+        RuleFor(d => d.OrderExtension.Nation).MaximumLength(50).When(d => d.OrderExtension != null).WithMessage("民族名称最多50字符");
+        RuleFor(d => d.OrderExtension.LicenceNo).MaximumLength(64).When(d=>d.OrderExtension!=null).WithMessage("证件号码最多64字符");
+        RuleFor(d => d.OrderExtension.PostalCode).MaximumLength(10).When(d=>d.OrderExtension!=null).WithMessage("邮政编码最多10字符");
+        RuleFor(d => d.OrderExtension.Email).MaximumLength(100).When(d=>d.OrderExtension!=null).WithMessage("电子邮箱最多100字符");
+        RuleFor(d => d.OrderExtension.OtherContact).MaximumLength(64).When(d=>d.OrderExtension!=null).WithMessage("其他联系方式最多64字符");
+        #endregion
+
+        #region 投诉对象信息
+        RuleFor(d => d.OrderExtension.EnterpriseName).MaximumLength(100).When(d=>d.OrderExtension!=null).WithMessage("企业名称最多100字符");
+        RuleFor(d => d.OrderExtension.UnifiedSocialCreditCode).MaximumLength(30).When(d=>d.OrderExtension!=null).WithMessage("统一社会信用代码最多30字符");
+        RuleFor(d => d.OrderExtension.RegisterAddress).MaximumLength(500).When(d=>d.OrderExtension!=null).WithMessage("企业注册地址最多500字符");
+        RuleFor(d => d.OrderExtension.RegisterNumber).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("注册号最多50字符");
+        RuleFor(d => d.OrderExtension.EnterpriseContact).MaximumLength(70).When(d=>d.OrderExtension!=null).WithMessage("联系人最多70字符");
+        RuleFor(d => d.OrderExtension.MarketTypeCode).MaximumLength(64).When(d => d.OrderExtension != null).WithMessage("市场主体类型代码最多64字符");
+        RuleFor(d => d.OrderExtension.MarketType).MaximumLength(200).When(d => d.OrderExtension != null).WithMessage("市场主体类型名称最多200字符");
+        RuleFor(d => d.OrderExtension.IndustryClassifyCode).MaximumLength(64).When(d => d.OrderExtension != null).WithMessage("行业类型代码最多64字符");
+        RuleFor(d => d.OrderExtension.IndustryClassify).MaximumLength(200).When(d => d.OrderExtension != null).WithMessage("行业类型名称最多10字符");
+        #endregion
+
+        #region 投诉详情
+        RuleFor(d => d.OrderExtension.ExternalOrderNo).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("订单号最多50字符");
+        RuleFor(d => d.OrderExtension.Patentee).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("专利权人最多50字符");
+        RuleFor(d => d.OrderExtension.PatentName).MaximumLength(200).When(d=>d.OrderExtension!=null).WithMessage("专利名称最多200字符");
+        RuleFor(d => d.OrderExtension.PatentNo).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("专利号最多50字符");
+        RuleFor(d => d.OrderExtension.ProductName).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("产品名称最多50字符");
+        RuleFor(d => d.OrderExtension.ApprovalNumber).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("批准文号最多50字符");
+        RuleFor(d => d.OrderExtension.ProductBatchNo).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("产品批号最多50字符");
+        RuleFor(d => d.OrderExtension.ProductStandard).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("产品规格最多50字符");
+        RuleFor(d => d.OrderExtension.Manufacturer).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("生产厂家最多50字符");
+        RuleFor(d => d.OrderExtension.SalesEnterprise).MaximumLength(50).When(d=>d.OrderExtension!=null).WithMessage("销售企业最多50字符");
+        RuleFor(d => d.OrderExtension.ConsumerAddress).MaximumLength(100).When(d=>d.OrderExtension!=null).WithMessage("消费者地址最多100字符");
+        RuleFor(d => d.OrderExtension.BusinessPosition.Street).MaximumLength(100).When(d=>d.OrderExtension!=null).WithMessage("经营详细地址最多100字符"); 
+        #endregion
     }
-}
+
+}
+

+ 16 - 0
src/Hotline.Application.Contracts/Validators/Order/UpdateOrderDtoValidator.cs

@@ -0,0 +1,16 @@
+using FluentValidation;
+using Hotline.Share.Dtos.Order;
+
+namespace Hotline.Application.Contracts.Validators.Order
+{
+    /// <summary>
+    /// 编辑验证
+    /// </summary>
+    public class UpdateOrderDtoValidator : AbstractValidator<UpdateOrderDto>
+    {
+        public UpdateOrderDtoValidator()
+        {
+            Include(new AddOrderDtoValidator());
+        }
+    }
+}

+ 8 - 27
src/Hotline.Application/FlowEngine/WorkflowApplication.cs

@@ -593,7 +593,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         {
             var success = int.TryParse(stepDefine.HandlerTypeItems.First().Key, out var level);
             int? orgLevel = success ? level : null;
-            var nextStepOption =   new NextStepOption
+            var nextStepOption = new NextStepOption
             {
                 Key = stepDefine.Code,
                 Value = stepDefine.Name,
@@ -608,12 +608,12 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 dto.OrgIds.Contains(d.ParentId))
                 .ToListAsync(cancellationToken);
             nextStepOption.Items = orgs.Select(d => new FlowStepHandler
-                {
-                    Key = d.Id,
-                    Value = d.Name,
-                    OrgId = d.Id,
-                    OrgName = d.Name
-                })
+            {
+                Key = d.Id,
+                Value = d.Name,
+                OrgId = d.Id,
+                OrgName = d.Name
+            })
                 .ToList();
             nextStepOption.FlowDirection = _workflowDomainService.GetFlowDirection(dto.BusinessType, stepDefine.BusinessType);
             stepOptions.Add(nextStepOption);
@@ -891,26 +891,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             //汇总节点只能选择对应节点办理对象
             if (workflow.FlowType is EFlowType.Handle && stepDefine.StepType is EStepType.Summary)
             {
-                //根据汇总对象id找到被汇总节点
-                var summaryTargetStep = workflow.Steps.FirstOrDefault(d =>
-                    d.StepType == EStepType.Normal &&
-                    d.Code == stepDefine.SummaryTargetCode &&
-                    d.Status == EWorkflowStepStatus.Handled &&
-                    d.IsOrigin);
-                if (summaryTargetStep is null)
-                    throw UserFriendlyException.SameMessage("未查询到汇总对象节点");
-
-                var handler = new FlowStepHandler
-                {
-                    Key = summaryTargetStep.HandlerId,
-                    Value = summaryTargetStep.HandlerName,
-                    UserId = summaryTargetStep.HandlerId,
-                    Username = summaryTargetStep.HandlerName,
-                    OrgId = summaryTargetStep.HandlerOrgId,
-                    OrgName = summaryTargetStep.HandlerOrgName,
-                    RoleId = summaryTargetStep.RoleId,
-                    RoleName = summaryTargetStep.RoleName
-                };
+                var handler = _workflowDomainService.GetSummaryTargetFlowStepHandler(workflow, stepDefine.SummaryTargetCode);
 
                 nextStepOption = new NextStepOption
                 {

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

@@ -9,7 +9,6 @@
   <ItemGroup>
     <PackageReference Include="HtmlToOpenXml.dll" Version="3.2.0" />
     <PackageReference Include="JiebaAspNetCore.Segmenter" Version="1.0.1" />
-    <PackageReference Include="JV.PanGu.Core" Version="1.0.1" />
     <PackageReference Include="NPOI" Version="2.7.0" />
     <PackageReference Include="OpenHtmlToPdf" Version="1.12.0" />
     <PackageReference Include="XC.RSAUtil" Version="1.3.6" />

+ 7 - 0
src/Hotline.Application/Knowledge/IKnowApplication.cs

@@ -70,5 +70,12 @@ namespace Hotline.Application.Knowledge
         /// <returns></returns>
         Task UpdateKnowledgeHotWordAsync(UpdateKnowledgeHotWordInDto dto, CancellationToken requestAborted = default);
         Task AddKnowledgeHotWordAsync(AddKnowledgeHotWordInDto dto, CancellationToken requestAborted = default);
+
+        /// <summary>
+        /// 知识检索
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        Task<(int, List<KnowledgeRetrievalDataDto>)> KnowRetrievalAsync(KnowledgeRetrievalPagedListDto dto);
     }
 }

+ 80 - 0
src/Hotline.Application/Knowledge/KnowApplication.cs

@@ -257,5 +257,85 @@ namespace Hotline.Application.Knowledge
             var entity = dto.Adapt<KnowledgeHotWord>();
             await _knowledgeHotWordRepository.AddAsync(entity, requestAborted);
         }
+
+        public async Task<(int, List<KnowledgeRetrievalDataDto>)> KnowRetrievalAsync(KnowledgeRetrievalPagedListDto dto)
+        {
+            var typeSpliceName = string.Empty;
+            var hotspotHotSpotFullName = string.Empty;
+            if (!string.IsNullOrEmpty(dto.KnowledgeTypeId))
+            {
+                var type = await _knowledgeTypeRepository.GetAsync(x => x.Id == dto.KnowledgeTypeId);
+                typeSpliceName = type?.SpliceName;
+            }
+            if (!string.IsNullOrEmpty(dto.HotspotId))
+            {
+                var hotspot = await _hotspotTypeRepository.GetAsync(x => x.Id == dto.HotspotId);
+                hotspotHotSpotFullName = hotspot?.HotSpotFullName;
+            }
+
+            var sugar = _knowledgeRepository
+                .Queryable(false, false, false)
+                .Includes(x => x.User)
+                .Includes(x => x.SystemOrganize)
+                .Includes(x => x.HotspotType)
+                .Where(x => x.IsDeleted == false)
+                .Where(x => x.Status == EKnowledgeStatus.OnShelf)
+                .Where(x => x.KnowledgeType.Any(t => t.KnowledgeType.KnowledgeTypeOrgs.Any(to => to.OrgId == _sessionContext.RequiredOrgId) || t.KnowledgeType.KnowledgeTypeOrgs.Any() == false))
+                .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.EndsWith(typeSpliceName)))
+                .WhereIF(!string.IsNullOrEmpty(hotspotHotSpotFullName), x => x.HotspotType.HotSpotFullName.EndsWith(hotspotHotSpotFullName!))
+                .WhereIF(!string.IsNullOrEmpty(dto.HotspotName), x => x.HotspotType.HotSpotFullName.EndsWith(dto.HotspotName!))
+                .WhereIF(!string.IsNullOrEmpty(dto.CreateOrgId), x => x.CreatorOrgId != null && x.CreatorOrgId.EndsWith(dto.CreateOrgId!))
+                .WhereIF(!string.IsNullOrEmpty(dto.Attribution), x => x.Attribution == dto.Attribution!);
+            if (dto.Keyword.NotNullOrEmpty())
+            {
+                var keywords = dto.Keyword!.SplitKeywords();
+                var exp = Expressionable.Create<KnowledgeBase.Knowledge>();
+                foreach (var keyword in keywords)
+                {
+                    if (dto.RetrievalType == EKnowledgeRetrievalType.All)
+                        exp.Or(x => x.Title.Contains(keyword) || x.Content.Contains(keyword));
+                    if (dto.RetrievalType == EKnowledgeRetrievalType.Title)
+                        exp.Or(x => x.Title.Contains(keyword));
+                    if (dto.RetrievalType == EKnowledgeRetrievalType.Content)
+                        exp.Or(x => x.Content.Contains(keyword));
+                    if (dto.RetrievalType == EKnowledgeRetrievalType.Summary)
+                        exp.Or(x => x.Summary != null && x.Summary.Contains(keyword));
+                    if (dto.RetrievalType == EKnowledgeRetrievalType.KeyWord)
+                    {
+                        var keywordEntity = await _knowledgeWordRepository.GetAsync(m => m.Tag == keyword && m.IsEnable == 0);
+                        if (keywordEntity is null) continue;
+                        exp.Or(x => SqlFunc.JsonArrayAny(x.Keywords, keywordEntity.Id));
+                    }
+                }
+                sugar = sugar.Where(exp.ToExpression());
+            }
+            if (dto.Content.NotNullOrEmpty())
+            {
+                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))
+                    );
+                sugar = sugar.Where(exp.ToExpression());
+            }
+
+            switch (dto.Sort)
+            {
+                case "2":
+                    sugar = sugar.OrderByDescending(p => p.CollectCount);
+                    break;
+                case "3":
+                    sugar = sugar.OrderByDescending(p => p.CreationTime);
+                    break;
+                default:
+                    sugar = sugar.OrderByDescending(p => p.PageView);
+                    break;
+            }
+            return await sugar.Select<KnowledgeRetrievalDataDto>().ToPagedListAsync(dto.PageIndex, dto.PageSize);
+        }
     }
 }

+ 11 - 10
src/Hotline.Share/Dtos/Knowledge/KnowledgePagedDto.cs

@@ -21,17 +21,18 @@ namespace Hotline.Share.Dtos.Knowledge
 	/// <param name="EKnowledgeWorkFlowStatus">审核状态</param>
 	public record KnowledgeApprovalPagedListDto(EKnowledgeApplyType? EKnowledgeApplyType, EKnowledgeWorkFlowStatus? EKnowledgeWorkFlowStatus) : PagedKeywordRequest;
 
-	/// <summary>
-	/// 知识检索
-	/// </summary>
-	/// <param name="RetrievalType">检索类型</param>
-	/// <param name="Attribution">归属</param>
-	/// <param name="Sort">排序字段</param>
-	/// <param name="CreateOrgId">部门id</param>
-	/// <param name="HotspotId">热点id</param>
+    /// <summary>
+    /// 知识检索
+    /// </summary>
+    /// <param name="RetrievalType">检索类型</param>
+    /// <param name="Attribution">归属</param>
+    /// <param name="Sort">排序字段</param>
+    /// <param name="CreateOrgId">部门id</param>
+    /// <param name="HotspotId">热点id</param>
     /// <param name="HotspotName">热点名称</param>;
-	/// <param name="KnowledgeTypeId">类型id</param>
-	public record KnowledgeRetrievalPagedListDto(EKnowledgeRetrievalType? RetrievalType,string? Attribution,string Sort, string? CreateOrgId, string? HotspotId, string? HotspotName, string? KnowledgeTypeId) : PagedKeywordRequest;
+    /// <param name="KnowledgeTypeId">类型id</param>
+    /// <param name="Content">受理内容</param>
+    public record KnowledgeRetrievalPagedListDto(EKnowledgeRetrievalType? RetrievalType,string? Attribution,string Sort, string? CreateOrgId, string? HotspotId, string? HotspotName, string? KnowledgeTypeId, string? Content) : PagedKeywordRequest;
 
     /// <summary>
     /// 来电弹屏知识库查询

+ 5 - 0
src/Hotline.Share/Dtos/Knowledge/KnowledgeWordDto.cs

@@ -94,6 +94,11 @@ namespace Hotline.Share.Dtos.Knowledge
 		/// 近义词
 		/// </summary>
 		public string? Synonym { get; set; }
+
+		/// <summary>
+		/// 启禁用  0  启用  1 禁用
+		/// </summary>
+		public int? IsEnable { get; set; }
 	}
 	public class KnowledgeWordBaseDto
 	{

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

@@ -12,6 +12,7 @@
 
   <ItemGroup>
     <PackageReference Include="DocXCore" Version="1.0.10" />
+    <PackageReference Include="JV.PanGu.Core" Version="1.0.1" />
     <PackageReference Include="Mapster" Version="7.4.0" />
     <PackageReference Include="MediatR.Contracts" Version="2.0.1" />
     <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />

+ 17 - 2
src/Hotline.Share/Tools/StringExtensions.cs

@@ -1,4 +1,5 @@
-using System.Text.RegularExpressions;
+using PanGu;
+using System.Text.RegularExpressions;
 
 namespace Hotline.Share.Tools;
 public static class StringExtensions
@@ -44,7 +45,21 @@ public static class StringExtensions
         return Regex.IsMatch(value, phoneNumberPattern);
     }
 
-    public static IList<string> SplitKeywords(this string value)
+    /// <summary>
+    /// 通过 PanGu 进行分词
+    /// </summary>
+    /// <param name="value"></param>
+    /// <returns></returns>
+    public static IList<string> GetSegment(this string value)
+    {
+        return new Segment()
+            .DoSegment(value)
+            .Where(word => word.WordType == WordType.SimplifiedChinese && word.Word.Length > 1)
+            .Select(word => word.Word)
+            .ToList();
+    }
+
+        public static IList<string> SplitKeywords(this string value)
     {
         var regex = new Regex(@"[ ,,]+");
 

+ 1 - 1
src/Hotline/File/File.cs

@@ -91,7 +91,7 @@ namespace Hotline.File
 		/// <summary>
 		/// 完整附件路径
 		/// </summary>
-		[SugarColumn(ColumnDescription = "完整附件路径")]
+		[SugarColumn(ColumnDescription = "完整附件路径", ColumnDataType = "varchar(500)")]
 		public string? AllPath { get; set; }
 	}
 }

+ 5 - 0
src/Hotline/FlowEngine/Workflows/IWorkflowDomainService.cs

@@ -292,5 +292,10 @@ namespace Hotline.FlowEngine.Workflows
         /// </summary>
         /// <returns></returns>
         Task<ICollection<WorkflowStep>> GetUnhandleStepsByOthersAsync(string workflowId, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 根据汇总对象id找到被汇总节点,生成指派到用户的办理对象
+        /// </summary>
+        FlowStepHandler GetSummaryTargetFlowStepHandler(Workflow workflow, string summaryTargetStepCode);
     }
 }

+ 38 - 0
src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs

@@ -284,6 +284,13 @@ namespace Hotline.FlowEngine.Workflows
                 nextStepDefine = GetStepDefine(workflow.WorkflowDefinition, dto.NextStepCode);
             }
 
+            //普通节点往汇总节点办理时可以不选,不选的场景主动去查之前的办理对象
+            if (nextStepDefine.StepType is EStepType.Summary && !dto.NextHandlers.Any())
+            {
+                var handler = GetSummaryTargetFlowStepHandler(workflow, nextStepDefine.SummaryTargetCode);
+                dto.NextHandlers.Add(handler);
+            }
+
             //需求:按角色选择办理人可以不选,表示该角色下所有人都可以办理,同时依据配置:是否本部门人办理显示待选办理人。角色下只要一人办理即可(即:角色下不发起会签)
             if (nextStepDefine.HandlerType != EHandlerType.Role && !dto.NextHandlers.Any())
                 throw new UserFriendlyException("未指定节点处理者");
@@ -1366,6 +1373,37 @@ namespace Hotline.FlowEngine.Workflows
                 .ToListAsync(cancellationToken);
         }
 
+        /// <summary>
+        /// 根据汇总对象id找到被汇总节点,生成指派到用户的办理对象
+        /// </summary>
+        /// <param name="workflow"></param>
+        /// <param name="stepDefine"></param>
+        /// <returns></returns>
+        public FlowStepHandler GetSummaryTargetFlowStepHandler(Workflow workflow, string summaryTargetStepCode)
+        {
+            //根据汇总对象id找到被汇总节点
+            var summaryTargetStep = workflow.Steps.FirstOrDefault(d =>
+                d.StepType == EStepType.Normal &&
+                d.Code == summaryTargetStepCode &&
+                d.Status == EWorkflowStepStatus.Handled &&
+                d.IsOrigin);
+            if (summaryTargetStep is null)
+                throw UserFriendlyException.SameMessage("未查询到汇总对象节点");
+
+            var handler = new FlowStepHandler
+            {
+                Key = summaryTargetStep.HandlerId,
+                Value = summaryTargetStep.HandlerName,
+                UserId = summaryTargetStep.HandlerId,
+                Username = summaryTargetStep.HandlerName,
+                OrgId = summaryTargetStep.HandlerOrgId,
+                OrgName = summaryTargetStep.HandlerOrgName,
+                RoleId = summaryTargetStep.RoleId,
+                RoleName = summaryTargetStep.RoleName
+            };
+            return handler;
+        }
+
         /// <summary>
         /// 查找当前会签内所有节点(含start,end)
         /// </summary>