소스 검색

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

Dun.Jason 2 달 전
부모
커밋
0b2cb921d3

+ 150 - 70
src/Hotline.Api/Controllers/FwThirdController.cs

@@ -21,15 +21,24 @@ using Hotline.Share.Dtos.WebPortal;
 using Hotline.Share.Enums.KnowledgeBase;
 using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Push;
+using Hotline.Share.Tools;
+using Hotline.Tools;
 using Hotline.WebPortal;
+using Hotline.YbEnterprise.Sdk;
 using MapsterMapper;
 using MathNet.Numerics;
 using MediatR;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Options;
+using MongoDB.Driver.Core.WireProtocol.Messages;
+using Newtonsoft.Json.Linq;
 using Senparc.Weixin.WxOpen.AdvancedAPIs.WxApp.WxAppJson;
+using Senparc.Weixin.WxOpen.Entities;
+using SharpCompress.Common;
 using SqlSugar;
+using System.Text.Json;
+using System.Threading;
 using XF.Domain.Authentications;
 using XF.Domain.Cache;
 using XF.Domain.Filters;
@@ -66,6 +75,7 @@ namespace Hotline.Api.Controllers
         private readonly IRepository<KnowledgeWord> _knowledgeWordRepository;
         private readonly ISystemDicDataCacheManager _sysDicDataCacheManager;
         private readonly IIdentityAppService _identityAppService;
+        private readonly IHttpClientFactory _httpClientFactory;
 
         public FwThirdController(IMapper mapper,
             IMediator mediator,
@@ -91,7 +101,8 @@ namespace Hotline.Api.Controllers
             ITypedCache<string> getVailData,
             IRepository<KnowledgeWord> knowledgeWordRepository,
             ISystemDicDataCacheManager sysDicDataCacheManager,
-            IIdentityAppService identityAppService
+            IIdentityAppService identityAppService,
+            IHttpClientFactory httpClientFactory
             )
         {
             _mapper = mapper;
@@ -119,6 +130,7 @@ namespace Hotline.Api.Controllers
             _knowledgeWordRepository = knowledgeWordRepository;
             _sysDicDataCacheManager = sysDicDataCacheManager;
             _identityAppService = identityAppService;
+            _httpClientFactory = httpClientFactory;
         }
 
         #endregion
@@ -142,7 +154,6 @@ namespace Hotline.Api.Controllers
         /// <param name="dto"></param>
         /// <returns></returns>
         [HttpPost("getacceptancetypestatisticsbymonth")]
-        [AllowAnonymous]
         public async Task<List<OrderFormCount>> GetAcceptanceTypeStatisticsByMonth([FromBody] OrderFormByMonthDto dto)
         {
             if (string.IsNullOrEmpty(dto.StartTime))
@@ -199,7 +210,6 @@ namespace Hotline.Api.Controllers
         /// <param name="dto"></param>
         /// <returns></returns>
         [HttpPost("getacceptancetypestatisticsbymonthend")]
-        [AllowAnonymous]
         public async Task<List<OrderFormCount>> GetAcceptanceTypeStatisticsByMonthEnd([FromBody] OrderFormByMonthDto dto)
         {
             if (string.IsNullOrEmpty(dto.StartTime))
@@ -253,7 +263,6 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <param name="dto"></param>
         /// <returns></returns>
-        [AllowAnonymous]
         [HttpPost("getarticlelistbynum")]
         public async Task<IReadOnlyList<DataListTopDto>> GetArticleListByNum([FromBody] ArticleIdByNumDto dto)
         {
@@ -288,7 +297,6 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <param name="dto"></param>
         /// <returns></returns>
-        [AllowAnonymous]
         [HttpPost("getarticledetails")]
         public async Task<List<ArticleDetailsDto>> GetArticleDetails([FromBody] ArticleIdDto dto)
         {
@@ -330,7 +338,6 @@ namespace Hotline.Api.Controllers
         /// <param name="dto"></param>
         /// <returns></returns>
         [HttpPost("get_order_list_publish_all")]
-        [AllowAnonymous]
         public async Task<OrderListReturnDto> GetOrderByListAllOpen([FromBody] QueryOrderListDto dto)
         {
             var queryNew = _orderPublishRepository.Queryable()
@@ -405,7 +412,6 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <param name="dto"></param>
         /// <returns></returns>
-        [AllowAnonymous]
         [HttpPost("getorderdetailbyid")]
         public async Task<OrderDetail> GetOrderDetailById([FromBody] ArticleIdDto dto)
         {
@@ -456,12 +462,13 @@ namespace Hotline.Api.Controllers
 
         #region 知识库
 
+        #region 知识库分类
+
         /// <summary>
-        /// 获取知识库分类
+        /// 知识库分类
         /// </summary>
         /// <returns></returns>
         [HttpPost("getknowledgetype")]
-        [AllowAnonymous]
         public async Task<object> GetKnowledgeType()
         {
             //查询知识分类
@@ -489,65 +496,73 @@ namespace Hotline.Api.Controllers
             return rsp;
         }
 
-        ///// <summary>
-        ///// 知识库查询
-        ///// </summary>
-        ///// <param name="dto"></param>
-        ///// <returns></returns>
-        //[HttpPost("getknowledgelist")]
-        //[AllowAnonymous]
-        //public async Task<PagedDto<KnowledgeInfoDto>> GetKnowledgeList([FromBody] QueryKnowledgeList dto)
-        //{
-        //    var typeSpliceName = string.Empty;
-        //    if (!string.IsNullOrEmpty(dto.KnowledgeTypeId))
-        //    {
-        //        var type = await _knowledgeTypeRepository.GetAsync(x => x.Name == dto.KnowledgeTypeId);
-        //        typeSpliceName = type?.SpliceName;
-        //    }
-
-        //    var typeSpliceNameTags = string.Empty;
-        //    if (!string.IsNullOrEmpty(dto.KnowledgeBaseTags))
-        //    {
-        //        var type = await _knowledgeWordRepository.GetAsync(x => x.Tag == dto.KnowledgeBaseTags && x.IsEnable == 1);
-        //        typeSpliceNameTags = type?.Id;
-        //    }
-
-        //    var (total, items) = await _knowledgeRepository.Queryable()
-        //        .Where(p => p.IsPublic == true && p.Status == EKnowledgeStatus.OnShelf)
-        //        .WhereIF(!string.IsNullOrEmpty(dto.Title), p => p.Title.Contains(dto.Title))
-        //        .WhereIF(!string.IsNullOrEmpty(typeSpliceNameTags), p => SqlFunc.JsonArrayAny(p.Keywords, typeSpliceNameTags) == true)
-        //        .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.EndsWith(typeSpliceName)))
-        //        .OrderByDescending(p => p.CreationTime)
-        //        .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
-
-        //    var data = new PagedDto<KnowledgeInfoDto>(total, _mapper.Map<IReadOnlyList<KnowledgeInfoDto>>(items));
-        //    return data;
-        //}
-
-        ///// <summary>
-        ///// 获取知识库详情
-        ///// </summary>
-        ///// <param name="dto"></param>
-        ///// <returns></returns>
-        //[HttpPost("getknowledgeinfo")]
-        //[AllowAnonymous]
-        //public async Task<KnowledgeInfoDto> GetKnowledgeInfo([FromBody] QueryKnowledgeInfo dto)
-        //{
-        //    var data = await _knowledgeRepository.GetAsync(p => p.Id == dto.Id, HttpContext.RequestAborted);
-        //    KnowledgeInfoDto detailsDto = null;
-        //    if (data != null)
-        //    {
-        //        detailsDto = _mapper.Map<KnowledgeInfoDto>(data);
-
-        //        if (detailsDto != null && !string.IsNullOrEmpty(detailsDto.Content))
-        //            data.Content = _bulletinApplication.GetSiteUrls(data.Content);
-        //    }
-        //    else
-        //    {
-        //        detailsDto = new();
-        //    }
-        //    return detailsDto;
-        //}
+        #endregion
+
+        #region 知识库查询
+
+        /// <summary>
+        /// 知识库查询
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("getknowledgelist")]
+        public async Task<PagedDto<KnowledgeInfoThirdDto>> GetKnowledgeList([FromBody] QueryKnowledgeList dto)
+        {
+            var typeSpliceName = string.Empty;
+            if (!string.IsNullOrEmpty(dto.KnowledgeTypeId))
+            {
+                var type = await _knowledgeTypeRepository.GetAsync(x => x.Name == dto.KnowledgeTypeId);
+                typeSpliceName = type?.SpliceName;
+            }
+
+            var typeSpliceNameTags = string.Empty;
+            if (!string.IsNullOrEmpty(dto.KnowledgeBaseTags))
+            {
+                var type = await _knowledgeWordRepository.GetAsync(x => x.Tag == dto.KnowledgeBaseTags && x.IsEnable == 1);
+                typeSpliceNameTags = type?.Id;
+            }
+
+            var (total, items) = await _knowledgeRepository.Queryable()
+                .Where(p => p.IsPublic == true && p.Status == EKnowledgeStatus.OnShelf)
+                .WhereIF(!string.IsNullOrEmpty(dto.Title), p => p.Title.Contains(dto.Title))
+                .WhereIF(!string.IsNullOrEmpty(typeSpliceNameTags), p => SqlFunc.JsonArrayAny(p.Keywords, typeSpliceNameTags) == true)
+                .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.EndsWith(typeSpliceName)))
+                .OrderByDescending(p => p.CreationTime)
+                .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+
+            var data = new PagedDto<KnowledgeInfoThirdDto>(total, _mapper.Map<IReadOnlyList<KnowledgeInfoThirdDto>>(items));
+            return data;
+        }
+
+        #endregion
+
+        #region 知识库详情
+
+        /// <summary>
+        /// 知识库详情
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("getknowledgeinfo")]
+        public async Task<KnowledgeInfoThirdDto> GetKnowledgeInfo([FromBody] QueryKnowledgeInfo dto)
+        {
+            var data = await _knowledgeRepository.GetAsync(p => p.Id == dto.Id, HttpContext.RequestAborted);
+            KnowledgeInfoThirdDto detailsDto = null;
+            if (data != null)
+            {
+                detailsDto = _mapper.Map<KnowledgeInfoThirdDto>(data);
+
+                if (detailsDto != null && !string.IsNullOrEmpty(detailsDto.Content))
+                    data.Content = _bulletinApplication.GetSiteUrls(data.Content);
+            }
+            else
+            {
+                detailsDto = new();
+            }
+            return detailsDto;
+        }
+
+        #endregion
 
         #endregion
 
@@ -558,7 +573,6 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <param name="dto"></param>
         /// <returns></returns>
-        [AllowAnonymous]
         [HttpPost("orderacceptance")]
         public async Task<OpenResponse> OrderAcceptance([FromBody] WebFlowAcceptDto dto)
         {
@@ -566,6 +580,12 @@ namespace Hotline.Api.Controllers
             if (!string.IsNullOrEmpty(dto.Mobile))
                 dto.Mobile = dto.Mobile.Trim();
 
+            string strResult = dto.ValidateObject();
+            if (!string.IsNullOrEmpty(strResult))
+            {
+                return OpenResponse.Ok(WebPortalDeResponse<OrderAcceptanceReturnDto>.Failed("数据验证不通过!"));
+            }
+
             var data = _mapper.Map<Hotline.Share.Dtos.Order.AddOrderDto>(dto);
             data.Source = ESource.WebPortal;
             data.SourceChannel = "因特网";
@@ -630,7 +650,50 @@ namespace Hotline.Api.Controllers
 
         #region 附件上传
 
+        /// <summary>
+        /// 附件上传
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("uploadfiles")]
+        public async Task<GetResultDto> uploadfiles(IFormFile fileData)
+        {
+            if (fileData == null || fileData.Length == 0)
+            {
+                return new GetResultDto();
+            }
+
+            Stream stream = fileData.OpenReadStream();
+            byte[] fileContentBytes = StreamToBytes(stream);
+            var client = _httpClientFactory.CreateClient("fileData");
+
+            using (var content = new MultipartFormDataContent())
+            {
+                var fileContent = new ByteArrayContent(fileContentBytes);
+                fileContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("form-data")
+                {
+                    Name = fileData.Name,
+                    FileName = fileData.FileName
+                };
+                content.Add(fileContent, "fileData", fileData.FileName);
 
+                var response = await client.PostAsync($"{_appOptions.Value.FileUpload.Url.TrimEnd('/')}/file/upload?source=HotlineWeb", content);
+                if (response.IsSuccessStatusCode)
+                {
+                    var result = await response.Content.ReadFromJsonAsync<GetFileDto>();
+                    return new GetResultDto()
+                    {
+                        id = result.result.id,
+                        fileName = result.result.fileName,
+                        path = result.result.path,
+                    };
+                }
+                else
+                {
+                    return new GetResultDto();
+                }
+            }
+        }
 
         #endregion
 
@@ -641,7 +704,6 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <param name="dto"></param>
         /// <returns></returns>
-        [AllowAnonymous]
         [HttpPost("getorderdetailbyno")]
         public async Task<OrderDetail> GetOrderDetailByNo([FromBody] GetOrderCodePwd dto)
         {
@@ -692,6 +754,24 @@ namespace Hotline.Api.Controllers
         }
 
 
+        #endregion
+
+        #region 私有方法
+
+        /// <summary>
+        /// 将文件转化为文件流
+        /// </summary>
+        /// <param name="stream"></param>
+        /// <returns></returns>
+        private byte[] StreamToBytes(Stream stream)
+        {
+            byte[] bytes = new byte[stream.Length];
+            stream.Read(bytes, 0, bytes.Length);
+            // 设置当前流的位置为流的开始    
+            stream.Seek(0, SeekOrigin.Begin);
+            return bytes;
+        }
+
         #endregion
     }
 }

+ 158 - 1
src/Hotline.Api/Controllers/OrderController.cs

@@ -77,6 +77,10 @@ using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
 using OrderDto = Hotline.Share.Dtos.Order.OrderDto;
 using System.Threading;
+using Hotline.Realtimes;
+using Hotline.Article;
+using Hotline.Share.Dtos.Article;
+using Hotline.Api.Realtimes;
 
 namespace Hotline.Api.Controllers;
 
@@ -157,6 +161,9 @@ public class OrderController : BaseController
     private readonly IIndustryRepository _industryRepository;
     private readonly IRepository<SystemDicData> _sysDicDataRepository;
     private readonly IRepository<SystemOrganize> _systemOrganizeRepository;
+    private readonly IRepository<OrderComplement> _orderComplementRepository;
+    private readonly IRealtimeService _realtimeService;
+    private readonly IRepository<NotificationWaitSend> _notificationWaitSendRepository;
 
     public OrderController(
         IOrderDomainService orderDomainService,
@@ -229,7 +236,10 @@ public class OrderController : BaseController
         IIndustryRepository industryRepository,
         IOrderSnapshotApplication orderSnapshotApplication,
         IRepository<SystemDicData> sysDicDataRepository,
-        IRepository<SystemOrganize> systemOrganizeRepository)
+        IRepository<SystemOrganize> systemOrganizeRepository,
+        IRepository<OrderComplement> orderComplementRepository,
+        IRealtimeService realtimeService,
+        IRepository<NotificationWaitSend> notificationWaitSendRepository)
     {
         _orderDomainService = orderDomainService;
         _orderRepository = orderRepository;
@@ -302,6 +312,9 @@ public class OrderController : BaseController
         _orderSnapshotApplication = orderSnapshotApplication;
         _sysDicDataRepository = sysDicDataRepository;
         _systemOrganizeRepository = systemOrganizeRepository;
+        _orderComplementRepository = orderComplementRepository;
+        _realtimeService = realtimeService;
+        _notificationWaitSendRepository = notificationWaitSendRepository;
     }
 
     #endregion
@@ -3890,6 +3903,53 @@ public class OrderController : BaseController
                 });
         }
 
+        //处理回访录音
+        if (dto.OrderVisits != null && dto.OrderVisits.Count > 0)
+        {
+            foreach (var item in dto.OrderVisits)
+            {
+                //var recordingFileUrl = "";
+                //var recordingBaseAddress = "";
+                //var recordingAbsolutePath = "";
+                //var aiVisitVoiceBaseUrl = "";
+                if (_appOptions.Value.GetDefaultAppScopeConfiguration().CallCenterType == AppDefaults.CallCenterType.TianRun)
+                {
+                    var callRecord = await _callApplication.GetTianrunCallAsync(item.CallId, HttpContext.RequestAborted);
+                    if (callRecord != null)
+                    {
+                        item.RecordingFileUrl = callRecord.RecordingFileUrl;
+                        item.RecordingBaseAddress = callRecord.RecordingBaseAddress;
+                        item.RecordingAbsolutePath = callRecord.RecordingAbsolutePath;
+                    }
+                }
+                else if (_appOptions.Value.GetDefaultAppScopeConfiguration().CallCenterType == AppDefaults.CallCenterType.XingTang)
+                {
+                    CallNative? call = null;
+                    if (item.CallId == null && item.VisitTime.HasValue)
+                    {
+                        call = await _callApplication.GetCallByTimeAndToNoAsync(dto.Contact, item.VisitTime.Value,
+                            HttpContext.RequestAborted);
+                    }
+
+                    call = await _callApplication.GetCallAsync(item.CallId, HttpContext.RequestAborted);
+                    if (call is not null)
+                    {
+                        if (call.AudioFile.IsNullOrEmpty())
+                        {
+                            call = await _callApplication.GetCallByCallNoAsync(call.CallNo, HttpContext.RequestAborted);
+                        }
+
+                        if (call is not null)
+                        {
+                            item.RecordingFileUrl = call.AudioFile;
+                            item.RecordingBaseAddress = call.AudioFile;
+                            item.RecordingAbsolutePath = call.AudioFile;
+                        }
+                    }
+                }
+            }
+        }
+
         return _sessionContext.OrgIsCenter ? dto : dto.DataMask();
     }
 
@@ -8898,4 +8958,101 @@ public class OrderController : BaseController
     }
 
     #endregion
+
+    #region 添加补充
+    /// <summary>
+    /// 添加补充 _notificationWaitSendRepository
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpPost("add_order_complement")]
+    public async Task AddOrderComplement([FromBody] AddComplementDto dto)
+    {
+        if (dto == null)
+            throw UserFriendlyException.SameMessage("数据错误!");
+        if (string.IsNullOrEmpty(dto.Opinion))
+            throw UserFriendlyException.SameMessage("补充内容不能为空!");
+        if (dto.Opinion.Length > 2000)
+            throw UserFriendlyException.SameMessage("补充内容限制2000字!");
+        var data = await _orderDomainService.GetOrderAsync(dto.OrderId, cancellationToken: HttpContext.RequestAborted);
+        if (data == null)
+            throw UserFriendlyException.SameMessage("工单查询失败!");
+
+        OrderComplement complement = new OrderComplement()
+        {
+            OrderId = dto.OrderId,
+            Opinion = dto.Opinion,
+            SupplyName = _sessionContext.UserName,
+            SupplyTime = DateTime.Now,
+            No = data.No,
+            IsProComplement = false
+        };
+
+        var id = await _orderComplementRepository.AddAsync(complement, HttpContext.RequestAborted);
+        if (!string.IsNullOrEmpty(id))
+        {
+            //获取当前办理节点数据
+            var work = await _workflowStepRepository.GetAsync(p => p.Id == data.ActualHandleStepId, HttpContext.RequestAborted);
+            if (work != null)
+            {
+                //获取办理指定类型
+                var workflowStepHandler = work.GetWorkflowStepHandler();
+                if (workflowStepHandler != null)
+                {
+                    List<string> users = [];
+                    if (!string.IsNullOrEmpty(workflowStepHandler.UserId)) //指定用户
+                    {
+                        users.Add(workflowStepHandler.UserId);
+                    }
+                    else if (!string.IsNullOrEmpty(workflowStepHandler.RoleId))//指定角色
+                    {
+                        //查询指定角色下面所有的用户
+                        var userlist = await _userRepository.Queryable().Where(x =>
+                            x.OrgId == workflowStepHandler.OrgId && x.Roles.Any(d => workflowStepHandler.RoleId.Contains(d.Id))).Select(p => p.Id).ToListAsync();
+                        if (userlist != null && userlist.Count > 0)
+                            users.AddRange(userlist);
+                    }
+                    else if (!string.IsNullOrEmpty(workflowStepHandler.OrgId))//指定部门
+                    {
+                        //添加成功以后查询需要发送的人员信息
+                        var acceptSmsRoleIds = _systemSettingCacheManager.GetSetting(SettingConstants.AcceptSmsRoleIds)?.SettingValue;
+
+                        //查询指定部门下面经办人的信息
+                        var userlist = await _userRepository.Queryable().Where(x =>
+                            x.OrgId == workflowStepHandler.OrgId && x.Roles.Any(d => acceptSmsRoleIds.Contains(d.Id))).Select(p => p.Id).ToListAsync();
+                        if (userlist != null && userlist.Count > 0)
+                            users.AddRange(userlist);
+                    }
+                    if (users != null && users.Count > 0)
+                    {
+                        var msg = @"工单" + data.No + "有补充内容,请注意查收!";
+                        foreach (var item in users)
+                        {
+                            //发送消息
+                            await _realtimeService.OrderComplementAsync(item, msg, HttpContext.RequestAborted);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /// <summary>
+    /// 根据userid查询补充消息
+    /// </summary>
+    /// <param name="userId"></param>
+    /// <returns></returns>
+    [HttpGet("get_notification_wait_send/{userId}")]
+    public async Task<List<NotificationWaitSendDto>> GetNotificationWaitSend(string userId)
+    {
+        var data = await _notificationWaitSendRepository.Queryable()
+                .Where(p => p.UserId == userId && p.Method == RealtimeMethods.OrderComplementRecord&&p.State=="0")
+                .ToListAsync();
+        if (data != null && data.Count > 0)
+        {
+            await _notificationWaitSendRepository.RemoveRangeAsync(data, HttpContext.RequestAborted);
+        }
+        return _mapper.Map<List<NotificationWaitSendDto>>(data);
+    }
+    #endregion
 }

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

@@ -279,14 +279,15 @@ ICallApplication callApplication,
     /// <returns></returns>
     [HttpGet("phonenumber_test")]
     [AllowAnonymous]
-    public async Task<ThirdPhoneOutDto> GetPhoneNumberTest()
+    public async Task GetPhoneNumberTest()
     {
-        var inDto = new ThirdTokenDto
-        {
-            AppId = _systemSettingCacheManager.WxOpenAppId,
-            Secret = _systemSettingCacheManager.WxOpenAppSecret
-        };
-        return await _thirdIdentiyService.GetPhoneNumberAsync(inDto);
+        await _realtimeService.OrderComplementAsync("08db98a6-29f9-4812-8ccc-c81bc1ab2d12","测试推送消息", HttpContext.RequestAborted);
+        //var inDto = new ThirdTokenDto
+        //{
+        //    AppId = _systemSettingCacheManager.WxOpenAppId,
+        //    Secret = _systemSettingCacheManager.WxOpenAppSecret
+        //};
+        //return await _thirdIdentiyService.GetPhoneNumberAsync(inDto);
     }
 
     /// <summary>

BIN
src/Hotline.Api/Documents/luzhou/丰窝12345平台工单接入接口(V1.1-20250225) .docx


+ 11 - 8
src/Hotline.Api/Realtimes/RealtimeMethods.cs

@@ -14,7 +14,7 @@
         /// <summary>
         /// 电话挂断通知
         /// </summary>
-        public static string Bye= "Bye";
+        public static string Bye = "Bye";
         /// <summary>
         /// 小休审核通过通知
         /// </summary>
@@ -41,6 +41,10 @@
         /// </summary>
         public static string CircularRecord = "CircularRecord";
 
+        /// <summary>
+        /// 工单补充消息通知
+        /// </summary>
+        public static string OrderComplementRecord = "OrderComplementRecord";
 
         #endregion
 
@@ -55,13 +59,13 @@
         public static string BsDataShowArea7 = "BsDataShowArea7";
         public static string BsDataShowArea8 = "BsDataShowArea8";
 
-		#endregion
+        #endregion
 
-		#region 大屏-坐席数据
-		public static string BsSeatStateDataShowArea1 = "BsSeatStateDataShowArea1";
-		public static string BsSeatStateDataShowArea2 = "BsSeatStateDataShowArea2";
-		public static string BsSeatStateDataShowArea3 = "BsSeatStateDataShowArea3";
-		public static string BsSeatStateDataShowArea4 = "BsSeatStateDataShowArea4";
+        #region 大屏-坐席数据
+        public static string BsSeatStateDataShowArea1 = "BsSeatStateDataShowArea1";
+        public static string BsSeatStateDataShowArea2 = "BsSeatStateDataShowArea2";
+        public static string BsSeatStateDataShowArea3 = "BsSeatStateDataShowArea3";
+        public static string BsSeatStateDataShowArea4 = "BsSeatStateDataShowArea4";
         #endregion
 
         #region 大屏数据
@@ -86,4 +90,3 @@
         #endregion
     }
 }
-    

+ 57 - 23
src/Hotline.Api/Realtimes/RealtimeService.cs

@@ -1,4 +1,5 @@
-using Hotline.Article;
+using DocumentFormat.OpenXml.Spreadsheet;
+using Hotline.Article;
 using Hotline.Caching.Interfaces;
 using Hotline.Caching.Services;
 using Hotline.Realtimes;
@@ -18,17 +19,20 @@ public class RealtimeService : IRealtimeService, IScopeDependency
     private readonly IRealtimeCacheManager _realtimeCacheManager;
     private readonly IRepository<CircularRecord> _circularRecordRepository;
     private readonly IRepository<User> _userRepository;
+    private readonly IRepository<NotificationWaitSend> _notificationWaitSendRepository;
 
     public RealtimeService(
         IHubContext<HotlineHub> hubContext,
         IRealtimeCacheManager realtimeCacheManager,
         IRepository<CircularRecord> circularRecordRepository,
-        IRepository<User> userRepository)
+        IRepository<User> userRepository,
+        IRepository<NotificationWaitSend> notificationWaitSendRepository)
     {
         _hubContext = hubContext;
         _realtimeCacheManager = realtimeCacheManager;
         _circularRecordRepository = circularRecordRepository;
         _userRepository = userRepository;
+        _notificationWaitSendRepository = notificationWaitSendRepository;
     }
 
     #region 通讯通知
@@ -122,6 +126,36 @@ public class RealtimeService : IRealtimeService, IScopeDependency
 
     #endregion
 
+    /// <summary>
+    /// 工单补充消息通知 
+    /// </summary>
+    /// <param name="userId"></param>
+    /// <param name="msg"></param>
+    /// <param name="cancellationToken"></param>
+    /// <returns></returns>
+    public async Task OrderComplementAsync(string userId, string msg, CancellationToken cancellationToken)
+    {
+        var connection = await _realtimeCacheManager.GetConnectionOtherAsync(userId, cancellationToken);
+        if (connection == null)
+        {
+            //如果没有建立链接,写入数据库
+            NotificationWaitSend notificationWait = new NotificationWaitSend()
+            {
+                UserId = userId,
+                Msg = msg,
+                Method = RealtimeMethods.OrderComplementRecord,
+                State = "0"
+            };
+            await _notificationWaitSendRepository.AddAsync(notificationWait, cancellationToken);
+        }
+        else
+        {
+            //如果建立了链接直接发消息
+            await _hubContext.Clients.Client(connection.ConnectionId).SendAsync(RealtimeMethods.OrderComplementRecord, msg, cancellationToken);
+        }
+    }
+
+
     #region 系统信息通知
 
     /// <summary>
@@ -152,7 +186,7 @@ public class RealtimeService : IRealtimeService, IScopeDependency
                 {
                     await SendToUserAsync(user.Id, RealtimeMethods.CircularRecord,
                         new CircularRecoordDto()
-                            { CircularType = Share.Enums.Article.ECircularType.Org, Count = record.RecordCount },
+                        { CircularType = Share.Enums.Article.ECircularType.Org, Count = record.RecordCount },
                         cancellationToken);
                 }
                 catch
@@ -166,37 +200,37 @@ public class RealtimeService : IRealtimeService, IScopeDependency
 
     #region 大屏.数据展示
 
-    public Task BsDataShowChanged1Async(object obj, CancellationToken cancellationToken) => 
+    public Task BsDataShowChanged1Async(object obj, CancellationToken cancellationToken) =>
         SendToGroupAsync(RealtimeGroupNames.BigScreenDataShow, RealtimeMethods.BsDataShowArea1, obj, cancellationToken);
-    public Task BsDataShowChanged2Async(object obj, CancellationToken cancellationToken) => 
+    public Task BsDataShowChanged2Async(object obj, CancellationToken cancellationToken) =>
         SendToGroupAsync(RealtimeGroupNames.BigScreenDataShow, RealtimeMethods.BsDataShowArea2, obj, cancellationToken);
-    public Task BsDataShowChanged3Async(object obj, CancellationToken cancellationToken) => 
+    public Task BsDataShowChanged3Async(object obj, CancellationToken cancellationToken) =>
         SendToGroupAsync(RealtimeGroupNames.BigScreenDataShow, RealtimeMethods.BsDataShowArea3, obj, cancellationToken);
-    public Task BsDataShowChanged4Async(object obj, CancellationToken cancellationToken) => 
+    public Task BsDataShowChanged4Async(object obj, CancellationToken cancellationToken) =>
         SendToGroupAsync(RealtimeGroupNames.BigScreenDataShow, RealtimeMethods.BsDataShowArea4, obj, cancellationToken);
-    public Task BsDataShowChanged5Async(object obj, CancellationToken cancellationToken) => 
+    public Task BsDataShowChanged5Async(object obj, CancellationToken cancellationToken) =>
         SendToGroupAsync(RealtimeGroupNames.BigScreenDataShow, RealtimeMethods.BsDataShowArea5, obj, cancellationToken);
-    public Task BsDataShowChanged6Async(object obj, CancellationToken cancellationToken) => 
+    public Task BsDataShowChanged6Async(object obj, CancellationToken cancellationToken) =>
         SendToGroupAsync(RealtimeGroupNames.BigScreenDataShow, RealtimeMethods.BsDataShowArea6, obj, cancellationToken);
-    public Task BsDataShowChanged7Async(object obj, CancellationToken cancellationToken) => 
+    public Task BsDataShowChanged7Async(object obj, CancellationToken cancellationToken) =>
         SendToGroupAsync(RealtimeGroupNames.BigScreenDataShow, RealtimeMethods.BsDataShowArea7, obj, cancellationToken);
-    public Task BsDataShowChanged8Async(object obj, CancellationToken cancellationToken) => 
+    public Task BsDataShowChanged8Async(object obj, CancellationToken cancellationToken) =>
         SendToGroupAsync(RealtimeGroupNames.BigScreenDataShow, RealtimeMethods.BsDataShowArea8, obj, cancellationToken);
 
-	#endregion
+    #endregion
 
-	#region 大屏坐席数据
-	public Task BsSeatStateDataShowChangedAsync1(object obj, CancellationToken cancellationToken) =>
-		SendToGroupAsync(RealtimeGroupNames.BigScreenSeatState, RealtimeMethods.BsSeatStateDataShowArea1, obj, cancellationToken);
+    #region 大屏坐席数据
+    public Task BsSeatStateDataShowChangedAsync1(object obj, CancellationToken cancellationToken) =>
+        SendToGroupAsync(RealtimeGroupNames.BigScreenSeatState, RealtimeMethods.BsSeatStateDataShowArea1, obj, cancellationToken);
 
-	public Task BsSeatStateDataShowChangedAsync2(object obj, CancellationToken cancellationToken) =>
-		SendToGroupAsync(RealtimeGroupNames.BigScreenSeatState, RealtimeMethods.BsSeatStateDataShowArea2, obj, cancellationToken);
+    public Task BsSeatStateDataShowChangedAsync2(object obj, CancellationToken cancellationToken) =>
+        SendToGroupAsync(RealtimeGroupNames.BigScreenSeatState, RealtimeMethods.BsSeatStateDataShowArea2, obj, cancellationToken);
 
-	public Task BsSeatStateDataShowChangedAsync3(object obj, CancellationToken cancellationToken) =>
-		SendToGroupAsync(RealtimeGroupNames.BigScreenSeatState, RealtimeMethods.BsSeatStateDataShowArea3, obj, cancellationToken);
+    public Task BsSeatStateDataShowChangedAsync3(object obj, CancellationToken cancellationToken) =>
+        SendToGroupAsync(RealtimeGroupNames.BigScreenSeatState, RealtimeMethods.BsSeatStateDataShowArea3, obj, cancellationToken);
 
-	public Task BsSeatStateDataShowChangedAsync4(object obj, CancellationToken cancellationToken) =>
-		SendToGroupAsync(RealtimeGroupNames.BigScreenSeatState, RealtimeMethods.BsSeatStateDataShowArea4, obj, cancellationToken);
+    public Task BsSeatStateDataShowChangedAsync4(object obj, CancellationToken cancellationToken) =>
+        SendToGroupAsync(RealtimeGroupNames.BigScreenSeatState, RealtimeMethods.BsSeatStateDataShowArea4, obj, cancellationToken);
     #endregion
 
     #region 数据大屏
@@ -217,7 +251,7 @@ public class RealtimeService : IRealtimeService, IScopeDependency
     /// <param name="cancellationToken"></param>
     /// <returns></returns>
     public Task OrderHandlingDetailAsync(object obj, CancellationToken cancellationToken) =>
-        SendToGroupAsync(RealtimeGroupNames.BigDataScreen, RealtimeMethods.OrderHandlingDetail,obj, cancellationToken);
+        SendToGroupAsync(RealtimeGroupNames.BigDataScreen, RealtimeMethods.OrderHandlingDetail, obj, cancellationToken);
 
     /// <summary>
     /// 推送二次办理中工单概览
@@ -282,6 +316,6 @@ public class RealtimeService : IRealtimeService, IScopeDependency
 
     #endregion
 
-   
+
 
 }

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

@@ -36,6 +36,9 @@
     "LuZhou": {
       "AreaCode": "510500",
       "CallCenterType": "XingTang"
+    },
+    "FileUpload": {
+      "Url": "http://110.188.24.28:50120/"
     }
   },
   "CallCenterConfiguration": {
@@ -62,9 +65,9 @@
       "Ip": "222.213.23.229"
     },
     "XingTang": {
-        //"DbConnectionString": "server=123.56.10.71;Database=callcenter_db;Uid=root;Pwd=Lhw1981!(*!"
-        "DbConnectionString": "server=110.188.24.182;Database=callcenter_xingtang;Uid=dev;Pwd=fengwo11!!"
-        //"DbConnectionString": "PORT=50143;server=110.188.24.182;Database=callcenter_db;Uid=dev;Pwd=fengwo123!@#;"
+      //"DbConnectionString": "server=123.56.10.71;Database=callcenter_db;Uid=root;Pwd=Lhw1981!(*!"
+      "DbConnectionString": "server=110.188.24.182;Database=callcenter_xingtang;Uid=dev;Pwd=fengwo11!!"
+      //"DbConnectionString": "PORT=50143;server=110.188.24.182;Database=callcenter_db;Uid=dev;Pwd=fengwo123!@#;"
     }
   },
   "ConnectionStrings": {
@@ -78,7 +81,7 @@
     "Database": 3 //test:3, dev:5
   },
   "Swagger": true,
-  "AccLog":  false,
+  "AccLog": false,
   "Cors": {
     "Origins": [ "http://localhost:8888", "http://admin.hotline.fw.com", "http://localhost:80", "http://localhost:8113" ]
   },

+ 10 - 1
src/Hotline.Application/Orders/OrderApplication.cs

@@ -1258,7 +1258,16 @@ public class OrderApplication : IOrderApplication, IScopeDependency
 
         visit.IsPutThrough = dto.IsPutThrough;
         visit.AgainState = dto.IsAgain ? EAgainState.NeedAgain : EAgainState.NoAgain;
-        visit.EmployeeId = _sessionContextProvider.SessionContext.UserId;
+        //任务 314 关于修改回访结果后回访人更新的问题
+        if (_appOptions.Value.IsZiGong == true)
+        {
+            if (string.IsNullOrEmpty(visit.EmployeeId))
+                visit.EmployeeId = _sessionContextProvider.SessionContext.UserId;
+        }
+        else
+        {
+            visit.EmployeeId = _sessionContextProvider.SessionContext.UserId;
+        }
         visit.CallId = dto.CallId;
         if (first != null)
         {

+ 18 - 0
src/Hotline.Share/Dtos/Article/NotificationWaitSendDto.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Dtos.Article
+{
+    public class NotificationWaitSendDto
+    {
+        public string Id { get; set; }
+
+        public string Msg { get; set; }
+
+        public string? Method { get; set; }
+
+    }
+}

+ 9 - 1
src/Hotline.Share/Dtos/Order/AddOrderComplementDto.cs

@@ -45,4 +45,12 @@ public class AddOrderComplementDto
 public class OrderComplementDto : AddOrderComplementDto
 {
     public string Id { get; set; }
-}
+}
+
+public class AddComplementDto
+{
+    public string OrderId { get; set; }
+
+    public string Opinion {  set; get; }
+}
+

+ 16 - 0
src/Hotline.Share/Dtos/WebPortal/GetOrderCodePwd.cs

@@ -519,4 +519,20 @@ namespace Hotline.Share.Dtos.WebPortal
 
         public string Value { get; set; }
     }
+
+    public class GetFileDto
+    {
+        public GetResultDto? result { get; set; }
+        public int? code { get; set; }
+        public string? message { get; set; }
+        public string? error { get; set; }
+    }
+
+    public class GetResultDto
+    {
+        public string? id { get; set; }
+        public string? fileName { get; set; }
+        public string? path { get; set; }
+    }
+
 }

+ 56 - 0
src/Hotline.Share/Dtos/WebPortal/QueryKnowledgeList.cs

@@ -103,4 +103,60 @@ namespace Hotline.Share.Dtos.WebPortal
         /// </summary>
         public DateTime? CreationTime { get; set; }
     }
+
+
+    /// <summary>
+    /// 知识实体
+    /// </summary>
+    public class KnowledgeInfoThirdDto
+    {
+        /// <summary>
+        /// id
+        /// </summary>
+        public string Id { get; set; }
+        /// <summary>
+        /// 标题
+        /// </summary>
+       	public string? Title { get; set; }
+
+        /// <summary>
+        /// 内容
+        /// </summary>
+        public string? Content { get; set; }
+
+        /// <summary>
+        /// 浏览量
+        /// </summary>
+        public int PageView { get; set; } = 0;
+
+        /// <summary>
+        /// 附件
+        /// </summary>
+        public List<FileJson>? FileJson { get; set; }
+
+        /// <summary>
+        /// 搜索量
+        /// </summary>
+        public int? SearchNum { get; set; } = 0;
+
+        /// <summary>
+        /// 评分
+        /// </summary>
+        public decimal? Score { get; set; } = decimal.Zero;
+
+        /// <summary>
+        /// 评论数
+        /// </summary>
+        public int? CommentNum { get; set; } = 0;
+
+        /// <summary>
+        /// 创建部门
+        /// </summary>
+        public string? CreatorOrgName { get; set; }
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        public DateTime? CreationTime { get; set; }
+    }
 }

+ 7 - 0
src/Hotline.Share/Dtos/WebPortal/WebFlowAcceptDto.cs

@@ -1,5 +1,6 @@
 using Hotline.Share.Dtos.File;
 using Hotline.Share.Enums.Order;
+using System.ComponentModel.DataAnnotations;
 
 namespace Hotline.Share.Dtos.WebPortal
 {
@@ -33,6 +34,7 @@ namespace Hotline.Share.Dtos.WebPortal
         /// <summary>
         /// 姓名
         /// </summary>
+        [Required]
         public string LKName { get; set; }
 
         /// <summary>
@@ -58,6 +60,7 @@ namespace Hotline.Share.Dtos.WebPortal
         /// <summary>
         /// 电话
         /// </summary>
+        [Required]
         public string Mobile { get; set; }
 
         /// <summary>
@@ -73,11 +76,13 @@ namespace Hotline.Share.Dtos.WebPortal
         /// <summary>
         /// 标题
         /// </summary>
+        [Required]
         public string Title { get; set; }
 
         /// <summary>
         /// 信件内容
         /// </summary>
+        [Required]
         public string Content { get; set; }
 
         /// <summary>
@@ -88,11 +93,13 @@ namespace Hotline.Share.Dtos.WebPortal
         /// <summary>
         /// 受理类型ID
         /// </summary>
+        [Required]
         public string PurTypeID { get; set; }
 
         /// <summary>
         /// 受理类型名称
         /// </summary>
+        [Required]
         public string PurTypeName { get; set; }
 
         /// <summary>

+ 21 - 0
src/Hotline/Article/NotificationWaitSend.cs

@@ -0,0 +1,21 @@
+using XF.Domain.Repository;
+
+namespace Hotline.Article
+{
+    /// <summary>
+    /// 消息待推送
+    /// </summary>
+    public class NotificationWaitSend : CreationEntity
+    {
+        public string UserId { get; set; }
+
+        public string Msg { get; set; }
+
+        public string? Method { get; set; }
+
+        /// <summary>
+        /// 0:待发,1:已发
+        /// </summary>
+        public string? State { get; set; }
+    }
+}

+ 2 - 0
src/Hotline/Caching/Interfaces/IRealtimeCacheManager.cs

@@ -12,5 +12,7 @@ namespace Hotline.Caching.Interfaces
         Task<RealtimeConnection> GetConnectionAsync(string userId, CancellationToken cancellationToken);
 
         Task<bool> IsInGroupAsync(string useId, string groupName, CancellationToken cancellationToken);
+
+        Task<RealtimeConnection> GetConnectionOtherAsync(string userId, CancellationToken cancellationToken);
     }
 }

+ 5 - 0
src/Hotline/Caching/Services/RealtimeCacheManager.cs

@@ -28,4 +28,9 @@ public class RealtimeCacheManager : IRealtimeCacheManager, IScopeDependency
         var connection = await GetConnectionAsync(useId, cancellationToken);
         return connection.IsInGroup(groupName);
     }
+
+    public async Task<RealtimeConnection> GetConnectionOtherAsync(string userId, CancellationToken cancellationToken)
+    {
+        return await _cacheConnection.GetAsync(userId, cancellationToken);
+    }
 }

+ 9 - 3
src/Hotline/Configurations/AppConfiguration.cs

@@ -8,11 +8,12 @@ namespace Hotline.Configurations
 {
     public class AppConfiguration
     {
-        public string OldFilesUrls {  get; set; }
+        public string OldFilesUrls { get; set; }
         public string AppScope { get; set; }
         public YiBinConfiguration YiBin { get; set; }
         public ZiGongConfiguration ZiGong { get; set; }
         public LuZhouConfiguration LuZhou { get; set; }
+        public FileUploadConfiguration FileUpload { get; set; }
 
         public bool IsYiBin => CheckScopeIs(AppDefaults.AppScope.YiBin);
         public bool IsZiGong => CheckScopeIs(AppDefaults.AppScope.ZiGong);
@@ -41,13 +42,18 @@ namespace Hotline.Configurations
 
     public class ZiGongConfiguration : DefaultAppScopeConfiguration
     {
-		public AiQualityConfiguration AiQuality { get; set; }
-	}
+        public AiQualityConfiguration AiQuality { get; set; }
+    }
 
     public class LuZhouConfiguration : DefaultAppScopeConfiguration
     {
     }
 
+    public class FileUploadConfiguration
+    {
+        public string Url { get; set; }
+    }
+
     public abstract class DefaultAppScopeConfiguration
     {
         /// <summary>

+ 12 - 3
src/Hotline/Realtimes/IRealtimeService.cs

@@ -39,11 +39,11 @@ namespace Hotline.Realtimes
         Task BsDataShowChanged7Async(object obj, CancellationToken cancellationToken);
         Task BsDataShowChanged8Async(object obj, CancellationToken cancellationToken);
 
-		#endregion
+        #endregion
 
-		#region 大屏坐席数据
+        #region 大屏坐席数据
 
-		Task BsSeatStateDataShowChangedAsync1(object obj, CancellationToken cancellationToken);
+        Task BsSeatStateDataShowChangedAsync1(object obj, CancellationToken cancellationToken);
         Task BsSeatStateDataShowChangedAsync2(object obj, CancellationToken cancellationToken);
         Task BsSeatStateDataShowChangedAsync3(object obj, CancellationToken cancellationToken);
         Task BsSeatStateDataShowChangedAsync4(object obj, CancellationToken cancellationToken);
@@ -85,5 +85,14 @@ namespace Hotline.Realtimes
         Task CurrentWaitNumAsync(int count, CancellationToken cancellationToken);
 
         #endregion
+
+        /// <summary>
+        /// 工单补充消息通知
+        /// </summary>
+        /// <param name="userId"></param>
+        /// <param name="msg"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        Task OrderComplementAsync(string userId, string msg, CancellationToken cancellationToken);
     }
 }