1476 lines
70 KiB
C#
1476 lines
70 KiB
C#
using DG.Core;
|
||
using Exceptionless.Models;
|
||
using Hg.Complaint.Domain.Dto.ContentModel;
|
||
using Hg.Core.Entity.Complaint;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||
using Microsoft.Extensions.DependencyInjection;
|
||
using MySqlConnector;
|
||
using Oracle.ManagedDataAccess.Client;
|
||
using StackExchange.Redis;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Drawing;
|
||
using System.Linq;
|
||
using System.Net.Http;
|
||
using System.Reflection;
|
||
using System.Security.Cryptography;
|
||
using System.Text.Encodings.Web;
|
||
using System.Text.Json;
|
||
using System.Xml.Linq;
|
||
|
||
namespace Hg.Complaint.Domain
|
||
{
|
||
internal class ComplaintDomain : IComplaintDomain
|
||
{
|
||
private readonly IServiceProvider _serviceProvider;
|
||
private readonly ComplaintEventSingleton _complaintEventSingleton;
|
||
private readonly IRedisManager _redisManager;
|
||
private readonly IConfiguration _configuration;
|
||
private readonly IHttpClient _httpClient;
|
||
private readonly IMapper _mapper;
|
||
private readonly ICacheDomain _cacheDomain;
|
||
private readonly int _retryCount = 3;
|
||
private readonly SystemConfig _systemConfig;
|
||
|
||
public ComplaintDomain(
|
||
ComplaintEventSingleton complaintEventSingleton,
|
||
IRedisManager redisManager,
|
||
IConfiguration configuration,
|
||
IMapper mapper,
|
||
IHttpClient httpClient,
|
||
ICacheDomain cacheDomain,
|
||
IServiceProvider serviceProvider)
|
||
{
|
||
_systemConfig = configuration.GetSection("SystemConfig").Get<SystemConfig>();
|
||
_complaintEventSingleton = complaintEventSingleton;
|
||
_redisManager = redisManager;
|
||
_mapper = mapper;
|
||
_httpClient = httpClient;
|
||
_configuration = configuration;
|
||
_cacheDomain = cacheDomain;
|
||
_serviceProvider = serviceProvider;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分析消极消息
|
||
/// </summary>
|
||
/// <param name="dto"></param>
|
||
/// <returns></returns>
|
||
public async Task<bool> AnalyseNegativeMessage(NegativeMessageDto dto)
|
||
{
|
||
var applyComplaint = new ApplyComplaintDto
|
||
{
|
||
Appid = dto.Corpid,
|
||
Appuserid = dto.EnternalAppuserid,
|
||
Unionid = dto.ExternalUnionid,
|
||
Deptid = dto.InternalDeptid,
|
||
CrmAppid = "",
|
||
Source = ComplaintSource.企微聊天记录,
|
||
Resid = dto.ExternalResid,
|
||
Content = dto.ToJson(),
|
||
SignType = ComplaintSignType.违规关键词,
|
||
SignWay = ComplaintSignWay.系统标记,
|
||
};
|
||
return await ApplyComplaint(applyComplaint);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 申请投诉
|
||
/// </summary>
|
||
/// <param name="dto"></param>
|
||
/// <returns></returns>
|
||
public async Task<bool> ApplyComplaint(ApplyComplaintDto dto)
|
||
{
|
||
var key = CacheKeys.ComplaintMessageEnqueue;
|
||
using var scope = _serviceProvider.CreateAsyncScope();
|
||
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
|
||
Log.Information($"投诉申请日志:dto:{dto.ToJson()}");
|
||
if (dto.Channel.HasValue)
|
||
{
|
||
var deptments = await _cacheDomain.GetDeptments();
|
||
var deptment = deptments.FirstOrDefault(y => y.DeptmentCampains.Any(a => dto.Channel >= a.StartCampainId && dto.Channel <= a.EndCampainId));
|
||
if (deptment != null)
|
||
{
|
||
dto.Deptid = deptment.Id;
|
||
}
|
||
}
|
||
var complaintLog = new ComplaintLog
|
||
{
|
||
Appid = dto.Appid,
|
||
Appuserid = dto.Appuserid,
|
||
Unionid = dto.Unionid,
|
||
Deptid = dto.Deptid,
|
||
CrmAppid = dto.CrmAppid,
|
||
Source = dto.Source,
|
||
Resid = dto.Resid,
|
||
Content = dto.Content,
|
||
Ctime = DateTime.Now,
|
||
IsLast = false,
|
||
SignType = dto.SignType,
|
||
SignWay = dto.SignWay,
|
||
IsCompletion = false,
|
||
Channel = dto.Channel,
|
||
Eid = dto.Eid,
|
||
Ename = dto.Ename,
|
||
Reason = dto.Reason,
|
||
};
|
||
complaintLog = await hgActionRepository.GetRepository<ComplaintLog>().InsertAsync(complaintLog);
|
||
var messageDto = _mapper.Map<ComplaintLog, ComplaintLogMessageDto>(complaintLog);
|
||
await _redisManager.EnqueueAsync(key, messageDto);
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取队列数量
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public async Task<long> GetQueue()
|
||
{
|
||
var key = CacheKeys.ComplaintMessageEnqueue;
|
||
if (!await _redisManager.ExistsAsync(key))
|
||
{
|
||
return 0;
|
||
}
|
||
return await _redisManager.CountAsync(key);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 消费单个队列对象
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public async Task<ComplaintLogMessageDto> DequeueQueue()
|
||
{
|
||
var key = CacheKeys.ComplaintMessageEnqueue;
|
||
if (!await _redisManager.ExistsAsync(key))
|
||
{
|
||
return new ComplaintLogMessageDto();
|
||
}
|
||
return await _redisManager.DequeueAsync<ComplaintLogMessageDto>(key);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取队列信息
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public async Task<List<ComplaintLogMessageDto>> GetQueues()
|
||
{
|
||
var key = CacheKeys.ComplaintMessageEnqueue;
|
||
if (!await _redisManager.ExistsAsync(key))
|
||
{
|
||
return new List<ComplaintLogMessageDto>();
|
||
}
|
||
return await _redisManager.GetListAsync<ComplaintLogMessageDto>(key);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分析投诉日志
|
||
/// </summary>
|
||
/// <param name="messageDto"></param>
|
||
/// <returns></returns>
|
||
public async Task AnalyseComplaintLog(ComplaintLogMessageDto messageDto)
|
||
{
|
||
Log.Information($"开始分析投诉日志:dto:{messageDto.ToJson()}");
|
||
var key = CacheKeys.ComplaintMessageEnqueue;
|
||
using var scope = _serviceProvider.CreateAsyncScope();
|
||
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
|
||
var complaintData = await GetComplaintUser(messageDto);
|
||
var complaintUser = complaintData.Item1;
|
||
var complaintUserDept = complaintData.Item2;
|
||
if (complaintUser == null)
|
||
{
|
||
if (messageDto.RetryCount > _retryCount)
|
||
{
|
||
Log.Error($"用户数据获取失败,已重试超过{_retryCount}次!dto:{messageDto.ToJson()}");
|
||
return;
|
||
}
|
||
Log.Information($"用户数据获取失败,下次重试!dto:{messageDto.ToJson()}");
|
||
// 丢到新的用户队列
|
||
messageDto.RetryCount++;
|
||
await _redisManager.EnqueueAsync(key, messageDto);
|
||
return;
|
||
}
|
||
if (complaintUserDept == null)
|
||
{
|
||
if (messageDto.Deptid == 0)
|
||
{
|
||
messageDto.Udid = 0;
|
||
}
|
||
else
|
||
{
|
||
if (messageDto.RetryCount > _retryCount)
|
||
{
|
||
Log.Error($"事业部组不存,已重试超过{_retryCount}次!dto:{messageDto.ToJson()}");
|
||
return;
|
||
}
|
||
Log.Information($"事业部组不存,下次重试!dto:{messageDto.ToJson()}");
|
||
messageDto.RetryCount++;
|
||
await _redisManager.EnqueueAsync(key, messageDto);
|
||
return;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
messageDto.Udid = complaintUserDept.Id;
|
||
}
|
||
var complaintLog = _mapper.Map<ComplaintLogMessageDto, ComplaintLog>(messageDto);
|
||
complaintLog.Fid = complaintUser.Id;
|
||
complaintLog.IsLast = true;
|
||
var oldLog = await hgActionRepository.GetRepository<ComplaintLog>().Query()
|
||
.Where(x => x.Udid == complaintLog.Udid && x.IsLast == true)
|
||
.FirstOrDefaultAsync();
|
||
var transaction = await hgActionRepository.BeginTransactionAsync();
|
||
try
|
||
{
|
||
// 更新是否有订单字段
|
||
if (complaintUserDept != null && !complaintUserDept.HasOrder)
|
||
{
|
||
var hasOrder = await HasOrderByResid(complaintUser.Resid, complaintUserDept.Deptid);
|
||
if (complaintUserDept.HasOrder != hasOrder)
|
||
{
|
||
complaintUserDept.HasOrder = hasOrder;
|
||
await hgActionRepository.GetRepository<ComplaintUserDept>().UpdateAsync(complaintUserDept, x => new
|
||
{
|
||
x.HasOrder
|
||
});
|
||
}
|
||
}
|
||
if (complaintUserDept != null)
|
||
{
|
||
complaintUserDept.Status = ComplaintStatus.待跟进;
|
||
await hgActionRepository.GetRepository<ComplaintUserDept>().UpdateAsync(complaintUserDept, x => new
|
||
{
|
||
x.Status
|
||
});
|
||
_complaintEventSingleton.AddComplaintEvent(new ComplaintEventDto
|
||
{
|
||
Id = complaintUserDept.Id,
|
||
Fid = complaintUserDept.Fid,
|
||
Ctime = complaintLog.Ctime
|
||
});
|
||
}
|
||
if (oldLog != null)
|
||
{
|
||
oldLog.IsLast = false;
|
||
await hgActionRepository.GetRepository<ComplaintLog>().UpdateAsync(oldLog, x => new
|
||
{
|
||
x.IsLast
|
||
});
|
||
}
|
||
complaintLog = await hgActionRepository.GetRepository<ComplaintLog>().UpdateAsync(complaintLog, x => new
|
||
{
|
||
x.Fid,
|
||
x.Udid,
|
||
x.IsLast
|
||
});
|
||
await transaction.CommitAsync();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
await transaction.RollbackAsync();
|
||
await transaction.DisposeAsync();
|
||
Log.Error(ex, $"分析投诉日志失败!dto:{messageDto.ToJson()}");
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取投诉用户和投诉用户归属
|
||
/// </summary>
|
||
/// <param name="complaintLog"></param>
|
||
/// <returns></returns>
|
||
private async Task<(ComplaintUser?, ComplaintUserDept?)> GetComplaintUser(ComplaintLog complaintLog)
|
||
{
|
||
using var scope = _serviceProvider.CreateAsyncScope();
|
||
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
|
||
Expression<Func<ComplaintUser, bool>> expressionComplaintUser = x => 1 == 1;
|
||
if (!string.IsNullOrEmpty(complaintLog.Appid) && !string.IsNullOrEmpty(complaintLog.Appuserid))
|
||
{
|
||
expressionComplaintUser = expressionComplaintUser.And(x => x.Appid == complaintLog.Appid && x.Appuserid == complaintLog.Appuserid);
|
||
}
|
||
else if (!string.IsNullOrEmpty(complaintLog.Resid))
|
||
{
|
||
expressionComplaintUser = expressionComplaintUser.And(x => x.Resid == complaintLog.Resid);
|
||
}
|
||
else if (!string.IsNullOrEmpty(complaintLog.Unionid))
|
||
{
|
||
expressionComplaintUser = expressionComplaintUser.And(x => x.Unionid == complaintLog.Unionid);
|
||
}
|
||
|
||
var queryComplaintUser = hgActionRepository.GetRepository<ComplaintUser>().Query()
|
||
.Where(expressionComplaintUser);
|
||
|
||
var complaintUserDept = new ComplaintUserDept();
|
||
var complaintUser = await queryComplaintUser.FirstOrDefaultAsync();
|
||
|
||
// 先判断是否有已存在的投诉用户
|
||
if (complaintUser != null)
|
||
{
|
||
complaintUserDept = await GetOrCreatComplaintUserDept(complaintUser, complaintLog);
|
||
return (complaintUser, complaintUserDept);
|
||
}
|
||
else
|
||
{
|
||
// 获取用户中心的用户信息
|
||
var user = await GetUserInfo(complaintLog.Appid, complaintLog.Appuserid, complaintLog.Resid, complaintLog.Unionid);
|
||
if (user.Item2 == null || user.Item2.Uid == 0)
|
||
{
|
||
Log.Information($"用户数据获取失败!dto:{complaintLog.ToJson()}");
|
||
return (null, null);
|
||
}
|
||
|
||
complaintUser = await hgActionRepository.GetRepository<ComplaintUser>().Query()
|
||
.Where(x => x.Appid + x.Appuserid == $"{user.Item2.Appid}{user.Item2.Appuserid}")
|
||
.FirstOrDefaultAsync();
|
||
|
||
// 如果用户中心的用户信息存在,但是投诉用户不存在,则创建投诉用户
|
||
if (complaintUser == null)
|
||
{
|
||
return await CreateComplaintUser(user.Item2, complaintLog);
|
||
}
|
||
}
|
||
|
||
// 如果用户中心的用户信息存在,投诉用户也存在,则更新投诉用户信息
|
||
complaintUserDept = await GetOrCreatComplaintUserDept(complaintUser, complaintLog);
|
||
return (complaintUser, complaintUserDept);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取用户信息
|
||
/// </summary>
|
||
/// <param name="appid"></param>
|
||
/// <param name="appuserid"></param>
|
||
/// <param name="resid"></param>
|
||
/// <param name="unionid"></param>
|
||
/// <returns></returns>
|
||
private async Task<(int, UserInfo?)> GetUserInfo(string? appid, string? appuserid, string? resid, string? unionid)
|
||
{
|
||
var userInfo = new UserInfo();
|
||
var cid = 0;
|
||
if (!string.IsNullOrEmpty(appid) && !string.IsNullOrEmpty(appuserid))
|
||
{
|
||
// 从缓存中获取用户信息
|
||
var uid = await _redisManager.ExistsAsync($"{appid}_1_{appuserid}", "UserCenter") ? await _redisManager.GetAsync<int>($"{appid}_1_{appuserid}", "UserCenter") : 0;
|
||
|
||
Log.Information($"在缓存key:【{appid}_1_{appuserid}】中获取 uid:{uid}");
|
||
if (uid == 0)
|
||
{
|
||
uid = await _redisManager.ExistsAsync($"{appid}_{appuserid}", "UserCenter") ? await _redisManager.GetAsync<int>($"{appid}_{appuserid}", "UserCenter") : 0;
|
||
Log.Information($"在缓存key:【{appid}_{appuserid}】中获取 uid:{uid}");
|
||
}
|
||
if (uid > 0 && await _redisManager.ExistsAsync($"user_{uid}", "UserCenter"))
|
||
{
|
||
Log.Information($"在缓存key:【user_{uid}】");
|
||
var userInfoDto = await _redisManager.GetHashAsync<UserInfoDto>($"user_{uid}", "UserCenter");
|
||
Log.Information($"在缓存key:【user_{uid}】, 获取数据:{userInfoDto.ToJson()}");
|
||
userInfo.Appuserid = userInfoDto.Appuserid;
|
||
userInfo.Appid = userInfoDto.Appid;
|
||
userInfo.Customerid = !string.IsNullOrEmpty(userInfoDto.Customerid) ? int.Parse(userInfoDto.Customerid) : 0;
|
||
cid = userInfo.Customerid;
|
||
}
|
||
|
||
// 如果uid不等于cid, 则根据cid获取用户信息
|
||
if (userInfo.Uid != cid || string.IsNullOrEmpty(userInfo.Resid))
|
||
{
|
||
userInfo = await GetUserInfoByCid(cid);
|
||
}
|
||
if (userInfo != null && string.IsNullOrEmpty(userInfo.Resid))
|
||
{
|
||
userInfo ??= await GetUserInfoBySqlCondition("customerid", cid);
|
||
}
|
||
}
|
||
else if (!string.IsNullOrEmpty(resid))
|
||
{
|
||
// 从缓存中获取用户信息
|
||
cid = await _redisManager.ExistsAsync(resid, "UserCenter") ? await _redisManager.GetAsync<int>(resid, "UserCenter") : 0;
|
||
if (cid > 0)
|
||
{
|
||
// 如果存cid,则根据cid获取用户信息
|
||
userInfo = await GetUserInfoByCid(cid);
|
||
}
|
||
|
||
// 如果缓存中没有用户信息,则从数据库中获取
|
||
userInfo ??= await GetUserInfoBySqlCondition("Resid", resid);
|
||
}
|
||
else if (!string.IsNullOrEmpty(unionid))
|
||
{
|
||
// 如果缓存中没有用户信息,则从数据库中获取
|
||
userInfo = await GetUserInfoBySqlCondition("Unionid", unionid);
|
||
}
|
||
return (cid, userInfo);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据客户id获取客户信息
|
||
/// </summary>
|
||
/// <param name="cid"></param>
|
||
/// <returns></returns>
|
||
private async Task<UserInfo?> GetUserInfoByCid(int cid)
|
||
{
|
||
var userInfo = await _redisManager.GetHashAsync<UserInfo>($"user_{cid}", "UserCenter");
|
||
if (userInfo != null && string.IsNullOrEmpty(userInfo.Resid))
|
||
{
|
||
var result = await _redisManager.GetDatabase("UserCenter").SetMembersAsync($"cid_{cid}");
|
||
var userInfos = new List<UserInfoDto>();
|
||
|
||
#region 反序列化处理
|
||
|
||
foreach (var item in result)
|
||
{
|
||
if (!item.HasValue) continue;
|
||
try
|
||
{
|
||
var data = JsonSerializer.Deserialize<UserInfo>(item, new JsonSerializerOptions
|
||
{
|
||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||
});
|
||
if (data == null) continue;
|
||
userInfos.Add(new UserInfoDto
|
||
{
|
||
Appid = data.Appid,
|
||
Appuserid = data.Appuserid,
|
||
Uid = data.Uid.ToString()
|
||
});
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Log.Error($"反序列化失败:{e.Message}");
|
||
}
|
||
try
|
||
{
|
||
var data = JsonSerializer.Deserialize<UserInfoDto>(item, new JsonSerializerOptions
|
||
{
|
||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||
});
|
||
if (data == null) continue;
|
||
userInfos.Add(new UserInfoDto
|
||
{
|
||
Appid = data.Appid,
|
||
Appuserid = data.Appuserid,
|
||
Uid = data.Uid
|
||
});
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Log.Error($"反序列化失败:{e.Message}");
|
||
}
|
||
}
|
||
|
||
#endregion 反序列化处理
|
||
|
||
Log.Information($"在缓存key:【cid_{cid}】, 获取数据:{userInfos.ToJson()} ");
|
||
if (userInfos != null && userInfos.Any())
|
||
{
|
||
foreach (var user in userInfos)
|
||
{
|
||
Log.Information($"在缓存key:【user_{user.Uid}】");
|
||
var u = await _redisManager.GetHashAsync<UserInfo>($"user_{user.Uid}", "UserCenter");
|
||
Log.Information($"在缓存key:【user_{user.Uid}】, 获取数据:{u.ToJson()}");
|
||
userInfo.Resid = !string.IsNullOrEmpty(u.Resid) ? u.Resid : userInfo.Resid;
|
||
Log.Information($"成功获取resid:{userInfo.Resid}");
|
||
if (!string.IsNullOrEmpty(userInfo.Resid)) break;
|
||
}
|
||
}
|
||
}
|
||
return userInfo;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据sql和条件获取客户信息
|
||
/// </summary>
|
||
/// <param name="key"></param>
|
||
/// <param name="value"></param>
|
||
/// <returns></returns>
|
||
private async Task<UserInfo?> GetUserInfoBySqlCondition(string key, object value)
|
||
{
|
||
var userInfo = new UserInfo();
|
||
var sql = @"SELECT Uid, Appid, Appuserid, Customerid, Resid, Unionid FROM UserInfo ";
|
||
if (string.IsNullOrEmpty(key) || value == null) return null;
|
||
using (var scope = _serviceProvider.CreateAsyncScope())
|
||
{
|
||
var userCenterRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<UserCenterDbContext>>();
|
||
sql += $"WHERE {key} = @{key}";
|
||
var param = new List<MySqlParameter>()
|
||
{
|
||
new MySqlParameter() { ParameterName=key, MySqlDbType = MySqlDbType.VarChar, Value=value }
|
||
};
|
||
Log.Information($"sql: {sql}, param: {value}");
|
||
var userInfos = await userCenterRepository.ExecuteSqlToListAsync<UserInfo>(sql, param.ToArray());
|
||
userInfo = userInfos.FirstOrDefault(x => x.Uid == x.Customerid);
|
||
if (userInfo != null && string.IsNullOrEmpty(userInfo.Resid))
|
||
{
|
||
userInfo.Resid = userInfos.FirstOrDefault(x => !string.IsNullOrEmpty(x.Resid))?.Resid;
|
||
}
|
||
}
|
||
return userInfo;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取用户名
|
||
/// </summary>
|
||
/// <param name="resid"></param>
|
||
/// <returns></returns>
|
||
private async Task<string> GetUserName(string? resid)
|
||
{
|
||
if (string.IsNullOrEmpty(resid)) return "";
|
||
using var scope = _serviceProvider.CreateAsyncScope();
|
||
var zxdRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<ZxdDbContext>>();
|
||
var orders = await zxdRepository.GetRepository<WX_SZZYORDER>().Query()
|
||
.Where(x => x.RESID == resid)
|
||
.Distinct()
|
||
.ToListAsync();
|
||
if (orders == null) return "";
|
||
if (orders.Any(x => !string.IsNullOrEmpty(x.CNAME)))
|
||
{
|
||
return orders.First(x => !string.IsNullOrEmpty(x.CNAME)).CNAME ?? "";
|
||
}
|
||
else if (orders.Any(x => !string.IsNullOrEmpty(x.SOFTUSERNAME)))
|
||
{
|
||
var softUserName = orders.First(x => !string.IsNullOrEmpty(x.SOFTUSERNAME)).SOFTUSERNAME;
|
||
var riskinfoUrl = _systemConfig.GetRiskinfo();
|
||
|
||
var bf = "{\"uid\": \"" + softUserName + "\"}";
|
||
var hqr = BlowFish.Encode(bf);
|
||
var para = new { hqr };
|
||
var res = await _httpClient.PostAsync<RiskInfoDto>(riskinfoUrl, para);
|
||
if (res.Ret == 0 && res.Businesstype != "smallAmount")
|
||
{
|
||
return res.Name ?? "";
|
||
}
|
||
}
|
||
return "";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取或创建用户事业部关系
|
||
/// </summary>
|
||
/// <param name="complaintUser"></param>
|
||
/// <param name="complaintLog"></param>
|
||
/// <returns></returns>
|
||
private async Task<ComplaintUserDept> GetOrCreatComplaintUserDept(ComplaintUser complaintUser, ComplaintLog complaintLog)
|
||
{
|
||
using var scope = _serviceProvider.CreateAsyncScope();
|
||
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
|
||
var complaintUserDept = await hgActionRepository.GetRepository<ComplaintUserDept>().Query()
|
||
.Where(x => x.Fid == complaintUser.Id)
|
||
.Where(x => x.Deptid == complaintLog.Deptid)
|
||
.FirstOrDefaultAsync();
|
||
if (complaintUserDept == null)
|
||
{
|
||
try
|
||
{
|
||
var deptmentGroup = await _cacheDomain.GetDeptmentGroupByDeptid(complaintLog.Deptid);
|
||
if (deptmentGroup == null)
|
||
{
|
||
Log.Information($"事业部组不存!id:{complaintLog.Deptid}");
|
||
return complaintUserDept;
|
||
}
|
||
complaintUserDept = new ComplaintUserDept
|
||
{
|
||
Deptid = complaintLog.Deptid,
|
||
Ctime = DateTime.Now,
|
||
DeptGroupId = deptmentGroup.Id,
|
||
CrmAppid = complaintLog.Appid,
|
||
Fid = complaintUser.Id,
|
||
LastContent = complaintLog.Content,
|
||
LastContentId = complaintLog.Id,
|
||
Status = ComplaintStatus.待跟进
|
||
};
|
||
complaintUserDept = await hgActionRepository.GetRepository<ComplaintUserDept>().InsertAsync(complaintUserDept);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Error(ex, $"创建用户事业部关系失败!complaintUser:{complaintUser.ToJson()}, complaintLog:{complaintLog.ToJson()}");
|
||
throw;
|
||
}
|
||
}
|
||
return complaintUserDept;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建用户
|
||
/// </summary>
|
||
/// <param name="userInfo"></param>
|
||
/// <param name="complaintLog"></param>
|
||
/// <returns></returns>
|
||
private async Task<(ComplaintUser, ComplaintUserDept?)> CreateComplaintUser(UserInfo userInfo, ComplaintLog complaintLog)
|
||
{
|
||
using var scope = _serviceProvider.CreateAsyncScope();
|
||
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
|
||
var deptmentGroup = await _cacheDomain.GetDeptmentGroupByDeptid(complaintLog.Deptid);
|
||
var complaintUser = new ComplaintUser
|
||
{
|
||
Appid = userInfo.Appid,
|
||
Appuserid = userInfo.Appuserid,
|
||
Ctime = DateTime.Now,
|
||
LastType = complaintLog.Source.ToString(),
|
||
Resid = complaintLog.Resid ?? userInfo.Resid,
|
||
Unionid = userInfo.Unionid,
|
||
Uname = await GetUserName(userInfo.Resid)
|
||
};
|
||
var transaction = await hgActionRepository.BeginTransactionAsync();
|
||
try
|
||
{
|
||
complaintUser = await hgActionRepository.GetRepository<ComplaintUser>().InsertAsync(complaintUser);
|
||
if (deptmentGroup == null)
|
||
{
|
||
Log.Information($"事业部组不存!id:{complaintLog.Deptid}");
|
||
return (complaintUser, null);
|
||
}
|
||
else
|
||
{
|
||
var complaintUserDept = await hgActionRepository.GetRepository<ComplaintUserDept>().Query()
|
||
.Where(x => x.Fid == complaintUser.Id)
|
||
.Where(x => x.Deptid == complaintLog.Deptid)
|
||
.FirstOrDefaultAsync();
|
||
if (complaintUserDept == null)
|
||
{
|
||
complaintUserDept = new ComplaintUserDept
|
||
{
|
||
Deptid = complaintLog.Deptid,
|
||
Ctime = DateTime.Now,
|
||
DeptGroupId = deptmentGroup.Id,
|
||
CrmAppid = complaintLog.Appid,
|
||
Fid = complaintUser.Id,
|
||
LastContent = complaintLog.Content,
|
||
LastContentId = complaintLog.Id,
|
||
Status = ComplaintStatus.待跟进
|
||
};
|
||
if (!string.IsNullOrEmpty(userInfo.Resid))
|
||
{
|
||
complaintUserDept.HasOrder = await HasOrderByResid(complaintUser.Resid, complaintLog.Deptid);
|
||
}
|
||
complaintUserDept = await hgActionRepository.GetRepository<ComplaintUserDept>().InsertAsync(complaintUserDept);
|
||
}
|
||
else
|
||
{
|
||
complaintUserDept.LastContent = complaintLog.Content;
|
||
complaintUserDept.LastContentId = complaintLog.Id;
|
||
complaintUserDept.Status = ComplaintStatus.待跟进;
|
||
complaintUserDept.Utime = DateTime.Now;
|
||
await hgActionRepository.GetRepository<ComplaintUserDept>().UpdateAsync(complaintUserDept, x => new
|
||
{
|
||
x.LastContent,
|
||
x.LastContentId,
|
||
x.Status,
|
||
x.Utime
|
||
});
|
||
}
|
||
await transaction.CommitAsync();
|
||
return (complaintUser, complaintUserDept);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
await transaction.RollbackAsync();
|
||
await transaction.DisposeAsync();
|
||
Log.Error(ex, $"创建用户失败!dto:{complaintLog.ToJson()}");
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据resid获取用户是否有订单
|
||
/// </summary>
|
||
/// <param name="resid"></param>
|
||
/// <param name="deptid"></param>
|
||
/// <returns></returns>
|
||
private async Task<bool> HasOrderByResid(string? resid, int? deptid)
|
||
{
|
||
var deptments = await _cacheDomain.GetDeptments();
|
||
var deptment = deptments.FirstOrDefault(y => y.Id == deptid);
|
||
using var scope = _serviceProvider.CreateAsyncScope();
|
||
var zxdRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<ZxdDbContext>>();
|
||
var where = PredicateExtensionses.True<WX_SZZYORDER>();
|
||
where = where.And(x => x.RESID == resid);
|
||
var whereOr = PredicateExtensionses.False<WX_SZZYORDER>();
|
||
if (deptment != null && deptment.DeptmentCampains != null && deptment.DeptmentCampains.Any())
|
||
{
|
||
foreach (var item in deptment.DeptmentCampains)
|
||
{
|
||
whereOr = whereOr.Or(m => m.CHANNEL >= item.StartCampainId && m.CHANNEL <= item.EndCampainId);
|
||
}
|
||
}
|
||
where = where.And(whereOr);
|
||
return await zxdRepository.GetRepository<WX_SZZYORDER>().Query()
|
||
.Where(where)
|
||
.AnyAsync();
|
||
}
|
||
|
||
private async Task<T> GetData<T>(string url, string appid) where T : class, new()
|
||
{
|
||
var client = new System.Net.Http.HttpClient();
|
||
client.DefaultRequestHeaders.Add("appid", appid);
|
||
var result = await client.GetAsync(url);
|
||
var bytes = await result.Content.ReadAsByteArrayAsync();
|
||
var json = Encoding.UTF8.GetString(bytes);
|
||
var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(json);
|
||
return obj;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分页
|
||
/// </summary>
|
||
/// <param name="dto"></param>
|
||
/// <param name="appid"></param>
|
||
/// <returns></returns>
|
||
public async Task<PageResult<ComplaintDto>> GetPage(SearchComplaintDto dto, string? appid)
|
||
{
|
||
var deptmentGroups = await _cacheDomain.GetDeptmentGroups();
|
||
var deptments = await _cacheDomain.GetDeptments();
|
||
using var scope = _serviceProvider.CreateAsyncScope();
|
||
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
|
||
var zxdRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<ZxdDbContext>>();
|
||
var userQuery = hgActionRepository.GetRepository<ComplaintUser>().Query();
|
||
var logQuery = hgActionRepository.GetRepository<ComplaintLog>().Query();
|
||
var deptQuery = hgActionRepository.GetRepository<ComplaintUserDept>().Query();
|
||
var followQuery = hgActionRepository.GetRepository<ComplaintUserFollow>().Query();
|
||
var cur = zxdRepository.GetRepository<RES_CUSTOMER>().Query();
|
||
var eids = new List<int>();
|
||
var deptids = new List<int>();
|
||
|
||
#region UMID转RESID
|
||
if (!string.IsNullOrEmpty(dto.UMID) && string.IsNullOrEmpty(dto.ResId)) {
|
||
var UMIDMain = cur.FirstOrDefault(m => m.UMID == dto.UMID);
|
||
if (UMIDMain != null)
|
||
{
|
||
dto.ResId = UMIDMain.RESID;
|
||
}
|
||
else {
|
||
dto.ResId = "NULL_RESID";
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
if (!string.IsNullOrEmpty(dto.Txt_deptId) || !string.IsNullOrEmpty(dto.Txt_groupIds))
|
||
{
|
||
//接口需要appid传到header中,此方法不适用,暂时改为自定义方法
|
||
//var eidResult = await _httpClient.GetAsync<ApiResult<BusinessLineDto>>($"{_systemConfig.GetBusinessLineByDeptMentIds(dto.Txt_groupIds, dto.Txt_deptId)}", appid);
|
||
var eidResult = await GetData<ApiResult<BusinessLineDto>>($"{_systemConfig.GetBusinessLineByDeptMentIds(dto.Txt_groupIds, dto.Txt_deptId)}", appid);
|
||
if (eidResult.Code == 0)
|
||
{
|
||
if (eidResult.Data.IsLine)
|
||
{
|
||
dto.DeptId = eidResult.Data.DeptId.ToString();
|
||
}
|
||
else
|
||
{
|
||
eids = eidResult.Data.EidInfo;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Log.Error($"查询CRM组织结构接口报错, message: {eidResult.Message}");
|
||
}
|
||
}
|
||
if (dto.Txt_userId.HasValue)
|
||
{
|
||
//接口需要appid传到header中,此方法不适用,暂时改为自定义方法
|
||
//var usersResult = await _httpClient.GetAsync<ApiResult<List<CrmUserInfoDto>>>(_systemConfig.GetUserInfoByEIds(dto.Txt_userId.ToString()), appid);
|
||
var usersResult = await GetData<ApiResult<List<CrmUserInfoDto>>>(_systemConfig.GetUserInfoByEIds(dto.Txt_userId.ToString()), appid);
|
||
if (usersResult.Code == 0)
|
||
{
|
||
var userInfos = usersResult.Data;
|
||
userInfos.ForEach(x =>
|
||
{
|
||
if (x.Eid.HasValue)
|
||
eids = new List<int> { x.Eid.Value };
|
||
});
|
||
}
|
||
else
|
||
{
|
||
Log.Error($"查询CRM组织结构接口报错, message: {usersResult.Message}");
|
||
}
|
||
}
|
||
|
||
if (!string.IsNullOrEmpty(appid))
|
||
{
|
||
deptids = deptments.Where(x => x.Appid == appid && x.Id != 0).Select(x => x.Id).ToList();
|
||
}
|
||
if (!string.IsNullOrWhiteSpace(dto.DeptId))
|
||
{
|
||
deptids = dto.DeptId.Split(',').Select(n => Convert.ToInt32(n)).ToList();
|
||
}
|
||
|
||
var endTime = dto.ETime.HasValue ? dto.ETime.Value.AddDays(1) : DateTime.Now;
|
||
var query = from a in deptQuery
|
||
join b in userQuery on a.Fid equals b.Id into tempB
|
||
from b in tempB.DefaultIfEmpty()
|
||
join c in logQuery.Where(x => x.IsLast) on a.Id equals c.Udid into tempC
|
||
from c in tempC.DefaultIfEmpty()
|
||
join d in followQuery.Where(x => x.IsLast) on a.Id equals d.Udid into tempD
|
||
from d in tempD.DefaultIfEmpty()
|
||
select new ComplaintDto
|
||
{
|
||
Id = a.Id,
|
||
ResId = b.Resid,
|
||
DeptId = a.Deptid,
|
||
SignType = c.SignType,
|
||
SignWay = c.SignWay,
|
||
ContentJson = c.Content,
|
||
Source = c.Source,
|
||
Ctime = c.Ctime,
|
||
Status = a.Status,
|
||
FollowContent = d.Content,
|
||
FollowTime = d.Ctime,
|
||
Eid = b.Eid,
|
||
EName = d.Ename,
|
||
BelongEname = b.Ename,
|
||
Appid = b.Appid,
|
||
Appuserid = b.Appuserid,
|
||
HasOrder = a.HasOrder,
|
||
HasAssign = b.Eid != null,
|
||
Uname = b.Uname,
|
||
Reason = c.Reason
|
||
};
|
||
|
||
query = query.If(dto.Status.HasValue, x => x.Where(q => q.Status == dto.Status))
|
||
.If(!string.IsNullOrEmpty(dto.DeptId), x => x.Where(q => deptids.Contains(q.DeptId.Value)))
|
||
.If(!string.IsNullOrEmpty(dto.ResId), x => x.Where(q => q.ResId.Contains(dto.ResId)))
|
||
.If(dto.Source.HasValue, x => x.Where(q => q.Source == dto.Source))
|
||
.If(dto.SignWay.HasValue, x => x.Where(q => q.SignWay == dto.SignWay))
|
||
.If(dto.SignType.HasValue, x => x.Where(q => q.SignType == dto.SignType))
|
||
.If(dto.STime.HasValue, x => x.Where(q => q.Ctime >= dto.STime))
|
||
.If(dto.ETime.HasValue, x => x.Where(q => q.Ctime < endTime))
|
||
.If(!string.IsNullOrEmpty(dto.UName), x => x.Where(q => q.Uname.Contains(dto.UName)))
|
||
.If(eids.Count > 0, x => x.Where(x => eids.Contains(x.Eid.Value)))
|
||
.If(dto.HasAssign.HasValue, x => x.Where(q => q.HasAssign == dto.HasAssign))
|
||
.If(dto.HasOrder.HasValue, x => x.Where(q => q.HasOrder == dto.HasOrder))
|
||
.If(!string.IsNullOrEmpty(dto.Content), x => x.Where(q => q.ContentJson.Contains(dto.Content)))
|
||
.If(deptids != null && deptids.Any(), x => x.Where(x => deptids.Contains(x.DeptId.Value)));
|
||
var total = await query.CountAsync();
|
||
var data = await query.OrderByDescending(x => x.Ctime)
|
||
.Skip((dto.PageIndex - 1) * dto.PageSize)
|
||
.Take(dto.PageSize)
|
||
.ToListAsync();
|
||
|
||
var resids = data.Where(x => !string.IsNullOrEmpty(x.ResId)).Select(x => x.ResId).Distinct().ToList();
|
||
var mycur = cur.Where(m => resids.Contains(m.RESID)).ToList();
|
||
foreach (var item in data)
|
||
{
|
||
var deptmentGroup = deptmentGroups.FirstOrDefault(x => x.Deptments != null && x.Deptments.Any(y => y.Id == item.DeptId));
|
||
if (deptmentGroup != null && deptmentGroup.Deptments != null && deptmentGroup.Deptments.Any())
|
||
{
|
||
item.DeptGroupName = deptmentGroup.GroupName;
|
||
item.DeptName = deptmentGroup.Deptments.First(x => x.Id == item.DeptId).Title;
|
||
}
|
||
try
|
||
{
|
||
switch (item.Source)
|
||
{
|
||
case ComplaintSource.官网:
|
||
case ComplaintSource.公众号投诉:
|
||
case ComplaintSource.企微客服名片投诉:
|
||
case ComplaintSource.软件app:
|
||
{
|
||
if (!string.IsNullOrEmpty(item.ContentJson))
|
||
{
|
||
var model = JsonSerializer.Deserialize<FormContentModelDto>(item.ContentJson);
|
||
item.Content = model?.Content;
|
||
}
|
||
break;
|
||
}
|
||
|
||
case ComplaintSource.企微聊天记录:
|
||
{
|
||
if (!string.IsNullOrEmpty(item.ContentJson))
|
||
{
|
||
var model = JsonSerializer.Deserialize<NegativeContentModelDto>(item.ContentJson);
|
||
item.Content = model?.TextContent;
|
||
item.Keywords = model?.IllegalWords != null && model.IllegalWords.Any() ? string.Join(";", model?.IllegalWords) : "";
|
||
}
|
||
break;
|
||
}
|
||
case ComplaintSource.标为水军:
|
||
{
|
||
item.Content = @$"<div>{item.Uname}: {item.ResId}</div>
|
||
<div>{item.Reason?.GetDescription()}: {item.ContentJson}</div>";
|
||
break;
|
||
}
|
||
default:
|
||
item.Content = item.ContentJson;
|
||
break;
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Error(ex, "投诉内容json解析失败!");
|
||
}
|
||
item.UMID = mycur.FirstOrDefault(m => m.RESID == item.ResId)?.UMID;
|
||
}
|
||
|
||
return new PageResult<ComplaintDto>(dto.PageIndex, dto.PageSize, total, data);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重新发起队列
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public async Task<List<ComplaintLogMessageDto>> SyncQueue()
|
||
{
|
||
var key = CacheKeys.ComplaintMessageEnqueue;
|
||
using var scope = _serviceProvider.CreateAsyncScope();
|
||
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
|
||
var data = await hgActionRepository.GetRepository<ComplaintLog>().Query()
|
||
.Where(x => x.Udid == null && x.Fid == null)
|
||
.ToListAsync();
|
||
await _redisManager.RemoveAsync(key);
|
||
foreach (var item in data)
|
||
{
|
||
var messageDto = _mapper.Map<ComplaintLog, ComplaintLogMessageDto>(item);
|
||
await _redisManager.EnqueueAsync(key, messageDto);
|
||
}
|
||
return _mapper.Map<ComplaintLog, ComplaintLogMessageDto>(data);
|
||
}
|
||
|
||
public async Task<ComplaintDetailDto> GetComplaintDetail(string? appid, string? appuserid, int? deptid)
|
||
{
|
||
using var scope = _serviceProvider.CreateAsyncScope();
|
||
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
|
||
var query = from a in hgActionRepository.GetRepository<ComplaintUser>().Query()
|
||
join b in hgActionRepository.GetRepository<ComplaintUserDept>().Query() on a.Id equals b.Fid
|
||
where a.Appid == appid && a.Appuserid == appuserid && b.Deptid == deptid
|
||
select b.Id;
|
||
|
||
var userDeptid = await query.FirstOrDefaultAsync();
|
||
if (userDeptid > 0)
|
||
{
|
||
return await GetComplaintDetail(userDeptid);
|
||
}
|
||
throw new ApiException("未找到投诉记录");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据资源id和业务线获取投诉详情
|
||
/// </summary>
|
||
/// <param name="resid"></param>
|
||
/// <param name="deptid"></param>
|
||
/// <returns></returns>
|
||
public async Task<ComplaintDetailDto> GetComplaintDetail(string? resid, int? deptid)
|
||
{
|
||
using var scope = _serviceProvider.CreateAsyncScope();
|
||
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
|
||
var query = from a in hgActionRepository.GetRepository<ComplaintUser>().Query()
|
||
join b in hgActionRepository.GetRepository<ComplaintUserDept>().Query() on a.Id equals b.Fid
|
||
where a.Resid == resid && b.Deptid == deptid
|
||
select b.Id;
|
||
|
||
var userDeptid = await query.FirstOrDefaultAsync();
|
||
if (userDeptid > 0)
|
||
{
|
||
return await GetComplaintDetail(userDeptid);
|
||
}
|
||
throw new ApiException("未找到投诉记录");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取投诉详情
|
||
/// </summary>
|
||
/// <param name="id"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="ApiException"></exception>
|
||
public async Task<ComplaintDetailDto> GetComplaintDetail(int id)
|
||
{
|
||
var deptmentGroups = await _cacheDomain.GetDeptmentGroups();
|
||
using var scope = _serviceProvider.CreateAsyncScope();
|
||
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
|
||
var data = await (from a in hgActionRepository.GetRepository<ComplaintUserDept>().Query()
|
||
join b in hgActionRepository.GetRepository<ComplaintUser>().Query() on a.Fid equals b.Id
|
||
where a.Id == id
|
||
select new ComplaintDetailDto
|
||
{
|
||
Id = a.Id,
|
||
Fid = a.Fid,
|
||
Status = a.Status,
|
||
Deptid = a.Deptid,
|
||
Uname = b.Uname,
|
||
Resid = b.Resid
|
||
}).FirstOrDefaultAsync() ?? throw new ApiException("未找到投诉记录");
|
||
data.ComplaintLogDetails = await hgActionRepository.GetRepository<ComplaintLog>().Query()
|
||
.Where(x => x.Udid == data.Id && x.Fid == data.Fid)
|
||
.OrderByDescending(x => x.Ctime)
|
||
.Select(x => new ComplaintLogDetailDto
|
||
{
|
||
ContentJson = x.Content,
|
||
SignType = x.SignType,
|
||
SignWay = x.SignWay,
|
||
Source = x.Source,
|
||
Ctime = x.Ctime,
|
||
Type = x.Type,
|
||
Appid = x.Appid,
|
||
Appuserid = x.Appuserid,
|
||
Id = x.Id,
|
||
Deptid = x.Deptid,
|
||
Eid = x.Eid,
|
||
InternalNickname = x.Ename,
|
||
Reason = x.Reason
|
||
}).ToListAsync();
|
||
|
||
foreach (var item in data.ComplaintLogDetails)
|
||
{
|
||
if (item.Deptid != null)
|
||
{
|
||
var deptmentGroup = deptmentGroups.FirstOrDefault(x => x.Deptments != null && x.Deptments.Any(y => y.Id == item.Deptid));
|
||
if (deptmentGroup != null && deptmentGroup.Deptments != null && deptmentGroup.Deptments.Any())
|
||
{
|
||
item.Deptname = deptmentGroup.Deptments.First(x => x.Id == item.Deptid).Title;
|
||
}
|
||
}
|
||
switch (item.Source)
|
||
{
|
||
case ComplaintSource.官网:
|
||
case ComplaintSource.公众号投诉:
|
||
case ComplaintSource.企微客服名片投诉:
|
||
case ComplaintSource.软件app:
|
||
{
|
||
if (!string.IsNullOrEmpty(item.ContentJson))
|
||
{
|
||
var model = JsonHelper.FromJson<FormContentModelDto>(item.ContentJson);
|
||
item.Content = model?.Content;
|
||
}
|
||
break;
|
||
}
|
||
|
||
case ComplaintSource.企微聊天记录:
|
||
{
|
||
if (!string.IsNullOrEmpty(item.ContentJson))
|
||
{
|
||
var model = JsonHelper.FromJson<NegativeContentModelDto>(item.ContentJson);
|
||
item.Nickname = model?.ExternalNickname;
|
||
item.Content = model?.TextContent;
|
||
item.InternalNickname = model?.InternalNickname;
|
||
item.InternalAppuserid = model?.InternalAppuserid;
|
||
item.Msgid = model?.Msgid;
|
||
item.Keywords = model?.IllegalWords != null && model.IllegalWords.Any() ? string.Join(";", model?.IllegalWords) : "";
|
||
}
|
||
break;
|
||
}
|
||
case ComplaintSource.标为水军:
|
||
{
|
||
item.Content = @$"<div>{data.Uname}: {data.Resid}</div>
|
||
<div>{item.Reason?.GetDescription()}: {item.ContentJson}</div>";
|
||
break;
|
||
}
|
||
default:
|
||
item.Content = item.ContentJson;
|
||
break;
|
||
};
|
||
}
|
||
|
||
data.ComplaintFollowDetails = await hgActionRepository.GetRepository<ComplaintUserFollow>().Query()
|
||
.Where(x => x.Udid == data.Id && x.Fid == data.Fid)
|
||
.OrderByDescending(x => x.Ctime)
|
||
.Select(x => new ComplaintFollowDetailDto
|
||
{
|
||
Content = x.Content,
|
||
Eid = x.Eid,
|
||
Ename = x.Ename,
|
||
Ctime = x.Ctime,
|
||
Id = x.Id,
|
||
Title = x.Title,
|
||
Crmappid = x.Crmappid,
|
||
}).ToListAsync();
|
||
|
||
if (data.ComplaintFollowDetails != null && data.ComplaintFollowDetails.Any())
|
||
{
|
||
var appids = data.ComplaintFollowDetails.Where(x => !string.IsNullOrEmpty(x.Crmappid)).Select(x => x.Crmappid);
|
||
foreach (var appid in appids)
|
||
{
|
||
var eids = string.Join(",", data.ComplaintFollowDetails.Where(x => x.Crmappid == appid).Select(x => x.Eid));
|
||
var usersResult = await _httpClient.GetAsync<ApiResult<List<CrmUserInfoDto>>>(_systemConfig.GetUserInfoByEIds(eids), appid);
|
||
if (usersResult.Code == 0)
|
||
{
|
||
var userInfos = usersResult.Data;
|
||
data.ComplaintFollowDetails.Where(x => x.Crmappid == appid).ToList().ForEach(x =>
|
||
{
|
||
var userInfo = userInfos.FirstOrDefault(x => x.Eid == x.Eid);
|
||
x.Deptname = userInfo?.DeptName;
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
return data;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新投诉状态
|
||
/// </summary>
|
||
/// <param name="dto"></param>
|
||
/// <param name="appid"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="ApiException"></exception>
|
||
public async Task<bool> UpdateComplaintStatus(UpdateComplaintStatusDto dto, string? appid)
|
||
{
|
||
using var scope = _serviceProvider.CreateAsyncScope();
|
||
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
|
||
var data = await hgActionRepository.GetRepository<ComplaintUserDept>().Query()
|
||
.FirstOrDefaultAsync(x => x.Id == dto.Id) ?? throw new ApiException("未找到投诉记录");
|
||
var title = $"由【{data.Status.GetDescription()}】变更为【{dto.Status.GetDescription()}】";
|
||
if (data.Status == ComplaintStatus.已完结)
|
||
{
|
||
throw new ApiException($"投诉记录【{data.Status.GetDescription()}】,无法更新状态");
|
||
}
|
||
if (data.Status > dto.Status && dto.Status != ComplaintStatus.待跟进已超时)
|
||
{
|
||
throw new ApiException($"投诉记录【{data.Status.GetDescription()}】,无法更新状态到【{dto.Status.GetDescription()}】");
|
||
}
|
||
data.Status = dto.Status;
|
||
data.Utime = DateTime.Now;
|
||
var transaction = await hgActionRepository.BeginTransactionAsync();
|
||
try
|
||
{
|
||
var complaintLogs = await hgActionRepository.GetRepository<ComplaintLog>().Query()
|
||
.Where(x => x.Udid == data.Id && x.Fid == data.Fid && x.IsLast)
|
||
.ToListAsync();
|
||
await hgActionRepository.GetRepository<ComplaintUserDept>().UpdateAsync(data, x => new
|
||
{
|
||
x.Status,
|
||
x.Utime
|
||
});
|
||
var complaintUserFollows = await hgActionRepository.GetRepository<ComplaintUserFollow>().Query()
|
||
.Where(x => x.Udid == data.Id && x.Fid == data.Fid && x.IsLast)
|
||
.ToListAsync();
|
||
if (complaintUserFollows.Any())
|
||
{
|
||
complaintUserFollows.ForEach(x => x.IsLast = false);
|
||
await hgActionRepository.GetRepository<ComplaintUserFollow>().BatchUpdateAsync(complaintUserFollows, x => new
|
||
{
|
||
x.IsLast
|
||
});
|
||
}
|
||
var complaintUserFollow = new ComplaintUserFollow
|
||
{
|
||
Udid = data.Id,
|
||
Fid = data.Fid,
|
||
Content = dto.Content,
|
||
Ctime = DateTime.Now,
|
||
Eid = dto.Eid,
|
||
Ename = dto.Ename,
|
||
IsLast = true,
|
||
Title = title,
|
||
Crmappid = appid
|
||
};
|
||
if (complaintLogs.Any())
|
||
{
|
||
if (data.Status == ComplaintStatus.已完结)
|
||
{
|
||
complaintLogs.ForEach(x => x.IsCompletion = true);
|
||
await hgActionRepository.GetRepository<ComplaintLog>().BatchUpdateAsync(complaintLogs, x => new
|
||
{
|
||
x.IsCompletion
|
||
});
|
||
}
|
||
complaintUserFollow.LastContentId = complaintLogs.FirstOrDefault()?.Id;
|
||
}
|
||
await hgActionRepository.GetRepository<ComplaintUserFollow>().InsertAsync(complaintUserFollow);
|
||
await transaction.CommitAsync();
|
||
if (dto.Status != ComplaintStatus.待跟进)
|
||
{
|
||
_complaintEventSingleton.RemoveComplaint(data.Id);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Error(ex, $"更新投诉状态报错!dto:{dto.ToJson()}");
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取投诉状态
|
||
/// </summary>
|
||
/// <param name="udid"></param>
|
||
/// <returns></returns>
|
||
public async Task<ComplainStatusModel> GetComplaintStatus(int udid)
|
||
{
|
||
ComplainStatusModel res = new ComplainStatusModel();
|
||
using var scope = _serviceProvider.CreateAsyncScope();
|
||
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
|
||
var data = await hgActionRepository.GetRepository<ComplaintUserDept>().Query()
|
||
.FirstOrDefaultAsync(x => x.Id == udid);
|
||
if (data != null)
|
||
{
|
||
res.Id = data.Id;
|
||
res.Status = data.Status;
|
||
}
|
||
return res;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 标记客户
|
||
/// </summary>
|
||
/// <param name="dto"></param>
|
||
/// <returns></returns>
|
||
public async Task<bool> MarkCustomer(MarkCustomerDto dto)
|
||
{
|
||
try
|
||
{
|
||
ApplyComplaintDto applyComplaintDto = new ApplyComplaintDto();
|
||
if (dto.IsResid)
|
||
{
|
||
applyComplaintDto.Resid = dto.PostId;
|
||
}
|
||
else
|
||
{
|
||
applyComplaintDto.Unionid = dto.PostId;
|
||
}
|
||
applyComplaintDto.SignType = dto.Signtype;
|
||
applyComplaintDto.SignWay = ComplaintSignWay.人工标记;
|
||
applyComplaintDto.Source = dto.Type == 1 ? ComplaintSource.合规提交 : ComplaintSource.业务人员提交;
|
||
applyComplaintDto.Content = dto.Content;
|
||
applyComplaintDto.Eid = dto.Eid;
|
||
applyComplaintDto.Ename = dto.Ename;
|
||
if (!string.IsNullOrWhiteSpace(dto.DeptId))
|
||
{
|
||
var depts = dto.DeptId.Split(",").ToList();
|
||
foreach (var dept in depts)
|
||
{
|
||
applyComplaintDto.Deptid = Convert.ToInt32(dept);
|
||
await ApplyComplaint(applyComplaintDto);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
await ApplyComplaint(applyComplaintDto);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Error($"MarkCustomer{ex.Message}");
|
||
throw;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化处理过期未处理投诉
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public async Task InitOverdueComplaint()
|
||
{
|
||
var now = DateTime.Now;
|
||
using var scope = _serviceProvider.CreateAsyncScope();
|
||
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
|
||
var dueTime = now.AddHours(-DefaultHelper.DueHours);
|
||
var query = from a in hgActionRepository.GetRepository<ComplaintUserDept>().Query()
|
||
join b in hgActionRepository.GetRepository<ComplaintLog>().Query() on a.Id equals b.Udid
|
||
where a.Status == ComplaintStatus.待跟进 && b.IsLast
|
||
select new ComplaintEventDto
|
||
{
|
||
Id = a.Id,
|
||
Ctime = b.Ctime,
|
||
Fid = a.Fid,
|
||
};
|
||
var data = await query.ToListAsync();
|
||
if (data.Any())
|
||
{
|
||
// 超时未跟进处理
|
||
var overdueData = data.Where(x => x.Ctime < dueTime).ToList();
|
||
foreach (var item in overdueData)
|
||
{
|
||
Log.Information($"系统处理超时未跟进,id: {item.Id}");
|
||
var dto = new UpdateComplaintStatusDto
|
||
{
|
||
Id = item.Id,
|
||
Status = ComplaintStatus.待跟进已超时,
|
||
Content = "超时未跟进",
|
||
Ename = "系统"
|
||
};
|
||
await UpdateComplaintStatus(dto, "");
|
||
}
|
||
// 未超时未跟进处理
|
||
var dueData = data.Where(x => x.Ctime >= dueTime).ToList();
|
||
if (dueData.Any())
|
||
{
|
||
_complaintEventSingleton.InitComplaintEvents(dueData);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理过期未处理投诉
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public async Task OverdueComplaint()
|
||
{
|
||
var now = DateTime.Now;
|
||
var dueTime = now.AddHours(-DefaultHelper.DueHours);
|
||
var data = _complaintEventSingleton.GetComplaintEvents();
|
||
if (data.Any())
|
||
{
|
||
var overdueData = data.Where(x => x.Ctime < dueTime).ToList();
|
||
foreach (var item in overdueData)
|
||
{
|
||
Log.Information($"系统处理超时未跟进,id: {item.Id}");
|
||
var dto = new UpdateComplaintStatusDto
|
||
{
|
||
Id = item.Id,
|
||
Status = ComplaintStatus.待跟进已超时,
|
||
Content = "超时未跟进",
|
||
Ename = "系统"
|
||
};
|
||
await UpdateComplaintStatus(dto, "");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 刷新resid
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public async Task SyncResid()
|
||
{
|
||
//var json = "{\"Msgid\":\"12603565224196055910_1684218213018_external\",\"Msgtype\":\"text\",\"Recvtime\":null,\"Msgtime\":1684218208766,\"Corpid\":\"wwd4cd11d60db47118\",\"Corpname\":null,\"Tolist\":\"懂牛股票通\",\"Roomid\":\"\",\"Seq\":674415358,\"send_type\":2,\"text_content\":\"大盘调整从来就不是一件可怕的事,可怕的是咱们一直在做涨了去追,跌了割肉,迟迟不敢下手的事情!uD83DuDD25“大科技行情”可能即将出现起爆点!今年的半导体板块走出了一波行情, 但目前又回调到前期低点位置。 机会大于风险 - 值得我们持续关注, 要珍惜眼下黄金坑主线方向持续关注大科技与中特估一带一路, 大科技包括AI + 半导体 + 跨境支付 + 消费电子等等。 每个时期的主线机会都十分稀缺, 我们可以看到北上资金持续流入, 也预示了当前市场并不存在大的风险, 短期的回调都是为了更好的上涨!主力的每一次洗盘, 都是希望散户交出带血的筹码。 但跟对主线非常重要, 否则只剩下煎熬。 未来的市场普遍只存在局部牛市, 需要时刻紧盯主力动向才能享受到财富喜悦。( 投顾: 罗啼明; 执业编号: A0100622030001; 股市有风险, 投资需谨慎。)没有对的逻辑和正确的思维, 再好的低位也把握不住! 四月份的回调, 正是给五月份蓄能的契机。紧跟头部动向, 速度跟上咱们这轮的部署计划好吗? \",\"illegal_words \":[\"坑\"],\"external_resid \":\"257029289502501233\",\"external_unionid \":\"o5bj2wX--v3UdvKYz - L0oHY24aEw\",\"external_appuserid \":\"wmir6CCwAAfwgRKIz3oGLpK1Aus_YRvw\",\"external_nickname \":\"17709026787\",\"internal_eid \":802021,\"internal_groupid \":87,\"internal_deptid \":40,\"internal_employee_name \":\"陈景固\",\"internal_group_name \":\"客户服务二组 \",\"internal_dept_name \":\"六合投研服务中心 \",\"internal_unionid \":\"o5bj2wfT3_EPTluIfOq7q2gohlmU\",\"internal_nickname \":\"东高投研-陈经理\",\"internal_appuserid \":\"220321-134803-45\"}";
|
||
//var da = JsonHelper.FromJson<NegativeContentModelDto>(json);
|
||
//var da2 = JsonSerializer.Deserialize<NegativeContentModelDto>(json);
|
||
//var us = await GetUserInfo("wwd4cd11d60db47118", "221213-142659-33", "", "");
|
||
using var scope = _serviceProvider.CreateAsyncScope();
|
||
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
|
||
var query = from a in hgActionRepository.GetRepository<ComplaintUserDept>().Query()
|
||
join b in hgActionRepository.GetRepository<ComplaintUser>().Query() on a.Fid equals b.Id
|
||
where !string.IsNullOrEmpty(b.Resid)
|
||
select new { a, b };
|
||
|
||
var data = await query.ToListAsync();
|
||
if (data.Any())
|
||
{
|
||
foreach (var item in data)
|
||
{
|
||
try
|
||
{
|
||
// 获取用户中心的用户信息
|
||
//var user = await GetUserInfo(item.Appid, item.Appuserid, item.Resid, item.Unionid);
|
||
|
||
var hasOrder = await HasOrderByResid(item.b.Resid, item.a.Deptid);
|
||
if (hasOrder)
|
||
{
|
||
item.a.HasOrder = true;
|
||
await hgActionRepository.GetRepository<ComplaintUserDept>().UpdateAsync(item.a, x => new
|
||
{
|
||
x.HasOrder
|
||
});
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Error(ex, $"刷新resid出错!data: {item.ToJson()}");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 批量分配客户给客服
|
||
/// </summary>
|
||
/// <param name="dto"></param>
|
||
/// <param name="appid"></param>
|
||
/// <returns></returns>
|
||
public async Task<bool> BatchAssignComplaint(BatchAssignComplaintDto dto, string? appid)
|
||
{
|
||
using var scope = _serviceProvider.CreateAsyncScope();
|
||
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
|
||
var complaintUsers = await (from a in hgActionRepository.GetRepository<ComplaintUserDept>().Query()
|
||
join b in hgActionRepository.GetRepository<ComplaintUser>().Query() on a.Fid equals b.Id into abtemp
|
||
from b in abtemp.DefaultIfEmpty()
|
||
where dto.Ids.Contains(a.Id)
|
||
select b).ToListAsync();
|
||
if (complaintUsers == null || !complaintUsers.Any()) throw new ApiException("投诉用户信息不存在!");
|
||
//接口需要appid传到header中,此方法不适用,暂时改为自定义方法
|
||
//var usersResult = await _httpClient.GetAsync<ApiResult<List<CrmUserInfoDto>>>(_systemConfig.GetUserInfoByEIds(dto.Eid.ToString()), appid);
|
||
var usersResult = await GetData<ApiResult<List<CrmUserInfoDto>>>(_systemConfig.GetUserInfoByEIds(dto.Eid.ToString()), appid);
|
||
var ename = "";
|
||
if (usersResult.Code == 0)
|
||
{
|
||
var userInfos = usersResult.Data;
|
||
var userInfo = userInfos.FirstOrDefault(x => x.Eid == x.Eid);
|
||
ename = userInfo?.Uname;
|
||
}
|
||
foreach (var complaintUser in complaintUsers)
|
||
{
|
||
complaintUser.Eid = dto.Eid;
|
||
complaintUser.Ename = ename;
|
||
complaintUser.Utime = DateTime.Now;
|
||
}
|
||
await hgActionRepository.GetRepository<ComplaintUser>().BatchUpdateAsync(complaintUsers, x => new
|
||
{
|
||
x.Ename,
|
||
x.Eid,
|
||
x.Utime
|
||
});
|
||
return true;
|
||
}
|
||
|
||
public async Task Total()
|
||
{
|
||
using var scope = _serviceProvider.CreateAsyncScope();
|
||
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
|
||
var querlog = hgActionRepository.GetRepository<ComplaintLog>().Query().Select(x => new { x.Deptid, x.Fid, x.Udid, x.Source }).Distinct();
|
||
var query =
|
||
hgActionRepository.GetRepository<ComplaintUserDept>().Query()
|
||
.GroupBy(x => x.Deptid)
|
||
.Select(x => new
|
||
{
|
||
x.Key,
|
||
系统标记 = querlog.Where(a => a.Deptid == x.Key && a.Source == ComplaintSource.企微聊天记录)
|
||
.Sum(x => 1),
|
||
客户投诉 = querlog.Where(a => a.Deptid == x.Key && new List<ComplaintSource> {
|
||
ComplaintSource.官网,
|
||
ComplaintSource.PC软件,
|
||
ComplaintSource.公众号投诉,
|
||
ComplaintSource.软件app,
|
||
ComplaintSource.企微客服名片投诉
|
||
}.Contains(a.Source.Value)).Sum(x => 1),
|
||
人工标记 = querlog.Where(a => a.Deptid == x.Key && new List<ComplaintSource> {
|
||
ComplaintSource.合规提交,
|
||
ComplaintSource.业务人员提交
|
||
}.Contains(a.Source.Value)).Sum(x => 1),
|
||
总计 = x.Sum(x => 1),
|
||
待跟进 = x.Where(x => x.Status == ComplaintStatus.待跟进).Sum(x => 1),
|
||
比例1 = x.Where(x => x.Status == ComplaintStatus.待跟进).Sum(x => 1) / x.Sum(x => 1),
|
||
跟进中 = x.Where(x => x.Status == ComplaintStatus.跟进中).Sum(x => 1),
|
||
比例2 = x.Where(x => x.Status == ComplaintStatus.跟进中).Sum(x => 1) / x.Sum(x => 1),
|
||
已完结 = x.Where(x => x.Status == ComplaintStatus.已完结).Sum(x => 1),
|
||
比例3 = x.Where(x => x.Status == ComplaintStatus.已完结).Sum(x => 1) / x.Sum(x => 1),
|
||
已超时 = x.Where(x => x.Status == ComplaintStatus.待跟进已超时).Sum(x => 1),
|
||
比例4 = x.Where(x => x.Status == ComplaintStatus.待跟进已超时).Sum(x => 1) / x.Sum(x => 1),
|
||
});
|
||
var data = await query.ToListAsync();
|
||
}
|
||
|
||
public async Task<bool> MarkTrolls(MarkTrollsDto dto)
|
||
{
|
||
var deptids = dto.Deptid?.Split(',').Select(x => int.Parse(x));
|
||
if (deptids == null) return false;
|
||
foreach (var deptid in deptids)
|
||
{
|
||
var applyComplaint = new ApplyComplaintDto
|
||
{
|
||
Deptid = deptid,
|
||
Appid = dto.Appid,
|
||
Appuserid = dto.Appuserid,
|
||
Content = dto.Content,
|
||
Eid = dto.Eid,
|
||
SignWay = ComplaintSignWay.人工标记,
|
||
Source = ComplaintSource.标为水军,
|
||
Reason = dto.Reason
|
||
};
|
||
await ApplyComplaint(applyComplaint);
|
||
}
|
||
return true;
|
||
}
|
||
public async Task<int> ComplaintLabel(string resId)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(resId))
|
||
throw new ApiException("请输入客户ID");
|
||
|
||
using var scope = _serviceProvider.CreateAsyncScope();
|
||
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
|
||
var id = await hgActionRepository.GetRepository<ComplaintUser>().Query()
|
||
.Where(x => x.Resid == resId).Select(x => x.Id).FirstOrDefaultAsync();
|
||
|
||
return id;
|
||
}
|
||
}
|
||
} |