using DotNetCore.CAP; using Hotline.Caching.Interfaces; using Hotline.Share.Dtos.Order; using Hotline.Share.Enums.Order; using MapsterMapper; using Microsoft.Extensions.Logging; using XF.Domain.Authentications; using XF.Domain.Cache; using XF.Domain.Dependency; using XF.Domain.Exceptions; using XF.Domain.Repository; using Hotline.CallCenter.Calls; using Hotline.File; using Hotline.FlowEngine.Workflows; using Hotline.Schedulings; using Hotline.SeedData; using Hotline.Users; using Hotline.Share.Dtos; using Hotline.Settings.Hotspots; using Hotline.Share.Dtos.FlowEngine; using Microsoft.AspNetCore.Http; using Hotline.Settings; using SqlSugar; using Hotline.Push.Notifies; using Hotline.Share.Enums.Push; using MediatR; using Hotline.Authentications; using Hotline.ContingencyManagement.Notifies; using Hotline.EventBus; using Hotline.Share.Enums.FlowEngine; using Mapster; using Microsoft.AspNetCore.Builder.Extensions; using Hotline.Configurations; using Microsoft.Extensions.Options; using Hotline.Share.Tools; using Hotline.Orders.Notifications; using Hotline.Share.Mq; using System.Security.Cryptography; using Hotline.FlowEngine.Definitions; using Hotline.Share.Dtos.FlowEngine.Workflow; using Hotline.Snapshot.Interfaces; using Hotline.Share.Enums.Settings; using Hotline.Settings.TimeLimitDomain; namespace Hotline.Orders; public class OrderDomainService : IOrderDomainService, IScopeDependency { private readonly IOrderRepository _orderRepository; private readonly IRepository _orderRedoRepository; private readonly IRepository _orderPublishRepository; private readonly IRepository _orderVisitRepository; private readonly IRepository _orderExtensionRepository; private readonly IRepository _orderComplementRepository; private readonly ITypedCache _cacheOrderNo; private readonly ISessionContext _sessionContext; private readonly ICapPublisher _capPublisher; private readonly IRepository _orderVisitDetailRepository; private readonly IMapper _mapper; private readonly ILogger _logger; private readonly IFileRepository _fileRepository; private readonly IRepository _schedulingRepository; private readonly Publisher _publisher; private readonly IOptionsSnapshot _appOptions; private readonly IRepository _userRepository; private readonly ISystemSettingCacheManager _systemSettingCacheManager; private readonly IWorkflowDomainService _workflowDomainService; private readonly IRepository _hotspotRepository; private readonly IMediator _mediator; private readonly IRepository _workflowStepRepository; private readonly ISystemLogRepository _systemLogRepository; private readonly IOrderSnapshotRepository _orderSnapshotRepository; private readonly ICalcExpireTime _expireTime; public OrderDomainService( IOrderRepository orderRepository, IRepository orderRedoRepository, IRepository orderPublishRepository, IRepository orderVisitRepository, IRepository orderExtensionRepository, IRepository orderComplementRepository, ITypedCache cacheOrderNo, ISessionContext sessionContext, ICapPublisher capPublisher, IMapper mapper, ILogger logger, IFileRepository fileRepository, IRepository wexCallRecordRepository, IRepository userRepository, ISystemSettingCacheManager systemSettingCacheManager, IRepository schedulingRepository, IWorkflowDomainService workflowDomainService, IRepository hotspotRepository, IMediator mediator, Publisher publisher, IRepository orderVisitDetailRepository, IRepository workflowStepRepository, ISystemLogRepository systemLogRepository, IOptionsSnapshot appOptions, IOrderSnapshotRepository orderSnapshotRepository, ICalcExpireTime expireTime) { _orderRepository = orderRepository; _orderRedoRepository = orderRedoRepository; _orderPublishRepository = orderPublishRepository; _orderVisitRepository = orderVisitRepository; _orderExtensionRepository = orderExtensionRepository; _orderComplementRepository = orderComplementRepository; _cacheOrderNo = cacheOrderNo; _sessionContext = sessionContext; _capPublisher = capPublisher; _mapper = mapper; _logger = logger; _fileRepository = fileRepository; _userRepository = userRepository; _schedulingRepository = schedulingRepository; _systemSettingCacheManager = systemSettingCacheManager; _workflowDomainService = workflowDomainService; _hotspotRepository = hotspotRepository; _mediator = mediator; _publisher = publisher; _orderVisitDetailRepository = orderVisitDetailRepository; _workflowStepRepository = workflowStepRepository; _systemLogRepository = systemLogRepository; _appOptions = appOptions; _orderSnapshotRepository = orderSnapshotRepository; _expireTime = expireTime; } /// /// 归档后自动发布 /// /// /// /// public async Task OrderAutomaticPublishAsync(Order order, CancellationToken cancellationToken) { var name = "中心直办件归档后默认自动发布"; var msg = $"OrderId: {order.Id}, No: {order.No}"; try { if (_systemSettingCacheManager.AutomaticPublishOrder == false) { return; } var isSnapshot = await _orderSnapshotRepository.AnyAsync(m => m.Id == order.Id, cancellationToken); if (isSnapshot == true) { _systemLogRepository.Add(name, msg, "随手拍工单不允许自动发布"); return; } var checkResult = order.AutoPublishCheck(); if (checkResult.NotNullOrEmpty()) { _systemLogRepository.Add(name, msg, checkResult); return; } if (order.FileOrgIsCenter == null || order.FileOrgIsCenter == false) { _systemLogRepository.Add(name, msg, "非中心直办件"); return; } await OrderPublishAsync(order, cancellationToken); } catch (Exception e) { _systemLogRepository.Add(name, msg, $"系统异常: {e.Message}"); } } public async Task OrderPublishAsync(Order order, CancellationToken cancellationToken) { OrderPublish orderPublish = new() { OrderId = order.Id, No = order.No, PublishState = false, //当前写死为false ArrangeTitle = order.Title, ArrangeContent = order.Content, ArrangeOpinion = order.FileOpinion, ProPublishState = false, FeedBackPhone = order.Contact, CreatorName = _sessionContext.UserName }; var id = await _orderPublishRepository.AddAsync(orderPublish, cancellationToken); order.Publish(orderPublish.PublishState); await _orderRepository.UpdateAsync(order, cancellationToken); //推省上 var publishPublishOrder = orderPublish.Adapt(); publishPublishOrder.Order = order.Adapt(); //查询实际办理附件 if (!string.IsNullOrEmpty(order.ActualHandleStepId)) { var actualHandleStep = await _workflowStepRepository.GetAsync(order.ActualHandleStepId, cancellationToken); publishPublishOrder.FileJsons = actualHandleStep?.FileJson; } await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderPublishOrder, publishPublishOrder, cancellationToken: cancellationToken); //推应急管理局 //是否开启 var isOpenContingencyManagement = _systemSettingCacheManager.GetSetting(SettingConstants.IsOpenContingencyManagement)?.SettingValue[0]; if (isOpenContingencyManagement == "true") await _publisher.PublishAsync(new ContingencyManagementNotify(order, order.Title, order.Content, order.ActualOpinion), PublishStrategy.ParallelWhenAll, cancellationToken); // 取消发布的工单数量 var orderPublishDeletedCount = await _orderPublishRepository.Queryable(includeDeleted: true) .Where(m => m.OrderId == order.Id && m.IsDeleted == true) .CountAsync(cancellationToken); var orderVisitVisitedCount = await _orderVisitRepository.Queryable() .Where(m => m.OrderId == order.Id && m.VisitState == EVisitState.Visited) .CountAsync(cancellationToken); // 若取消发布的工单,已经被回访过了,没有经过重新办理,再次发布后,自动跳过回访环节,展示取消发布前的回访结果 if (orderPublishDeletedCount != 0 && orderVisitVisitedCount != 0) { return; } var orderVisit = new OrderVisit { No = order.No, OrderId = order.Id, VisitState = EVisitState.WaitForVisit, PublishTime = DateTime.Now, IsCanHandle = true, EmployeeId = _sessionContext.RequiredUserId }; if (!order.IsProvince) { orderVisit.EmployeeId = _sessionContext.RequiredUserId; } if (_appOptions.Value.IsZiGong) { orderVisit.EmployeeId = string.Empty; } if (order is { FileOrgIsCenter: true, CounterSignType: null } && !order.IsProvince) { orderVisit.VisitState = EVisitState.Visited; orderVisit.VisitTime = DateTime.Now; orderVisit.VisitType = EVisitType.OtherVisit; orderVisit.NowEvaluate = new Kv() { Key = "4", Value = "满意" }; if (_appOptions.Value.IsZiGong) { // 根据禅道 自贡需求 Id_361, 第一条, 3小条需求; // 直办件归档后自动回访量需统计在“胡玲”的默认回访量中; orderVisit.EmployeeId = _systemSettingCacheManager.DefaultVisitEmployeeId; } } if (order.CounterSignType != ECounterSignType.Center) { orderVisit.IsCanAiVisit = true; } string visitId = await _orderVisitRepository.AddAsync(orderVisit); //新增回访信息 var visitedDetail = new List(); var seatDetail = new OrderVisitDetail(); seatDetail.VisitId = visitId; seatDetail.VisitTarget = EVisitTarget.Seat; var orgDetail = new OrderVisitDetail(); orgDetail.VisitId = visitId; orgDetail.VisitOrgCode = order.ActualHandleOrgCode; orgDetail.VisitOrgName = order.ActualHandleOrgName; orgDetail.VisitTarget = EVisitTarget.Org; if (order is { FileOrgIsCenter: true, CounterSignType: null, IsProvince: false }) { var satisfy = new Kv() { Key = "4", Value = "满意" }; orgDetail.OrgProcessingResults = satisfy; //orgDetail.OrgHandledAttitude = satisfy; } visitedDetail.Add(orgDetail); if (order is { FileOrgIsCenter: true, CounterSignType: null }) { seatDetail.VoiceEvaluate = EVoiceEvaluate.Satisfied; seatDetail.SeatEvaluate = ESeatEvaluate.Satisfied; order.Visited("4", "满意"); order.Status = EOrderStatus.Visited; await _orderRepository.UpdateAsync(order, cancellationToken); } visitedDetail.Add(seatDetail); await _orderVisitDetailRepository.AddRangeAsync(visitedDetail, cancellationToken); await _publisher.PublishAsync(new ContingencyManagementNotify(order, order.Title, order.Content, order.ActualOpinion), PublishStrategy.ParallelWhenAll, cancellationToken); //handle publish trace var acceptor = new UserInfo(orderPublish.CreatorId, orderPublish.CreatorName, orderPublish.CreatorOrgId, orderPublish.CreatorOrgName); var handler = new UserInfo(orderPublish.CreatorId, orderPublish.CreatorName, orderPublish.CreatorOrgId, orderPublish.CreatorOrgName); //上面回访人取值当前操作人,所以暂时可取值SessionContext,如需求有变需取值ordervisit中指定的回访人 var visitAcceptor = string.IsNullOrEmpty(orderVisit.EmployeeId) ? new UserInfo() : new UserInfo(_sessionContext.UserId, _sessionContext.UserName, _sessionContext.OrgId, _sessionContext.OrgName, _sessionContext.OrgIsCenter); await _workflowDomainService.HandlePublishTraceAsync(order.WorkflowId, orderPublish.Id, acceptor, handler, orderPublish.CreationTime, visitAcceptor, orderVisit.Id, cancellationToken); //需求251 某些工单需自动发送短信 //任何类型的省工单都不需要发送短信 var isOpenSendEndSms = _systemSettingCacheManager.GetSetting(SettingConstants.IsOpenSendEndSms)?.SettingValue[0]; if (isOpenSendEndSms == "true") { try { if (order.Source != ESource.ProvinceStraight && order.FileOrgIsCenter.Value == false) { var code = ""; //受理类型为“投诉、举报” if ((order.AcceptTypeCode == "30" || order.AcceptTypeCode == "35") && orderVisit.VisitState != EVisitState.Visited) { code = "1017"; orderVisit.VisitState = EVisitState.SMSVisiting; await _orderVisitRepository.UpdateAsync(orderVisit); } else code = "1018"; //发送查询短信 var messageDto = new Share.Dtos.Push.MessageDto { PushBusiness = EPushBusiness.SearchSms, ExternalId = visitId, OrderId = order.Id, PushPlatform = EPushPlatform.Sms, Remark = order.Title, Name = order.FromName, TemplateCode = code, Params = new List() { order.No, order.Password }, TelNumber = order.Contact, }; await _mediator.Publish(new PushMessageNotify(messageDto), cancellationToken); // 发送短信后推送一个 48小时的延迟消息队列. 当消息队列收到消息时, 判断用户是否回复了, 如果未回复短信就 默认满意 var delaySecond = _systemSettingCacheManager.DefaultVisitSmsDelaySecond; await _capPublisher.PublishDelayAsync( TimeSpan.FromSeconds(delaySecond), EventNames.UpdateVisitDelaySms, messageDto, cancellationToken: cancellationToken); } } catch (Exception) { } } if (order.IsProvince == false && orderVisit.VisitState == EVisitState.Visited) { //推省上 await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderVisited, new PublishVisitDto() { Order = order.Adapt(), No = orderVisit.No, VisitType = orderVisit.VisitType, VisitName = orderVisit.CreatorName, VisitTime = orderVisit.VisitTime, VisitRemark = orderVisit.NowEvaluate?.Value, AreaCode = order.AreaCode!, SubjectResultSatifyCode = orderVisit.NowEvaluate?.Key, FirstSatisfactionCode = orderVisit.NowEvaluate?.Key, ClientGuid = "" }, cancellationToken: cancellationToken); } //推门户 await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderVisitedWeb, new PublishVisitAllDto() { Id = orderVisit.Id, Order = order.Adapt(), OrderVisitDetails = orderVisit.OrderVisitDetails.Adapt>(), VisitName = _sessionContext.UserName, VisitTime = orderVisit.VisitTime, VisitType = orderVisit.VisitType, VisitState = orderVisit.VisitState, PublishTime = orderVisit.PublishTime, }, cancellationToken: cancellationToken); } /// /// 重办和退回工单时如果有取消发布的工单, 清除回访待办和回访列表中的数据 /// /// /// /// public async Task VisitNoneByCancelPublishAsync(string orderId, CancellationToken cancellationToken) { var cancelPublishOrderEnabled = _systemSettingCacheManager.CancelPublishOrderEnabled; // 取消发布的工单数量 var orderPublishDeletedCount = await _orderPublishRepository.Queryable(includeDeleted: true) .Where(m => m.OrderId == orderId && m.IsDeleted == true) .CountAsync(cancellationToken); if (orderPublishDeletedCount == 0 || cancelPublishOrderEnabled == false) return; var visit = await _orderVisitRepository.GetAsync(x => x.OrderId == orderId && x.VisitState != EVisitState.None, cancellationToken); if (visit != null) { visit.VisitState = EVisitState.None; await _orderVisitRepository.UpdateAsync(visit, cancellationToken); } } public async Task GetOrderAsync(string? orderId, bool withHotspot = false, bool withAcceptor = false, bool withExtension = false, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(orderId)) throw UserFriendlyException.SameMessage("无效工单编号"); var query = _orderRepository.Queryable(); if (withHotspot) query = query.Includes(d => d.Hotspot); if (withAcceptor) query = query.Includes(d => d.Acceptor); if (withExtension) query = query.Includes(d => d.OrderExtension); var order = await query.FirstAsync(d => d.Id == orderId, cancellationToken); if (order == null) throw new UserFriendlyException($"无效工单编号, orderId: {orderId}", "无效工单编号"); return order; } public async Task AddAsync(Order order, bool autoAccept = false, CancellationToken cancellationToken = default) { if (autoAccept) { order.AutoAccept(_sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.StaffNo); order.Sign(_sessionContext.RequiredUserId, _sessionContext.UserName); } order.Init(); order.No = GenerateNewOrderNo(); if (order.Source == ESource.Police110) { order.Title = @"(非警情工单-" + order.No.Substring(order.No.Length - 6) + ")"; } order.Password = Random.Shared.Next(100000, 1000000).ToString(); order.ProvinceNo = string.IsNullOrEmpty(order.ProvinceNo) ? GenerateNewProvinceNo(order.No, order.SourceChannelCode) : order.ProvinceNo; return await _orderRepository.AddOrderNavAsync(order, cancellationToken); } /// /// 撤回或跳转前处理数据及校验 /// ///工单撤回时需校验工单当前是否存在待发布记录、待回访记录,若存在需删除对应记录(跳转同理) ///工单撤回时需校验工单是否存在甄别中记录,若存在不允许撤回当前工单(跳转同理) /// /// public async Task ReadyToRecallAsync(string orderId, CancellationToken cancellationToken) { var order = await _orderRepository.Queryable() .Includes(d => d.OrderPublish) .Includes(d => d.OrderVisits.Where(d => d.VisitState == EVisitState.WaitForVisit)) .Includes(d => d.OrderScreens) .FirstAsync(d => d.Id == orderId, cancellationToken); if (order.OrderScreens.Any()) throw UserFriendlyException.SameMessage("已甄别工单无法撤回或跳转"); //todo 需求调整:P72, 4,5,6,7,8,9,10 if (order.OrderPublish is not null) await _orderPublishRepository.RemoveAsync(order.OrderPublish, cancellationToken: cancellationToken); if (order.OrderVisits.Any()) await _orderVisitRepository.RemoveNav(order.OrderVisits) .Include(d => d.OrderVisitDetails) .ExecuteCommandAsync(); } public Task AddOrderRedoAsync(OrderRedo orderRedo, CancellationToken cancellationToken) => _orderRedoRepository.AddAsync(orderRedo, cancellationToken); public Task RemoveOrderRedoAsync(string id, CancellationToken cancellationToken) => _orderRedoRepository.RemoveAsync(id, cancellationToken: cancellationToken); public Task UpdateOrderRedoAsync(OrderRedo orderRedo, CancellationToken cancellationToken) => _orderRedoRepository.UpdateAsync(orderRedo, cancellationToken); #region 工单扩展信息 public async Task GetOrderExtensionsAsync(string provinceNo, CancellationToken cancellationToken) => await _orderExtensionRepository.GetAsync(d => d.ProvinceNo == provinceNo, cancellationToken); public Task UpdateExtensionAsync(OrderExtension orderExtension, CancellationToken cancellationToken) => _orderExtensionRepository.UpdateAsync(orderExtension, cancellationToken); /// /// 新增工单扩展信息 /// public async Task AddExtensionAsync(OrderExtension extension, CancellationToken cancellationToken) => await _orderExtensionRepository.AddAsync(extension, cancellationToken); /// /// 新增工单补充信息 /// public async Task AddOrderComplementAsync(AddOrderComplementDto dto, CancellationToken cancellationToken) { var complement = _mapper.Map(dto); complement.InitId(); if (dto.Files.Any()) complement.FileJson = await _fileRepository.AddFileAsync(dto.Files, complement.Id, "", cancellationToken); return await _orderComplementRepository.AddAsync(complement, cancellationToken); } #endregion /// /// 验证是否是管理员 /// /// public bool IsCheckAdmin() { var systemAdministrator = _systemSettingCacheManager.GetSetting(SettingConstants.SystemAdministrator)?.SettingValue[0]; if (!string.IsNullOrEmpty(systemAdministrator) && (_sessionContext.Roles.Contains(systemAdministrator) || _sessionContext.Roles.Contains(RoleSeedData.AdminRole))) return true; else return false; } #region 平均派单 /// /// 平均派单 /// /// public async Task AverageOrder(CancellationToken cancellationToken) { var time = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd")); var scheduling = await _schedulingRepository.Queryable().Includes(x => x.SchedulingUser) .Where(x => x.SchedulingTime == time && x.WorkingTime <= DateTime.Now.TimeOfDay && x.OffDutyTime >= DateTime.Now.TimeOfDay && x.AtWork == true) .OrderBy(x => x.SendOrderNum).FirstAsync(cancellationToken); var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0]; var roleId = _systemSettingCacheManager.GetSetting(SettingConstants.RolePaiDan)?.SettingValue[0]; if (scheduling is null) return new StepAssignInfo { Key = AppDefaults.SendPoolId, Value = "待派单池", UserId = AppDefaults.SendPoolId, Username = "待派单池", OrgId = OrgSeedData.CenterId, OrgName = centerOrgName, RoleId = roleId, RoleName = "派单员", FlowAssignType = EFlowAssignType.User, }; scheduling.SendOrderNum++; await _schedulingRepository.UpdateAsync(scheduling, cancellationToken); var user = scheduling.SchedulingUser; return new StepAssignInfo { Key = user.UserId, Value = user.UserName, UserId = user.UserId, Username = user.UserName, OrgId = user.OrgId, OrgName = user.OrgIdName, RoleId = roleId, RoleName = "派单员", FlowAssignType = EFlowAssignType.User, }; } /// /// 登录平均派单 /// /// /// public async Task LogAverageOrder(string userId, Scheduling scheduling, CancellationToken cancellationToken) { //1.获取默认派单员所属的工单 //2.获取今天上班的人员 //3.给当前这个用户平均派单 var steps = await _workflowDomainService.GetStepsBelongsToAsync(AppDefaults.SendPoolId, cancellationToken); var roleId = _systemSettingCacheManager.GetSetting(SettingConstants.RolePaiDan)?.SettingValue[0]; var user = await _userRepository.Queryable() .Includes(d => d.Organization) .FirstAsync(d => d.Id == userId, cancellationToken); var time = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd")); var schedulings = await _schedulingRepository.Queryable().Includes(x => x.SchedulingUser) .Where(x => x.SchedulingTime == time).CountAsync(cancellationToken); if (schedulings > 0) { var sendNum = steps.Count / schedulings; scheduling.SendOrderNum += sendNum; if (!scheduling.LoginSendOrderNum.HasValue) { scheduling.LoginSendOrderNum = scheduling.LoginSendOrderNum.HasValue && scheduling.LoginSendOrderNum > sendNum ? scheduling.LoginSendOrderNum : sendNum; await _schedulingRepository.Updateable() .SetColumns(s => new Scheduling() { LoginSendOrderNum = scheduling.LoginSendOrderNum }) .Where(s => s.SchedulingTime == scheduling.SchedulingTime).ExecuteCommandAsync(cancellationToken); } sendNum = scheduling.LoginSendOrderNum.Value; await _schedulingRepository.Updateable() .SetColumns(s => new Scheduling() { SendOrderNum = scheduling.SendOrderNum, AtWork = scheduling.AtWork }) .Where(s => s.Id == scheduling.Id).ExecuteCommandAsync(cancellationToken); if (sendNum <= 0) return; var sendSteps = steps.Take(sendNum).ToList(); await _orderRepository.Updateable().SetColumns(o => new Order() { CenterToOrgHandlerId = user.OrgId, CenterToOrgHandlerName = user.Name }).Where(o => sendSteps.Any(s => s.ExternalId == o.Id)).ExecuteCommandAsync(cancellationToken); await _workflowDomainService.ChangeHandlerBatchAsync(new List<(string userId, string username, string orgId, string orgName, string? roleId, string? roleName, ICollection steps)> { new(user.Id, user.Name, user.OrgId, user.Organization.Name,roleId,"派单员", sendSteps) }, cancellationToken); } } /// /// 触发平均派单 /// /// public async Task TriggerAverageOrder(CancellationToken cancellationToken) { //1.从排班里面获取今天上班的人 //2.获取默认派单员剩下的工单 //3.平均分配剩下的工单给今天上班的人 DateTime time = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd")); var schedulings = await _schedulingRepository.Queryable().Includes(x => x.SchedulingUser) .Where(x => x.SchedulingTime!.Value == time && x.WorkingTime!.Value <= DateTime.Now.TimeOfDay && x.OffDutyTime!.Value >= DateTime.Now.TimeOfDay && x.AtWork == true) .OrderBy(x => x.SendOrderNum).ToListAsync(cancellationToken); var roleId = _systemSettingCacheManager.GetSetting(SettingConstants.RolePaiDan)?.SettingValue[0]; if (schedulings.Any()) { var steps = await _workflowDomainService.GetStepsBelongsToAsync(AppDefaults.SendPoolId, cancellationToken); if (steps.Any()) { List<(string userId, string username, string orgId, string orgName, string? roleId, string? roleName, ICollection steps)> handlers = new(); var avg = steps.Count / schedulings.Count; var remaining = steps.Count % schedulings.Count; var skip = 0; for (var i = 0; i < schedulings.Count; i++) { var scheduling = schedulings[i]; var size = avg + (i < remaining ? 1 : 0); if (size > 0) { var stSteps = steps.Skip(skip).Take(size).ToList(); handlers.Add(new( scheduling.SchedulingUser.UserId, scheduling.SchedulingUser.UserName, scheduling.SchedulingUser.OrgId, scheduling.SchedulingUser.OrgIdName, roleId, "派单员", stSteps)); skip += size; scheduling.SendOrderNum += size; await _schedulingRepository.Updateable() .SetColumns(s => new Scheduling() { SendOrderNum = scheduling.SendOrderNum }) .Where(s => s.Id == scheduling.Id).ExecuteCommandAsync(cancellationToken); await _orderRepository.Updateable().SetColumns(o => new Order() { CenterToOrgHandlerId = scheduling.SchedulingUser.UserId, CenterToOrgHandlerName = scheduling.SchedulingUser.UserName }) .Where(o => stSteps.Any(s => s.ExternalId == o.Id)).ExecuteCommandAsync(cancellationToken); } } if (handlers.Any()) await _workflowDomainService.ChangeHandlerBatchAsync(handlers, cancellationToken); } } } #endregion #region 工单校验- 交通类工单 /// /// 工单校验 - 交通类工单 /// /// public async Task OrderValidation(AddOrderDto dto, CancellationToken cancellationToken) { var valid = new OrderValidation { Validation = false, Result = "" }; var hotspot = await _hotspotRepository.GetAsync(dto.HotspotId, cancellationToken); if (hotspot != null && hotspot.TrunkNum.Equals(AppDefaults.TrafficTrunkNum)) { switch (dto.AcceptTypeCode) { //投诉举报 case "30": case "35": valid.Validation = dto.Title.Contains("意见") || dto.Title.Contains("建议") || dto.Title.Contains("咨询") || dto.Content.Contains("意见") || dto.Content.Contains("建议") || dto.Content.Contains("咨询"); if (dto.Content.Length < 25) { valid.Validation = true; valid.Result = "保存失败,受理内容字数不足,需要至少25个字!"; } break; // 意见 case "1": valid.Validation = dto.Title.Contains("投诉") || dto.Title.Contains("举报") || dto.Title.Contains("咨询") || dto.Content.Contains("投诉") || dto.Content.Contains("举报") || dto.Content.Contains("咨询"); if (dto.Content.Length < 5) { valid.Validation = true; valid.Result = "保存失败,受理内容字数不足,需要至少5个字!"; } break; //建议求助 case "15": case "20": valid.Validation = dto.Title.Contains("投诉") || dto.Title.Contains("举报") || dto.Title.Contains("咨询") || dto.Content.Contains("投诉") || dto.Content.Contains("举报") || dto.Content.Contains("咨询"); if (dto.Content.Length < 25) { valid.Validation = true; valid.Result = "保存失败,受理内容字数不足,需要至少25个字!"; } break; // 咨询 case "10": valid.Validation = dto.Title.Contains("投诉") || dto.Title.Contains("举报") || dto.Title.Contains("意见") || dto.Title.Contains("建议") || dto.Content.Contains("投诉") || dto.Content.Contains("举报") || dto.Content.Contains("意见") || dto.Content.Contains("建议"); if (dto.Content.Length < 5) { valid.Validation = true; valid.Result = "保存失败,受理内容字数不足,需要至少5个字!"; } break; // 表扬 case "25": if (dto.Content.Length < 25) { valid.Validation = true; valid.Result = "保存失败,受理内容字数不足,需要至少25个字!"; } break; default: if (dto.Content.Length < 5) { valid.Validation = true; valid.Result = "保存失败,受理内容字数不足,需要至少5个字!"; } break; } } if (valid.Validation && string.IsNullOrEmpty(valid.Result)) valid.Result = "标题或受理内容出现限制词,请检查!"; return valid; } #endregion #region 即将超期和超期短信 /// /// 即将超期和超期短信 /// /// public async Task SendOverTimeSms(CancellationToken cancellationToken) { var now = DateTime.Now; var isSend = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.IsSendOverTimeSms).SettingValue[0]); if (!isSend) { return; } //查询即将超期和超期工单 var orderList = await _orderRepository.Queryable() .Where(x => x.Status < EOrderStatus.Filed && !string.IsNullOrEmpty(x.CurrentHandleOrgId)) .GroupBy(x => x.CurrentHandleOrgId) .Select(x => new OverTimeOrderDto { OrgId = x.CurrentHandleOrgId, NearlyOrderCount = SqlFunc.AggregateSum(SqlFunc.IIF(now >= x.NearlyExpiredTime && now < x.ExpiredTime, 1, 0)), ExpiredTimeOrderCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.ExpiredTime < now, 1, 0)) }) .ToListAsync(cancellationToken); foreach (var item in orderList) { if (item.NearlyOrderCount == 0 && item.ExpiredTimeOrderCount == 0) { continue; } var acceptSmsRoleIds = _systemSettingCacheManager.GetSetting(SettingConstants.AcceptSmsRoleIds)?.SettingValue; //查询部门所有账号 var userlist = await _userRepository.Queryable().Where(x => x.OrgId == item.OrgId && !string.IsNullOrEmpty(x.PhoneNo) && x.Roles.Any(d => acceptSmsRoleIds.Contains(d.Id))).ToListAsync(); //发送短信 foreach (var user in userlist) { var messageDto = new Share.Dtos.Push.MessageDto { PushBusiness = EPushBusiness.OrderExpire, PushPlatform = EPushPlatform.Sms, Name = user.Name, TemplateCode = "1009", Params = new List() { item.NearlyOrderCount.ToString(), item.ExpiredTimeOrderCount.ToString() }, TelNumber = user.PhoneNo, }; await _mediator.Publish(new PushMessageNotify(messageDto), cancellationToken); } } } #endregion #region 计算甄别申请截至日期 public async Task GetScreenByEndTime() { var endTime = DateTime.Now; var beginTime = DateTime.Now; var timeValue = 1; if (_appOptions.Value.IsZiGong) { timeValue = 2; } else if (_appOptions.Value.IsLuZhou) { DateTime firstDayOfMonth = new DateTime(beginTime.Year, beginTime.Month, 1); DateTime lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1); beginTime = new DateTime(lastDayOfMonth.Year, lastDayOfMonth.Month, lastDayOfMonth.Day, 23, 59, 59); } else { var setting = _systemSettingCacheManager.GetSetting(SettingConstants.ScreenApplyEndTime); if (int.Parse(setting?.SettingValue[0]) > 0) { timeValue = int.Parse(setting?.SettingValue[0]); } } endTime = (await _expireTime.CalcEndTime(beginTime, beginTime, ETimeType.WorkDay, timeValue, 0, 0)).EndTime; return endTime; } #endregion #region private private async Task GetOrderByFlowIdAsync(string workflowId, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(workflowId)) throw UserFriendlyException.SameMessage("无效流程编号"); var order = await _orderRepository.Queryable() .Includes(d => d.Hotspot) .FirstAsync(d => d.WorkflowId == workflowId); if (order == null) throw new UserFriendlyException($"无效流程编号, workflowId: {workflowId}", "无效流程编号"); return order; } private CacheOrderNO GetCurrentOrderNo() { var today = DateTime.Today; var cacheKey = today.ToString("yyyyMMdd"); var cacheOrderNo = _cacheOrderNo.GetOrSet(cacheKey, f => { var todayOrderCount = _orderRepository.Queryable(includeDeleted: true) .CountAsync(d => d.CreationTime.Date == today.Date) .GetAwaiter() .GetResult(); return new CacheOrderNO { TotalCount = todayOrderCount }; }, TimeSpan.FromDays(1)); return cacheOrderNo; } public string GenerateNewOrderNo() { var today = DateTime.Today; var cacheKey = $"{today:yyyyMMdd}"; var cacheOrderNo = _cacheOrderNo.GetOrSet(cacheKey, f => { var todayOrderCount = _orderRepository.Queryable(true) .CountAsync(d => d.CreationTime.Date == today.Date) .GetAwaiter() .GetResult(); return new CacheOrderNO { TotalCount = todayOrderCount }; }, TimeSpan.FromDays(1)); cacheOrderNo!.TotalCount += 1; var no = GenerateOrderNo(today, cacheOrderNo.TotalCount); _cacheOrderNo.Set(cacheKey, cacheOrderNo); return no; } private string GenerateNewProvinceNo(string no, string sourceChannelCode) { //诉求渠道+成员单位标识+行政区划代码+年月日+流水号 //成员单位标识 99 //宜宾市 511500 市级 var today = DateTime.Today; //var count = no.Substring(no.Length - 5); var setting = _systemSettingCacheManager.GetSetting(SettingConstants.VersionsAreaCode); var versionsAreaCode = setting?.SettingValue[0]; //todo 双系统并行暂时执行此方案 var count = no.Substring(no.Length - 4); count = (Convert.ToInt32(count) + 50000).ToString(); var provinceCodes = new[] { "RGDH", "WX", "WB", "AP", "WZ", "YJ", "SCZWFWW", "XCX", "QT" }; var prefix = provinceCodes.Any(d => d.Equals(sourceChannelCode)) ? sourceChannelCode : "QT"; return $"{prefix}99{versionsAreaCode}{today:yyMMdd}{count}"; } private string GenerateOrderNo(DateTime today, int count) { return $"{today:yyyyMMdd}{count:000000}"; } #endregion } public class CacheOrderNO { public int TotalCount { get; set; } } public class OrderValidation { public bool Validation { get; set; } public string Result { get; set; } }