123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432 |
- 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<List<Ivr>> _cacheIvrList;
- private readonly IIvrCacheManager _ivrCacheManager;
- private readonly ITelCacheManager _telCacheManager;
- private readonly IUserCacheManager _userCacheManager;
- private readonly IMapper _mapper;
- private readonly ILogger<IvrDomainService> _logger;
- private readonly IOptionsSnapshot<WorkTimeSettings> _worktimeOptions;
- private readonly ITypedCache<WorkTimeSettings> _worktimeCache;
- private readonly ITypedCache<IReadOnlyList<DaySetting>> _daySettingCache;
- private readonly IDaySettingRepository _daySettingRepository;
- public IvrDomainService(
- IDeviceManager deviceManager,
- IIvrRepository ivrRepository,
- ITypedCache<List<Ivr>> cacheIvrList,
- IIvrCacheManager ivrCacheManager,
- ITelCacheManager telCacheManager,
- IUserCacheManager userCacheManager,
- IMapper mapper,
- ILogger<IvrDomainService> logger,
- IOptionsSnapshot<WorkTimeSettings> worktimeOptions,
- ITypedCache<WorkTimeSettings> worktimeCache,
- ITypedCache<IReadOnlyList<DaySetting>> 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;
- }
- /// <summary>
- /// 语音播放结束
- /// </summary>
- /// <param name="menuId"></param>
- /// <param name="cancellationToken"></param>
- /// <returns></returns>
- public async Task<IvrAnswer?> 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;
- }
- /// <summary>
- /// 获得按键输入
- /// </summary>
- /// <param name="menuId"></param>
- /// <param name="input"></param>
- /// <param name="cancellationToken"></param>
- /// <returns></returns>
- public async Task<IvrAnswer?> 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<IvrAnswer?> 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<string> 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);
- }
- /// <summary>
- /// 构建IVR关系
- /// </summary>
- /// <param name="dto"></param>
- /// <param name="cancellationToken"></param>
- /// <returns></returns>
- 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);
- }
- /// <summary>
- /// 删除IVR关系(并非删除IVR)
- /// </summary>
- /// <param name="ivrId"></param>
- /// <param name="cancellationToken"></param>
- /// <returns></returns>
- 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);
- }
- /// <summary>
- /// 替换某个IVR分组下的起始IVR(根节点)
- /// </summary>
- /// <param name="ivrId"></param>
- /// <param name="cancellationToken"></param>
- /// <returns></returns>
- 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<string?> 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
- }
|