using CallCenter.Caches; using CallCenter.Calls; using CallCenter.Devices; using CallCenter.Settings; using CallCenter.Share.Dtos; using CallCenter.Share.Enums; using MapsterMapper; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using XF.Domain.Cache; using XF.Domain.Dependency; using XF.Domain.Exceptions; namespace CallCenter.Ivrs; public class IvrDomainService : IIvrDomainService, IScopeDependency { private readonly IDeviceManager _deviceManager; private readonly IIvrRepository _ivrRepository; private readonly ITypedCache> _cacheIvrList; private readonly IIvrCacheManager _ivrCacheManager; private readonly ITelCacheManager _telCacheManager; private readonly IUserCacheManager _userCacheManager; private readonly IMapper _mapper; private readonly ILogger _logger; private readonly IOptionsSnapshot _worktimeOptions; private readonly ITypedCache _worktimeCache; private readonly ITypedCache> _daySettingCache; private readonly IDaySettingRepository _daySettingRepository; public IvrDomainService( IDeviceManager deviceManager, IIvrRepository ivrRepository, ITypedCache> cacheIvrList, IIvrCacheManager ivrCacheManager, ITelCacheManager telCacheManager, IUserCacheManager userCacheManager, IMapper mapper, ILogger logger, IOptionsSnapshot worktimeOptions, ITypedCache worktimeCache, ITypedCache> daySettingCache, IDaySettingRepository daySettingRepository) { _deviceManager = deviceManager; _ivrRepository = ivrRepository; _cacheIvrList = cacheIvrList; _ivrCacheManager = ivrCacheManager; _telCacheManager = telCacheManager; _userCacheManager = userCacheManager; _mapper = mapper; _logger = logger; _worktimeOptions = worktimeOptions; _worktimeCache = worktimeCache; _daySettingCache = daySettingCache; _daySettingRepository = daySettingRepository; } /// /// 语音播放结束 /// /// /// /// public async Task GetVoiceEndAnswerAsync(string menuId, CancellationToken cancellationToken = default) { var ivrs = _ivrCacheManager.GetIvrs(); if (!ivrs.Any()) throw new UserFriendlyException("未查到任何ivr配置"); var ivr = ivrs.FirstOrDefault(d => d.No == menuId); if (ivr is null) throw new UserFriendlyException("未知语音菜单编号"); var strategy = ivr.IvrStrategies.FirstOrDefault(d => d.IvrStrategeType == EIvrStrategeType.VoiceEnd); var answer = await ValuationStrategyAnswer(strategy, ivr, cancellationToken); _logger.LogInformation("语音播放结束响应策略: type: {type}, content: {content}, preVoice: {voice}", answer?.IvrAnswerTypeText, answer?.Content, answer?.PreVoice); return answer; } /// /// 获得按键输入 /// /// /// /// /// public async Task GetDtmfAnswerAsync(string menuId, string input, CancellationToken cancellationToken = default) { var ivrs = _ivrCacheManager.GetIvrs(); if (!ivrs.Any()) throw new UserFriendlyException("未查到任何ivr配置"); if (string.IsNullOrEmpty(input)) throw new UserFriendlyException("无效输入参数"); var ivr = ivrs.FirstOrDefault(d => d.No == menuId); if (ivr is null) throw new UserFriendlyException("未知语音菜单编号"); if (input.Length > ivr.InfoLength) throw new UserFriendlyException("按键输入内容超出配置允许长度"); IvrAnswer? answer; //超时 if (input.EndsWith('T')) { var overTimeStrategy = ivr.IvrStrategies.FirstOrDefault(d => d.IvrStrategeType == EIvrStrategeType.OverTime); if (overTimeStrategy is null) { answer = new IvrAnswer { IvrAnswerType = EIvrAnswerType.HangUp }; _logger.LogInformation($"未配置IVR超时策略, menuId: {menuId}"); return answer; } overTimeStrategy.Answer.PreVoice = GetStrategyVoice(overTimeStrategy, ivr); answer = overTimeStrategy.Answer; _logger.LogInformation("按键响应策略: type: {type}, content: {content}, preVoice: {voice}", answer?.IvrAnswerTypeText, answer?.Content, answer?.PreVoice); return answer; } var inputValue = input; if (!string.IsNullOrEmpty(ivr.Exit) && input.EndsWith(ivr.Exit)) { var length = ivr.Exit.Length; inputValue = input.Remove(input.Length - length, length); } if (ivr.IvrType == EIvrType.Normal) { var strategy = ivr.IvrStrategies.FirstOrDefault(d => d.Input == inputValue); answer = await ValuationStrategyAnswer(strategy, ivr, cancellationToken); _logger.LogInformation("按键响应策略: type: {type}, content: {content}, preVoice: {voice}", answer?.IvrAnswerTypeText, answer?.Content, answer?.PreVoice); return answer; } else if (ivr.IvrType == EIvrType.Score)//score { //todo 评分 //评分成功返回第一个策略,否则直接挂机 return ivr.IvrStrategies.FirstOrDefault()?.Answer ?? new IvrAnswer { IvrAnswerType = EIvrAnswerType.HangUp }; } else { throw new ArgumentOutOfRangeException("无效IvrType"); } } private async Task ValuationStrategyAnswer(IvrStrategy? strategy, Ivr ivr, CancellationToken cancellationToken) { if (strategy is null) { var errorStrategy = ivr.IvrStrategies.FirstOrDefault(d => d.IvrStrategeType == EIvrStrategeType.Error); return errorStrategy?.Answer ?? null; } if (strategy.IvrStrategeType == EIvrStrategeType.PreStep) { strategy.Answer.Content = ivr.PrevIvrNo; strategy.Answer.PreVoice = null; } if (strategy.Answer.IvrAnswerType == EIvrAnswerType.TelGroup) { var groupNo = await GetCurrentTelGroupAsync(strategy, cancellationToken); if (string.IsNullOrEmpty(groupNo)) return null; strategy.Answer.Content = groupNo; } return strategy.Answer; } public async Task AddIvrAsync(Ivr ivr, CancellationToken cancellationToken = default) { var existNo = await _ivrRepository.AnyAsync(d => d.No == ivr.No, cancellationToken); if (existNo) throw new UserFriendlyException("IVR编号已存在"); await _deviceManager.AssginConfigMenuAsync(ivr.No, ivr.Voice, ivr.Repeat.ToString(), ivr.InfoLength.ToString(), ivr.Exit, cancellationToken); var exists = await _ivrRepository.AnyAsync(d => d.IvrCategoryId == ivr.IvrCategoryId, cancellationToken); ivr.IsRoot = !exists; var id = await _ivrRepository.AddAsync(ivr, cancellationToken); _cacheIvrList.Remove(Ivr.Key); return id; } public async Task UpdateIvrAsync(Ivr ivr, CancellationToken cancellationToken = default) { await _deviceManager.AssginConfigMenuAsync(ivr.No, ivr.Voice, ivr.Repeat.ToString(), ivr.InfoLength.ToString(), ivr.Exit, cancellationToken); await _ivrRepository.UpdateWithoutStrategiesAsync(ivr, cancellationToken); _cacheIvrList.Remove(Ivr.Key); } /// /// 构建IVR关系 /// /// /// /// public async Task StructureIvrAsync(StructureIvrDto dto, CancellationToken cancellationToken) { var ivr = await _ivrRepository.GetAsync(dto.Id, cancellationToken); if (ivr == null) throw new UserFriendlyException("无效IVR编号"); //if (!ivr.IsRoot && string.IsNullOrEmpty(dto.PrevIvrNo)) // throw new UserFriendlyException("上级IVR编号不能为空"); //转下级IVR if (dto.IvrStrategies.Any(d => d.Answer.IvrAnswerType == EIvrAnswerType.Voice)) { var voiceStrategy = dto.IvrStrategies.First(d => d.Answer.IvrAnswerType == EIvrAnswerType.Voice); if (string.IsNullOrEmpty(voiceStrategy.Answer.Content)) throw new UserFriendlyException("未正确配置下一级IVR"); var nextIvr = await _ivrRepository.GetAsync(d => d.No == voiceStrategy.Answer.Content); if (nextIvr == null) throw new UserFriendlyException("无效IVRNo"); //支持插播语音以后转回自身 if (nextIvr.No != ivr.No) { nextIvr.PrevIvrNo = ivr.No; await _ivrRepository.UpdateAsync(nextIvr, cancellationToken); } } //转分机组置空content if (dto.IvrStrategies.Any(d => d.Answer.IvrAnswerType == EIvrAnswerType.TelGroup)) { var groupStrategy = dto.IvrStrategies.First(d => d.Answer.IvrAnswerType == EIvrAnswerType.TelGroup); groupStrategy.Answer.Content = null; } _mapper.Map(dto, ivr); await _ivrRepository.UpdateAsync(ivr, cancellationToken); _cacheIvrList.Remove(Ivr.Key); } /// /// 删除IVR关系(并非删除IVR) /// /// /// /// public async Task DeStructureIvrAsync(string ivrId, CancellationToken cancellationToken) { var ivr = await _ivrRepository.GetAsync(ivrId, cancellationToken); if (ivr == null) throw new UserFriendlyException("无效IVR编号"); ivr.PrevIvrNo = null; ivr.IvrStrategies = new(); await _ivrRepository.UpdateAsync(ivr, false, cancellationToken); } /// /// 替换某个IVR分组下的起始IVR(根节点) /// /// /// /// public async Task ReplaceRootAsync(string ivrId, CancellationToken cancellationToken) { var ivr = await _ivrRepository.GetAsync(ivrId, cancellationToken); if (ivr == null) throw new UserFriendlyException("无效IVR编号"); var root = await _ivrRepository.GetAsync(d => d.IvrCategoryId == ivr.IvrCategoryId && d.IsRoot, cancellationToken); if (root == null) throw new UserFriendlyException($"异常起始IVR, IvrCategoryId: {ivr.IvrCategoryId}"); root.IsRoot = false; ivr.IsRoot = true; ivr.PrevIvrNo = null; await _ivrRepository.UpdateAsync(ivr, false, cancellationToken); await _ivrRepository.UpdateAsync(root, cancellationToken); } #region private private async Task GetCurrentTelGroupAsync(IvrStrategy strategy, CancellationToken cancellationToken) { if (!strategy.GroupSorts.Any()) return null; foreach (var groupSort in strategy.GroupSorts.OrderBy(d => d.Sort)) { var group = _telCacheManager.GetTelGroup(groupSort.GroupNo); if (!group.Tels.Any()) continue; foreach (var tel in group.Tels) { var working = await _userCacheManager.IsWorkingByTelAsync(tel.No, cancellationToken); if (working) return group.No; } } return null; } private string? GetStrategyVoice(IvrStrategy strategy, Ivr ivr) { return !string.IsNullOrEmpty(strategy.Answer.PreVoice) ? $"{strategy.Answer.PreVoice}+{ivr.Voice}" : null; } #endregion #region IVR流程处理 private Ivr GetCorrectIvr() { var worktimeSettings = _worktimeCache.GetOrSet("worktimesettings", d => _worktimeOptions.Value, TimeSpan.FromDays(1)); var categoryId = GetCorrectCategory(worktimeSettings); var ivrList = _ivrCacheManager.GetIvrs(); var ivr = ivrList.First(x => x.IvrCategoryId == categoryId && x.IsRoot); return ivr; } public CorrectIvr GetCorrectIvr(string to, bool isEvaluate = false) { var worktimeSettings = _worktimeCache.GetOrSet("worktimesettings", d => _worktimeOptions.Value, TimeSpan.FromDays(1)); var setting = worktimeSettings.LineSetting.FirstOrDefault(x => x.NumNo == to); if (setting == null) { setting = worktimeSettings.LineSetting.First(x => x.NumNo == "default"); } var correct = GetCorrectCategory(setting, isEvaluate); return correct; } private CorrectIvr GetCorrectCategory(LineSetting settings, bool isEvaluate) { if (isEvaluate) { return new CorrectIvr() { eCorrectIvr = ECorrectIvr.Ivr, ReturnValue = settings.EvaluateCategory,BusyGroup = settings.BusyGroup }; } var time = TimeOnly.FromDateTime(DateTime.Now); //是否使用工作日逻辑 if (settings.UseWorkDay) { var daySetting = _daySettingCache.GetOrSet(DaySetting.Key, _daySettingRepository.QueryAsync().GetAwaiter().GetResult()); bool isWorkDay = _daySettingRepository.IsWorkDay(daySetting, DateTime.Now); if (isWorkDay) { if ((time >= TimeOnly.Parse(settings.MorningBegin) && time <= TimeOnly.Parse(settings.MorningEnd)) || (time >= TimeOnly.Parse(settings.AfterBegin) && time <= TimeOnly.Parse(settings.AfterEnd))) { if (!string.IsNullOrEmpty(settings.WorkCategory)) { return new CorrectIvr() { eCorrectIvr = ECorrectIvr.Ivr, ReturnValue = settings.WorkCategory, BusyGroup = settings.BusyGroup }; } else { return new CorrectIvr() { eCorrectIvr = ECorrectIvr.Group, ReturnValue = settings.WorkToGroup, BusyGroup = settings.BusyGroup }; } } else { if (!string.IsNullOrEmpty(settings.RestCategory)) { return new CorrectIvr() { eCorrectIvr = ECorrectIvr.Ivr, ReturnValue = settings.RestCategory, BusyGroup = settings.BusyGroup }; } else { return new CorrectIvr() { eCorrectIvr = ECorrectIvr.Group, ReturnValue = settings.RestToGroup, BusyGroup = settings.BusyGroup }; } } } else { if (!string.IsNullOrEmpty(settings.RestCategory)) { return new CorrectIvr() { eCorrectIvr = ECorrectIvr.Ivr, ReturnValue = settings.RestCategory, BusyGroup = settings.BusyGroup }; } else { return new CorrectIvr() { eCorrectIvr = ECorrectIvr.Group, ReturnValue = settings.RestToGroup, BusyGroup = settings.BusyGroup }; } } } else { if (!settings.WorkDay.Contains((int)DateTime.Now.DayOfWeek)) return new CorrectIvr() { eCorrectIvr = ECorrectIvr.Group, ReturnValue = settings.RestToGroup, BusyGroup = settings.BusyGroup }; if ((time >= TimeOnly.Parse(settings.MorningBegin) && time <= TimeOnly.Parse(settings.MorningEnd)) || (time >= TimeOnly.Parse(settings.AfterBegin) && time <= TimeOnly.Parse(settings.AfterEnd))) { if (!string.IsNullOrEmpty(settings.WorkCategory)) { return new CorrectIvr() { eCorrectIvr = ECorrectIvr.Ivr, ReturnValue = settings.WorkCategory, BusyGroup = settings.BusyGroup }; } else { return new CorrectIvr() { eCorrectIvr = ECorrectIvr.Group, ReturnValue = settings.WorkToGroup, BusyGroup = settings.BusyGroup }; } } else { if (!string.IsNullOrEmpty(settings.RestCategory)) { return new CorrectIvr() { eCorrectIvr = ECorrectIvr.Ivr, ReturnValue = settings.RestCategory, BusyGroup = settings.BusyGroup }; } else { return new CorrectIvr() { eCorrectIvr = ECorrectIvr.Group, ReturnValue = settings.RestToGroup, BusyGroup = settings.BusyGroup }; } } } } public class CorrectIvr { public string ReturnValue { get; set; } public ECorrectIvr eCorrectIvr { get; set; } public string BusyGroup { get; set; } } private string GetCorrectCategory(WorkTimeSettings settings) { if (!settings.WorkDay.Contains((int)DateTime.Now.DayOfWeek)) return settings.RestCategory; var time = TimeOnly.FromDateTime(DateTime.Now); if ((time >= TimeOnly.Parse(settings.MorningBegin) && time <= TimeOnly.Parse(settings.MorningEnd)) || (time >= TimeOnly.Parse(settings.AfterBegin) && time <= TimeOnly.Parse(settings.AfterEnd)) ) return settings.WorkCategory; return settings.RestCategory; } #endregion }