初始化

This commit is contained in:
朱小炯 2025-06-28 09:46:16 +08:00
commit 80e2934b51
8864 changed files with 2187015 additions and 0 deletions

5
.gitattributes vendored Normal file
View File

@ -0,0 +1,5 @@
# 2010
*.txt -crlf
# 2020
*.txt text eol=lf

258
.gitignore vendored Normal file
View File

@ -0,0 +1,258 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
*.DS_Store
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
logs/
[Bb]in/
[Oo]bj/
results/
# Visual Studio 2015 cache/options directory
.vs/
.vscode/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
site/wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Microsoft Azure ApplicationInsights config file
ApplicationInsights.config
# Windows Store app package directory
AppPackages/
BundleArtifacts/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
!idsrv3test.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
# FAKE - F# Make
.fake/
!tools/packages.config
tools/
# MacOS
.DS_Store
# Ocelot acceptance test config
test/Ocelot.AcceptanceTests/ocelot.json
# Read the docstates
_build/
_static/
_templates/
# JetBrains Rider
.idea/
# Test Results
*.trx

2
README.md Normal file
View File

@ -0,0 +1,2 @@
# ComplianceServer

25
code/.dockerignore Normal file
View File

@ -0,0 +1,25 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public enum LifeCycle
{
Scoped = 0x1,
Singleton = 0x2,
Transient = 0x3,
}
}

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Version>1.1.9</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public interface IScopedDependency
{
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public interface ISingletonDependency
{
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public interface ITransientDependency
{
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public class ApiException : DGException
{
public ApiException(string? message)
: base(message)
{
}
public ApiException(string? message, int code)
: base(message)
{
Data.Add("code", code);
}
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public class DGException : Exception
{
public DGException(string? message)
: base(message)
{
}
public DGException(string? message, int code)
: base(message)
{
Data.Add("code", code);
}
}
}

View File

@ -0,0 +1,20 @@

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public static class HttpExtensions
{
public static string GetCorrelationId(this HttpContext httpContext)
{
httpContext.Request.Headers.TryGetValue("Cko-Correlation-Id", out StringValues correlationId);
return correlationId.FirstOrDefault() ?? httpContext.TraceIdentifier;
}
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace DG.Core
{
public class JsonOptionsExtensions : JsonConverter<DateTime>
{
private readonly string Format;
public JsonOptionsExtensions(string format = "yyyy-MM-dd HH:mm:ss")
{
Format = format;
}
public override void Write(Utf8JsonWriter writer, DateTime date, JsonSerializerOptions options)
{
writer.WriteStringValue(date.ToString(Format));
}
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// 获取时间类型的字符串
var dt = reader.GetString();
if (!string.IsNullOrEmpty(dt))
{
//将日期与时间之间的"T"替换为一个空格,将结尾的"Z"去掉,否则会报错
dt = dt.Replace("T", " ").Replace("Z", "");
//取到秒,毫秒内容也要去掉,经过测试,不去掉会报错
if (dt.Length > 19)
{
dt = dt.Substring(0, 19);
}
return DateTime.ParseExact(dt, Format, null);
}
return DateTime.Now;
}
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public static class LinqMethodExtensions
{
/// <summary>
/// 使用自定linq扩展执行排序查询分页功能 item1: 未分页结果item2分页后的结果
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="coditionEntity"></param>
/// <returns></returns>
public static IQueryable<T> UseCoditionFind<T>(this IQueryable<T> source, bool condition, Action<IQueryable<T>> action)
{
if (condition)
{
action(source);
}
return source;
}
}
}

View File

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public static class PredicateExtensionses
{
public static Expression<Func<T, bool>> True<T>() { return f => true; }
public static Expression<Func<T, bool>> False<T>() { return f => false; }
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expLeft, Expression<Func<T, bool>> expRight)
{
var candidateExpr = Expression.Parameter(typeof(T), "candidate");
var parameterReplacer = new ParameterReplacer(candidateExpr);
var left = parameterReplacer.Replace(expLeft.Body);
var right = parameterReplacer.Replace(expRight.Body);
var body = Expression.And(left, right);
return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expLeft, Expression<Func<T, bool>> expRight)
{
var candidateExpr = Expression.Parameter(typeof(T), "candidate");
var parameterReplacer = new ParameterReplacer(candidateExpr);
var left = parameterReplacer.Replace(expLeft.Body);
var right = parameterReplacer.Replace(expRight.Body);
var body = Expression.OrElse(left, right);
return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
}
/// <summary>
/// And ((a or b )and (x or d))关系但是and后面里面的关系是Or的关系如 a.resid='' and ((a.channel>=1 and a.channel<10) or (a.channel>=50 and a.channel<60))
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expLeft"></param>
/// <param name="expRight"></param>
/// <returns></returns>
public static Expression<Func<T, bool>> AndListOr<T>(this Expression<Func<T, bool>> expLeft, Expression<Func<T, bool>>[] predicates)
{
var candidateExpr = Expression.Parameter(typeof(T), "candidate");
var parameterReplacer = new ParameterReplacer(candidateExpr);
var left = parameterReplacer.Replace(expLeft.Body);
Expression<Func<T, bool>> lambda = predicates[0];
for (int i = 1; i < predicates.Length; i++)
{
lambda = lambda.Or(predicates[i]);
}
var right = parameterReplacer.Replace(lambda.Body);
var body = Expression.And(left, right);
return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
}
/// <summary>
/// 传入条件之间为OR查询
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="predicates"></param>
/// <returns></returns>
public static IQueryable<T> WhereOR<T>(this IQueryable<T> source, params Expression<Func<T, bool>>[] predicates)
{
if (source == null) throw new ArgumentNullException("source");
if (predicates == null) throw new ArgumentNullException("predicates");
if (predicates.Length == 0) return source.Where(x => true);
if (predicates.Length == 1) return source.Where(predicates[0]);
var param = Expression.Parameter(typeof(T), "x");
Expression body = Expression.Invoke(predicates[0], param);
for (int i = 1; i < predicates.Length; i++)
{
body = Expression.OrElse(body, Expression.Invoke(predicates[i], param));
}
var lambda = Expression.Lambda<Func<T, bool>>(body, param);
return source.Where(lambda);
}
}
internal class ParameterReplacer : ExpressionVisitor
{
public ParameterReplacer(ParameterExpression paramExpr)
{
this.ParameterExpression = paramExpr;
}
public ParameterExpression ParameterExpression { get; private set; }
public Expression Replace(Expression expr)
{
return this.Visit(expr);
}
protected override Expression VisitParameter(ParameterExpression p)
{
return this.ParameterExpression;
}
}
}

View File

@ -0,0 +1,145 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public static class SystemKeyExtensions
{
/// <summary>
/// If extensions
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <param name="condition"></param>
/// <param name="action"></param>
/// <returns></returns>
public static T If<T>(this T t, bool condition, Action<T> action) where T : class
{
if (condition)
{
action(t);
}
return t;
}
/// <summary>
/// If extensions
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <param name="predicate"></param>
/// <param name="action"></param>
/// <returns></returns>
public static T If<T>(this T t, Predicate<T> predicate, Action<T> action) where T : class
{
if (t == null)
{
throw new ArgumentNullException();
}
if (predicate(t))
{
action(t);
}
return t;
}
/// <summary>
/// If extensions
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <param name="condition"></param>
/// <param name="func"></param>
/// <returns></returns>
public static T If<T>(this T t, bool condition, Func<T, T> func) where T : class => condition ? func(t) : t;
/// <summary>
/// If extensions
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <param name="predicate"></param>
/// <param name="func"></param>
/// <returns></returns>
public static T If<T>(this T t, Predicate<T> predicate, Func<T, T> func) where T : class => predicate(t) ? func(t) : t;
/// <summary>
/// If and else extensions
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <param name="condition"></param>
/// <param name="ifAction"></param>
/// <param name="elseAction"></param>
/// <returns></returns>
public static T IfAndElse<T>(this T t, bool condition, Action<T> ifAction, Action<T> elseAction) where T : class
{
if (condition)
{
ifAction(t);
}
else
{
elseAction(t);
}
return t;
}
/// <summary>
/// If and else extensions
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <param name="predicate"></param>
/// <param name="ifAction"></param>
/// <param name="elseAction"></param>
/// <returns></returns>
public static T IfAndElse<T>(this T t, Predicate<T> predicate, Action<T> ifAction, Action<T> elseAction) where T : class
{
if (t == null)
{
throw new ArgumentNullException();
}
if (predicate(t))
{
ifAction(t);
}
else
{
elseAction(t);
}
return t;
}
/// <summary>
/// If and else extensions
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <param name="condition"></param>
/// <param name="ifFunc"></param>
/// <param name="elseFunc"></param>
/// <returns></returns>
public static T IfAndElse<T>(this T t, bool condition, Func<T, T> ifFunc, Func<T, T> elseFunc) where T : class => condition ? ifFunc(t) : elseFunc(t);
/// <summary>
/// If and else extensions
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <param name="predicate"></param>
/// <param name="ifFunc"></param>
/// <param name="elseFunc"></param>
/// <returns></returns>
public static T IfAndElse<T>(this T t, Predicate<T> predicate, Func<T, T> ifFunc, Func<T, T> elseFunc) where T : class => predicate(t) ? ifFunc(t) : elseFunc(t);
}
}

759
code/DG.Core/HttpClient.cs Normal file
View File

@ -0,0 +1,759 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Unicode;
using System.Threading.Tasks;
using System.Web;
namespace DG.Core
{
public class HttpClient : IHttpClient
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<HttpClient> _logger;
private static LogLevel _logLevel = LogLevel.Debug;
public HttpClient(IHttpClientFactory httpClientFactory,
ILogger<HttpClient> logger)
{
_httpClientFactory = httpClientFactory;
_logger = logger;
}
private static JsonSerializerOptions options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNameCaseInsensitive = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
public void ChangeLogLevel(LogLevel logLevel)
{
_logLevel = logLevel;
}
private void Log(string message)
{
_logger.Log(_logLevel, message);
}
/// <summary>
/// Post Security
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url"></param>
/// <param name="data"></param>
/// <param name="clientid"></param>
/// <param name="accessKey"></param>
/// <param name="iv"></param>
/// <returns></returns>
public async Task<T> PostSecurityAsync<T>(string url, object data, string clientid, string accessKey, string iv)
{
try
{
var timeStamp = GetTimeStamp();
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNameCaseInsensitive = true
};
var param = JsonSerializer.Serialize(data, options);
var bodyJson = EncryptByAES(param, accessKey, iv);
var sign = SignData(bodyJson, accessKey);
var client = _httpClientFactory.CreateClient();
client.DefaultRequestHeaders.TryAddWithoutValidation("clientid", clientid);
client.DefaultRequestHeaders.Add("sign", sign);
var httpData = new StringContent(bodyJson, Encoding.UTF8, "application/json");
Log($"POST 请求Url:{url}, Body:{bodyJson}");
var httpResponse = await client.PostAsync($"{url}", httpData);
var stream = await httpResponse.Content.ReadAsStringAsync();
Log($"请求结果:{stream}");
var response = JsonSerializer.Deserialize<T>(stream, options);
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "POST 方法请求错误!");
throw;
}
}
private static string SignData(string ciphertext, string accessKey)
{
Encoding utf = new UTF8Encoding();
HMACMD5 hmac = new HMACMD5(utf.GetBytes(accessKey));
byte[] hashValue = hmac.ComputeHash(utf.GetBytes(ciphertext));
return Convert.ToBase64String(hashValue);
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url"></param>
/// <param name="param"></param>
/// <param name="data"></param>
/// <param name="clientid"></param>
/// <param name="accessKey"></param>
/// <returns></returns>
public async Task<T> PostSecurityAsync<T>(string url, object param, object data, string clientid, string accessKey)
{
try
{
var timeStamp = GetTimeStamp();
var paramStr = JsonSerializer.Serialize(param, options);
var bodyJson = JsonSerializer.Serialize(data, options);
var content = EncyptData(paramStr, accessKey);
var sign = SignData(content, accessKey);
var client = _httpClientFactory.CreateClient();
client.Timeout = TimeSpan.FromSeconds(30);
var httpData = new StringContent(bodyJson, Encoding.UTF8, "application/json");
url = $"{url}?content={HttpUtility.UrlEncode(content)}&sign={HttpUtility.UrlEncode(sign, Encoding.UTF8)}&clientid={clientid}";
Log($"POST 请求Url:{url}, Body:{bodyJson}");
var httpResponse = await client.PostAsync(url, httpData);
var stream = await httpResponse.Content.ReadAsStringAsync();
Log($"请求结果:{stream}");
var response = JsonSerializer.Deserialize<T>(stream, options);
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "POST 方法请求错误!");
throw;
}
}
/// <summary>
/// Post Security
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url"></param>
/// <param name="data"></param>
/// <param name="clientid"></param>
/// <param name="accessKey"></param>
/// <param name="iv"></param>
/// <returns></returns>
public async Task<string> PostSecurityAsync(string url, object data, string clientid, string accessKey, string iv)
{
try
{
var timeStamp = GetTimeStamp();
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNameCaseInsensitive = true
};
var param = JsonSerializer.Serialize(data, options);
var bodyJson = EncryptByAES(param, accessKey, iv);
var sign = SignData(bodyJson, accessKey);
var client = _httpClientFactory.CreateClient();
client.DefaultRequestHeaders.TryAddWithoutValidation("clientid", clientid);
client.DefaultRequestHeaders.Add("sign", sign);
var httpData = new StringContent(bodyJson, Encoding.UTF8, "application/json");
Log($"POST 请求Url:{url}, Body:{bodyJson}");
var httpResponse = await client.PostAsync($"{url}", httpData);
var response = await httpResponse.Content.ReadAsStringAsync();
Log($"请求结果:{response}");
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "POST 方法请求错误!");
throw;
}
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url"></param>
/// <param name="param"></param>
/// <param name="data"></param>
/// <param name="clientid"></param>
/// <param name="accessKey"></param>
/// <returns></returns>
public async Task<string> PostSecurityAsync(string url, object param, object data, string clientid, string accessKey)
{
try
{
var timeStamp = GetTimeStamp();
var paramStr = JsonSerializer.Serialize(param, options);
var bodyJson = JsonSerializer.Serialize(data, options);
var content = EncyptData(paramStr, accessKey);
var sign = SignData(content, accessKey);
var client = _httpClientFactory.CreateClient();
var httpData = new StringContent(bodyJson, Encoding.UTF8, "application/json");
url = $"{url}?content={HttpUtility.UrlEncode(content)}&sign={HttpUtility.UrlEncode(sign, Encoding.UTF8)}&clientid={clientid}";
Log($"POST 请求Url:{url}, Body:{bodyJson}");
var httpResponse = await client.PostAsync(url, httpData);
var response = await httpResponse.Content.ReadAsStringAsync();
Log($"请求结果:{response}");
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "POST 方法请求错误!");
throw;
}
}
public async Task<T> UploadFileAsync<T>(string url, string fileName, string fullName, Dictionary<string, string>? headers = null)
{
try
{
var buffer = await File.ReadAllBytesAsync(fullName);
var client = _httpClientFactory.CreateClient();
if (headers != null)
{
foreach (var header in headers)
{
client.DefaultRequestHeaders.Add(header.Key, header.Value);
}
}
ByteArrayContent fileContent = new ByteArrayContent(buffer);
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") { Name = "file", FileName = fileName };
MultipartFormDataContent content = new MultipartFormDataContent
{
fileContent
};
Log($"UploadFile 文件上传请求Url:{url}");
var httpResponse = await client.PostAsync(url, content);
var stream = await httpResponse.Content.ReadAsStringAsync();
Log($"请求结果:{stream}");
var response = JsonSerializer.Deserialize<T>(stream, options);
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "UploadFile 方法请求错误!");
throw;
}
}
public async Task<string> UploadFileAsync(string url, string fileName, string fullName, Dictionary<string, string>? headers = null)
{
try
{
var buffer = await File.ReadAllBytesAsync(fullName);
var client = _httpClientFactory.CreateClient();
if (headers != null)
{
foreach (var header in headers)
{
client.DefaultRequestHeaders.Add(header.Key, header.Value);
}
}
ByteArrayContent fileContent = new ByteArrayContent(buffer);
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") { Name = "file", FileName = fileName };
MultipartFormDataContent content = new MultipartFormDataContent
{
fileContent
};
Log($"UploadFile 文件上传请求Url:{url}");
var httpResponse = await client.PostAsync(url, content);
var response = await httpResponse.Content.ReadAsStringAsync();
Log($"请求结果:{response}");
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "UploadFile 方法请求错误!");
throw;
}
}
#region
/// <summary>
/// Post
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url"></param>
/// <param name="data"></param>
/// <param name="appId"></param>
/// <param name="appSecret"></param>
/// <param name="mediaType"></param>
/// <returns></returns>
public async Task<T> PostAsync2<T>(string url, string data, string? appId = "", string? appSecret = "", string? mediaType = "application/json")
{
// _logger.LogInformation("卧槽进来了。1111");
try
{
var client = _httpClientFactory.CreateClient();
//var options = new JsonSerializerOptions
//{
// DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
// PropertyNameCaseInsensitive = true
//};
// _logger.LogInformation("卧槽,进来了。");
var bodyJson = data;
if (!string.IsNullOrEmpty(appId))
{
client.DefaultRequestHeaders.Add("appid", appId);
}
if (!string.IsNullOrEmpty(appId) && !string.IsNullOrEmpty(appSecret))
{
var timeStamp = GetTimeStamp();
var sign = CreateSign(appId, bodyJson, appSecret, timeStamp);
var authorization = $"{appId}:{sign}";
client.DefaultRequestHeaders.TryAddWithoutValidation("authorization", authorization);
client.DefaultRequestHeaders.Add("timestamps", timeStamp);
}
var httpData = new StringContent(bodyJson, Encoding.UTF8, mediaType);
_logger.LogInformation($"POST 请求Url:{url}, Body:{bodyJson}");
var httpResponse = await client.PostAsync($"{url}", httpData);
var stream = await httpResponse.Content.ReadAsStringAsync();
_logger.LogInformation($"请求结果:{stream}");
var response = JsonSerializer.Deserialize<T>(stream, options);
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "POST 方法请求错误!");
throw;
}
}
/// <summary>
/// Post
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url"></param>
/// <param name="data"></param>
/// <param name="appId"></param>
/// <param name="appSecret"></param>
/// <param name="mediaType"></param>
/// <returns></returns>
public async Task<T> PostAsync<T>(string url, object? data = null, string? appId = "", string? appSecret = "", string? mediaType = "application/json")
{
try
{
var client = _httpClientFactory.CreateClient();
//var options = new JsonSerializerOptions
//{
// DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
// PropertyNameCaseInsensitive = true
//};
Log("卧槽,进来了。");
var bodyJson = data != null ? Newtonsoft.Json.JsonConvert.SerializeObject(data) : "";
if (!string.IsNullOrEmpty(appId))
{
client.DefaultRequestHeaders.Add("appid", appId);
}
if (!string.IsNullOrEmpty(appId) && !string.IsNullOrEmpty(appSecret))
{
var timeStamp = GetTimeStamp();
var sign = CreateSign(appId, bodyJson, appSecret, timeStamp);
var authorization = $"{appId}:{sign}";
client.DefaultRequestHeaders.TryAddWithoutValidation("authorization", authorization);
client.DefaultRequestHeaders.Add("timestamps", timeStamp);
}
var httpData = new StringContent(bodyJson, Encoding.UTF8, mediaType);
Log($"POST 请求Url:{url}, Body:{bodyJson}");
var httpResponse = await client.PostAsync($"{url}", httpData);
var stream = await httpResponse.Content.ReadAsStringAsync();
Log($"请求结果:{stream}");
var response = JsonSerializer.Deserialize<T>(stream, options);
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "POST 方法请求错误!");
throw;
}
}
public async Task<string> PostAsync(string url, object? data = null, string? appId = "", string? appSecret = "", string? mediaType = "application/json")
{
try
{
var client = _httpClientFactory.CreateClient();
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNameCaseInsensitive = true
};
var bodyJson = data != null ? JsonSerializer.Serialize(data, options) : "";
if (!string.IsNullOrEmpty(appId))
{
client.DefaultRequestHeaders.Add("appid", appId);
}
if (!string.IsNullOrEmpty(appId) && !string.IsNullOrEmpty(appSecret))
{
var timeStamp = GetTimeStamp();
var sign = CreateSign(appId, bodyJson, appSecret, timeStamp);
var authorization = $"{appId}:{sign}";
client.DefaultRequestHeaders.TryAddWithoutValidation("authorization", authorization);
client.DefaultRequestHeaders.Add("timestamps", timeStamp);
}
var httpData = new StringContent(bodyJson, Encoding.UTF8, mediaType);
Log($"POST 请求Url:{url}, Body:{bodyJson}");
var httpResponse = await client.PostAsync($"{url}", httpData);
var response = await httpResponse.Content.ReadAsStringAsync();
Log($"请求结果:{response}");
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "POST 方法请求错误!");
throw;
}
}
/// <summary>
/// Get
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url"></param>
/// <param name="appId"></param>
/// <param name="appSecret"></param>
/// <returns></returns>
public async Task<T> GetAsync<T>(string url, string appId = "", string appSecret = "", int timeout = 10000)
{
try
{
var client = _httpClientFactory.CreateClient();
client.Timeout = TimeSpan.FromMilliseconds(timeout);
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNameCaseInsensitive = true
};
if (!string.IsNullOrEmpty(appId))
{
client.DefaultRequestHeaders.Add("appid", appId);
}
if (!string.IsNullOrEmpty(appId) && !string.IsNullOrEmpty(appSecret))
{
var uri = new Uri(url);
var query = uri.Query;
var param = new Dictionary<string, object>();
if (query != null)
{
foreach (var item in query.Split('&'))
{
var sp = item.Split("=");
if (sp.Count() > 1)
{
param.Add(sp[0].Replace("?", ""), sp[1]);
}
}
}
var timeStamp = GetTimeStamp();
param = param.OrderBy(m => m.Key).ToDictionary(m => m.Key, n => n.Value);
var paramStr = JsonSerializer.Serialize(param, options);
var sign = CreateSign(appId, paramStr, appSecret, timeStamp);
var authorization = $"{appId}:{sign}";
client.DefaultRequestHeaders.TryAddWithoutValidation("authorization", authorization);
client.DefaultRequestHeaders.Add("timestamps", timeStamp);
}
Log($"GET 请求Url:{url}");
var httpResponse = await client.GetAsync($"{url}");
var stream = await httpResponse.Content.ReadAsStringAsync();
Log($"请求结果:{stream}");
var response = JsonSerializer.Deserialize<T>(stream, options);
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "GET 方法请求错误!");
throw;
}
}
/// <summary>
/// Get
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url"></param>
/// <param name="param"></param>
/// <param name="appId"></param>
/// <param name="appSecret"></param>
/// <returns></returns>
public async Task<T> GetAsync<T>(string url, Dictionary<string, object> param, string appId = "", string appSecret = "")
{
try
{
var client = _httpClientFactory.CreateClient();
client.Timeout = TimeSpan.FromSeconds(30);
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNameCaseInsensitive = true
};
var urlParam = string.Join("&", param.Select(m => m.Key + "=" + m.Value));
if (url.IndexOf('?') > -1)
{
url += urlParam;
}
else
{
url = url + "?" + urlParam;
}
if (!string.IsNullOrEmpty(appId))
{
client.DefaultRequestHeaders.Add("appid", appId);
}
if (!string.IsNullOrEmpty(appId) && !string.IsNullOrEmpty(appSecret))
{
var timeStamp = GetTimeStamp();
param = param.OrderBy(m => m.Key).ToDictionary(m => m.Key, n => n.Value);
var paramStr = JsonSerializer.Serialize(param, options);
var sign = CreateSign(appId, paramStr, appSecret, timeStamp);
var authorization = $"{appId}:{sign}";
client.DefaultRequestHeaders.TryAddWithoutValidation("authorization", authorization);
client.DefaultRequestHeaders.Add("timestamps", timeStamp);
}
Log($"GET 请求Url:{url}");
var httpResponse = await client.GetAsync($"{url}");
var stream = await httpResponse.Content.ReadAsStringAsync();
Log($"请求结果:{stream}");
var response = JsonSerializer.Deserialize<T>(stream, options);
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "GET 方法请求错误!");
throw;
}
}
/// <summary>
/// Get
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url"></param>
/// <param name="appId"></param>
/// <param name="appSecret"></param>
/// <returns></returns>
public async Task<string> GetAsync(string url, string appId = "", string appSecret = "")
{
try
{
var client = _httpClientFactory.CreateClient();
client.Timeout = TimeSpan.FromSeconds(30);
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNameCaseInsensitive = true
};
if (!string.IsNullOrEmpty(appId) && !string.IsNullOrEmpty(appSecret))
{
var uri = new Uri(url);
var query = uri.Query;
var param = new Dictionary<string, object>();
if (query != null)
{
foreach (var item in query.Split('&'))
{
var sp = item.Split("=");
if (sp.Count() > 1)
{
param.Add(sp[0].Replace("?", ""), sp[1]);
}
}
}
var timeStamp = GetTimeStamp();
param = param.OrderBy(m => m.Key).ToDictionary(m => m.Key, n => n.Value);
var paramStr = JsonSerializer.Serialize(param, options);
var sign = CreateSign(appId, paramStr, appSecret, timeStamp);
var authorization = $"{appId}:{sign}";
client.DefaultRequestHeaders.TryAddWithoutValidation("authorization", authorization);
client.DefaultRequestHeaders.Add("timestamps", timeStamp);
}
Log($"GET 请求Url:{url}");
var httpResponse = await client.GetAsync($"{url}");
var response = await httpResponse.Content.ReadAsStringAsync();
Log($"请求结果:{response}");
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "GET 方法请求错误!");
throw;
}
}
/// <summary>
/// Get
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url"></param>
/// <param name="param"></param>
/// <param name="appId"></param>
/// <param name="appSecret"></param>
/// <returns></returns>
public async Task<string> GetAsync(string url, Dictionary<string, object> param, string appId = "", string appSecret = "")
{
try
{
var client = _httpClientFactory.CreateClient();
client.Timeout = TimeSpan.FromSeconds(30);
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNameCaseInsensitive = true
};
var urlParam = string.Join("&", param.Select(m => m.Key + "=" + m.Value));
if (url.IndexOf('?') > -1)
{
url += urlParam;
}
else
{
url = url + "?" + urlParam;
}
if (!string.IsNullOrEmpty(appId))
{
client.DefaultRequestHeaders.Add("appid", appId);
}
if (!string.IsNullOrEmpty(appId) && !string.IsNullOrEmpty(appSecret))
{
var timeStamp = GetTimeStamp();
param = param.OrderBy(m => m.Key).ToDictionary(m => m.Key, n => n.Value);
var paramStr = JsonSerializer.Serialize(param, options);
var sign = CreateSign(appId, paramStr, appSecret, timeStamp);
var authorization = $"{appId}:{sign}";
client.DefaultRequestHeaders.TryAddWithoutValidation("authorization", authorization);
client.DefaultRequestHeaders.Add("timestamps", timeStamp);
}
Log($"GET 请求Url:{url}");
var httpResponse = await client.GetAsync($"{url}");
var response = await httpResponse.Content.ReadAsStringAsync();
Log($"请求结果:{response}");
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "GET 方法请求错误!");
throw;
}
}
/// <summary>
/// 生成签名
/// </summary>
/// <param name="appId"></param>
/// <param name="bodyJson"></param>
/// <param name="secret"></param>
/// <param name="timestamps"></param>
/// <returns></returns>
private static string CreateSign(string appId, string bodyJson, string secret, string timestamps)
{
var enStrList = new string[] { appId, bodyJson, secret, timestamps };
Array.Sort(enStrList, string.CompareOrdinal);
var enStr = string.Join("", enStrList);
var md = GetMd5Hash(enStr);
return md;
}
/// <summary>
/// 计算 md5
/// </summary>
/// <param name="enCode"></param>
/// <returns></returns>
private static string GetMd5Hash(string enCode)
{
string res = "";
byte[] data = Encoding.GetEncoding("utf-8").GetBytes(enCode);
MD5 md5 = MD5.Create();
byte[] bytes = md5.ComputeHash(data);
for (int i = 0; i < bytes.Length; i++)
{
res += bytes[i].ToString("x2");
}
return res;
}
/// <summary>
/// 获取时间戳
/// </summary>
/// <returns></returns>
public static string GetTimeStamp()
{
TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds).ToString();
}
/// <summary>
/// 加密
/// </summary>
/// <param name="ciphertext"></param>
/// <param name="accessKey"></param>
/// <returns></returns>
private static string EncyptData(string ciphertext, string accessKey)
{
SymmetricAlgorithm des = DES.Create();
Encoding utf = new UTF8Encoding();
byte[] key = utf.GetBytes(accessKey);
byte[] iv = { 0x75, 0x70, 0x63, 0x68, 0x69, 0x6e, 0x61, 0x31 };
ICryptoTransform encryptor = des.CreateEncryptor(key, iv);
byte[] data = utf.GetBytes(ciphertext);
byte[] encData = encryptor.TransformFinalBlock(data, 0, data.Length);
return Convert.ToBase64String(encData);
}
/// <summary>
/// 解密
/// </summary>
/// <param name="cryptograph"></param>
/// <param name="accessKey"></param>
/// <returns></returns>
private static string DecyptData(string cryptograph, string accessKey)
{
SymmetricAlgorithm des = DES.Create();
Encoding utf = new UTF8Encoding();
byte[] key = utf.GetBytes(accessKey);
byte[] iv = { 0x75, 0x70, 0x63, 0x68, 0x69, 0x6e, 0x61, 0x31 };
ICryptoTransform decryptor = des.CreateDecryptor(key, iv);
byte[] encData = Convert.FromBase64String(cryptograph);
byte[] data = decryptor.TransformFinalBlock(encData, 0, encData.Length);
return utf.GetString(data);
}
/// <summary>
/// AES加密算法
/// </summary>
/// <param name="input">明文字符串</param>
/// <returns>字符串</returns>
private static string EncryptByAES(string input, string key, string iv)
{
if (string.IsNullOrWhiteSpace(input))
{
return input;
}
Aes aes = Aes.Create();
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.FeedbackSize = 128;
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = Encoding.UTF8.GetBytes(iv);
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using MemoryStream msEncrypt = new();
using CryptoStream csEncrypt = new(msEncrypt, encryptor, CryptoStreamMode.Write);
using (StreamWriter swEncrypt = new(csEncrypt))
{
swEncrypt.Write(input);
}
byte[] bytes = msEncrypt.ToArray();
return Convert.ToBase64String(bytes);
}
#endregion
}
}

View File

@ -0,0 +1,39 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public interface IHttpClient
{
void ChangeLogLevel(LogLevel logLevel);
Task<T> PostSecurityAsync<T>(string url, object data, string clientid, string accessKey, string iv);
Task<T> PostSecurityAsync<T>(string url, object param, object data, string clientid, string accessKey);
Task<string> PostSecurityAsync(string url, object data, string clientid, string accessKey, string iv);
Task<string> PostSecurityAsync(string url, object param, object data, string clientid, string accessKey);
Task<T> PostAsync<T>(string url, object? data = null, string? appId = "", string? appSecret = "", string? mediaType = "application/json");
Task<T> PostAsync2<T>(string url, string data, string? appId = "", string? appSecret = "", string? mediaType = "application/json");
Task<string> PostAsync(string url, object? data = null, string? appId = "", string? appSecret = "", string? mediaType = "application/json");
Task<T> GetAsync<T>(string url, string appId = "", string appSecret = "", int timeout = 10000);
Task<T> GetAsync<T>(string url, Dictionary<string, object> param, string appId = "", string appSecret = "");
Task<string> GetAsync(string url, string appId = "", string appSecret = "");
Task<string> GetAsync(string url, Dictionary<string, object> param, string appId = "", string appSecret = "");
Task<T> UploadFileAsync<T>(string url, string fileName, string fullName, Dictionary<string, string>? headers = null);
Task<string> UploadFileAsync(string url, string fileName, string fullName, Dictionary<string, string>? headers = null);
}
}

View File

@ -0,0 +1,51 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public static class IMvcBuilderApiResultExtensions
{
/// <summary>
/// 启用API标准返回值模式
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public static IMvcBuilder AddApiResult(this IMvcBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
return builder.AddMvcOptions(options => {
options.Filters.Add(typeof(ApiExceptionFilterAttribute));
options.Filters.Add(typeof(ApiResultFilterAttribute));
});
}
/// <summary>
/// 启用API签名模式
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public static IMvcBuilder AddApiSignature(this IMvcBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
return builder.AddMvcOptions(options => {
options.Filters.Add(typeof(ApiSecurityAsyncFilter));
options.Filters.Add(typeof(ApiSignatureAsyncFilterAttribute));
options.Filters.Add(typeof(ApiTimeSecurityAsyncFilter));
});
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public interface IMapper
{
TTarget Map<TSource, TTarget>(TSource source);
List<TTarget> Map<TSource, TTarget>(List<TSource> source);
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
internal class Mapper
{
public static readonly Mapper Instance = new Mapper();
private Mapper()
{
}
public TTarget Map<TSource, TTarget>(TSource source) => CacheModel<TSource, TTarget>.Invoke(source);
public List<TTarget> Map<TSource, TTarget>(List<TSource> sources) => sources.AsParallel().Select(CacheModel<TSource, TTarget>.Invoke).ToList();
internal class CacheModel<TSource, TTarget>
{
private static readonly Func<TSource, TTarget> Func;
static CacheModel()
{
var parameterExpression = Expression.Parameter(typeof(TSource), "x");
var sourcePropNames = typeof(TSource).GetProperties()
.Where(x => !x.IsDefined(typeof(NotMapAttribute), true))
.Select(x => x.Name)
.ToArray();
var memberBindings = typeof(TTarget).GetProperties()
.Where(x => x.CanWrite && sourcePropNames.Any(y => y.ToUpper() == x.Name.ToUpper()))
.Select(x => Expression.Bind(typeof(TTarget).GetProperty(x.Name),
Expression.Property(parameterExpression,
typeof(TSource).GetProperty(sourcePropNames.FirstOrDefault(y => y.ToUpper() == x.Name.ToUpper())))));
Func = Expression.Lambda<Func<TSource, TTarget>>(Expression.MemberInit(Expression.New(typeof(TTarget)), memberBindings), parameterExpression).Compile();
}
public static TTarget Invoke(TSource source) => Func(source);
}
}
}

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public static class MapperExtendsions
{
/// <summary>
/// 映射到
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <typeparam name="TTarget"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
public static TTarget Map<TSource, TTarget>(this TSource source) => Mapper.Instance.Map<TSource, TTarget>(source);
/// <summary>
/// 映射到
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <typeparam name="TTarget"></typeparam>
/// <param name="sources"></param>
/// <returns></returns>
public static List<TTarget> Map<TSource, TTarget>(this List<TSource> sources) => Mapper.Instance.Map<TSource, TTarget>(sources);
/// <summary>
/// 复制到
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <typeparam name="TTarget"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
/// <remarks>因为重名了,所以对方法取别名,同 MapTo</remarks>
public static TTarget Replicate<TSource, TTarget>(this TSource source) => source.Map<TSource, TTarget>();
/// <summary>
/// 复制到
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <typeparam name="TTarget"></typeparam>
/// <param name="sources"></param>
/// <returns></returns>
/// <remarks>因为重名了,所以对方法取别名,同 MapTo</remarks>
public static List<TTarget> Replicate<TSource, TTarget>(this List<TSource> sources) => sources.Map<TSource, TTarget>();
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public class MapperManager : IMapper
{
public TTarget Map<TSource, TTarget>(TSource source) => source.Map<TSource, TTarget>();
public List<TTarget> Map<TSource, TTarget>(List<TSource> source) => source.Map<TSource, TTarget>();
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public class NotMapAttribute : Attribute
{
public NotMapAttribute()
{
}
}
}

View File

@ -0,0 +1,49 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using System.Text.Json;
namespace DG.Core
{
/// <summary>
/// 表示处理API异常的筛选器。
/// </summary>
public class ApiExceptionFilterAttribute : Attribute, IExceptionFilter
{
private readonly ILogger<ApiExceptionFilterAttribute> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="ApiExceptionFilterAttribute" /> class.
/// </summary>
/// <param name="logger">The logger</param>
public ApiExceptionFilterAttribute(ILogger<ApiExceptionFilterAttribute> logger)
{
_logger = logger;
}
/// <summary>
/// Called when [exception].
/// </summary>
/// <param name="context">The context.</param>
public void OnException(ExceptionContext context)
{
_logger.LogError(0, context.Exception, $"ip={context.HttpContext.Connection.RemoteIpAddress}, path={context.HttpContext.Request.Path}, error={JsonSerializer.Serialize(context.Exception.Message)}");
if (context.Exception.Data != null && context.Exception.Data["code"] != null)
{
var code = (int)context.Exception.Data["code"];
context.Result = new ObjectResult(ApiResult.Failed(context.Exception.Message, code));
}
else
{
context.Result = new ObjectResult(ApiResult.Failed(context.Exception.Message));
}
context.ExceptionHandled = true;
}
}
}

View File

@ -0,0 +1,37 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public class ApiResourceFilter : Attribute, IResourceFilter
{
public void OnResourceExecuted(ResourceExecutedContext context)
{
if (!context.ModelState.IsValid)
{
var errors = string.Empty;
foreach (var key in context.ModelState.Keys)
{
var modelState = context.ModelState[key];
foreach (var error in modelState.Errors)
{
errors += string.IsNullOrEmpty(errors) ? error.ErrorMessage
: $",{error.ErrorMessage}";
}
}
context.Result = new ObjectResult(ApiResult.Failed(errors));
}
// 执行完后的操作
}
public void OnResourceExecuting(ResourceExecutingContext context)
{
// 执行中的过滤器管道
}
}
}

View File

@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace DG.Core
{
public class ApiResult : IApiResult
{
/// <summary>
/// Represents an empty <see cref="IApiResult"/>.
/// </summary>
public static readonly IApiResult Empty = new ApiResult
{
Code = 0
};
/// <summary>
/// Gets or sets the status code.
/// </summary>
/// <value>The status code.</value>
[JsonPropertyName("code")]
public int Code { get; set; }
/// <summary>
/// Gets or sets the message.
/// </summary>
/// <value>The message.</value>
[JsonPropertyName("message")]
public string? Message { get; set; }
/// <summary>
/// Creates a new instance of <see cref="IApiResult{TData}"/> by the specified result.
/// </summary>
/// <typeparam name="TData">The type of the result.</typeparam>
/// <param name="data">The result.</param>
/// <returns>An instance inherited from <see cref="IApiResult{TResult}"/> interface.</returns>
public static IApiResult<TData> Succeed<TData>(TData data) => new ApiResult<TData>
{
Code = 0,
Data = data
};
/// <summary>
/// Creates a new instance of <see cref="IApiResult"/> by the specified error message.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="code">The status code</param>
/// <returns>An instance inherited from <see cref="IApiResult"/> interface.</returns>
public static IApiResult Failed(string message, int? code = null) => new ApiResult
{
Code = code ?? -1,
Message = message
};
/// <summary>
/// Creates a new instance of <see cref="IApiResult{TResult}"/> by the specified error message.
/// </summary>
/// <typeparam name="TData">The type of the result.</typeparam>
/// <param name="data">The error result.</param>
/// <param name="message">The message.</param>
/// <param name="code">The status code.</param>
/// <returns>An instance inherited from <see cref="IApiResult"/> interface.</returns>
public static IApiResult<TData> Failed<TData>(TData data, string message, int? code = null) => new ApiResult<TData>
{
Code = code ?? -1,
Message = message,
Data = data
};
/// <summary>
/// Creates a new instance of <see cref="IApiResult"/> by the specified status code and message.
/// </summary>
/// <param name="code">The status code.</param>
/// <param name="message">The message.</param>
/// <returns>An instance inherited from <see cref="IApiResult"/> interface.</returns>
public static IApiResult From(int code, string message = null) => new ApiResult
{
Code = code,
Message = message
};
/// <summary>
/// Creates a new instance of <see cref="IApiResult{TResult}"/> by the specified result.
/// </summary>
/// <typeparam name="TData">The type of the result.</typeparam>
/// <param name="data">The result.</param>
/// <param name="code">The status code.</param>
/// <param name="message">The message.</param>
/// <returns>An instance inherited from <see cref="IApiResult{TResult}"/> interface.</returns>
public static IApiResult<TData> From<TData>(TData data, int code, string message) => new ApiResult<TData>
{
Code = code,
Message = message,
Data = data
};
}
}

View File

@ -0,0 +1,69 @@
using System;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace DG.Core
{
public class ApiResultFilterAttribute : ResultFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext context)
{
if (context.Filters.Any(filterMetadata => filterMetadata.GetType() == typeof(ApiResultFilterForbidAttribute)))
{
return;
}
switch (context.Result)
{
case ObjectResult result:
{
// this include OkObjectResult, NotFoundObjectResult, BadRequestObjectResult, CreatedResult (lose Location)
var objectResult = result;
if (objectResult.Value == null)
{
context.Result = new ObjectResult(ApiResult.Empty);
}
else if (objectResult.Value is ValidationProblemDetails validationProblemDetails)
{
var errors = string.Empty;
foreach(var error in validationProblemDetails.Errors)
{
errors += string.IsNullOrEmpty(errors) ? error.Value.First()
: $",{error.Value.First()}";
}
context.Result = new ObjectResult(ApiResult.Failed(errors));
}
else if (!(objectResult.Value is IApiResult))
{
if (objectResult.DeclaredType != null)
{
var apiResult = Activator.CreateInstance(
typeof(ApiResult<>).MakeGenericType(objectResult.DeclaredType), objectResult.Value, objectResult.StatusCode);
context.Result = new ObjectResult(apiResult);
}
else
{
context.Result = objectResult;
}
}
break;
}
case EmptyResult _:
// return void or Task
context.Result = new ObjectResult(ApiResult.Empty);
break;
case ContentResult result:
context.Result = new ObjectResult(ApiResult.Succeed(result.Content));
break;
case StatusCodeResult result:
// this include OKResult, NoContentResult, UnauthorizedResult, NotFoundResult, BadRequestResult
context.Result = new ObjectResult(ApiResult.From(result.StatusCode));
break;
}
}
}
}

View File

@ -0,0 +1,15 @@
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public class ApiResultFilterForbidAttribute : ResultFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext context)
{ }
}
}

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace DG.Core
{
public class ApiResult<TData> : ApiResult, IApiResult<TData>
{
/// <summary>
/// Initializes a new instance of the <see cref="ApiResult{TResult}"/> class.
/// </summary>
public ApiResult() { }
/// <summary>
/// Initializes a new instance of the <see cref="ApiResult{TResult}" /> class.
/// </summary>
/// <param name="data">The result.</param>
/// <param name="code">The status code.</param>
public ApiResult(TData data, int? code)
{
Code = code ?? 0;
Data = data;
}
/// <summary>
/// Gets or sets the result.
/// </summary>
/// <value>The result.</value>
[JsonPropertyName("data")]
public TData Data { get; set; }
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public interface IApiResult
{
/// <summary>
/// Gets or sets the status code.
/// </summary>
/// <value>The status code.</value>
int Code { get; set; }
/// <summary>
/// Gets or sets the message.
/// </summary>
/// <value>The message.</value>
string Message { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public interface IApiResult<TData> : IApiResult
{
/// <summary>
/// Gets or sets the result.
/// </summary>
/// <value>The result.</value>
TData Data { get; set; }
}
}

View File

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public class PageResult<TData> where TData : class
{
public PageResult(int pageIndex, int pageSize, int total, IList<TData>? data)
{
PageIndex = pageIndex;
PageSize = pageSize;
Total = total;
Data = data;
TotalCount = total == 0 ? 0 : (Total / PageSize) + (Total % PageSize) > 0 ? 1 : 0;
}
/// <summary>
/// 页数
/// </summary>
public int PageIndex { get; set; }
/// <summary>
/// 分页大小
/// </summary>
public int PageSize { get; set; }
/// <summary>
/// 总数量
/// </summary>
public int Total { get; set; }
/// <summary>
/// 分页总数量
/// </summary>
public int TotalCount { get; set; }
/// <summary>
/// 数据
/// </summary>
public IList<TData>? Data { get; set; }
}
}

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public class SearchPageBase
{
/// <summary>
/// 页数
/// </summary>
public int PageIndex { get; set; }
/// <summary>
/// 分页大小
/// </summary>
public int PageSize { get; set; }
/// <summary>
/// 排序字段,支持逗号隔开
/// </summary>
public string? Sort { get; set; }
/// <summary>
/// 升降序Asc/Desc
/// </summary>
public string? Order { get; set; }
/// <summary>
/// 是否导出
/// </summary>
public bool? Export { get; set; }
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public class SelectItem
{
public SelectItem(object key, object value)
{
Key = key;
Value = value;
}
public object Key { get; set; }
public object? Value { get; set; }
}
}

View File

@ -0,0 +1,133 @@
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Text.Json.Serialization;
namespace DG.Core
{
public class ApiSecurityAsyncFilter : IAsyncAuthorizationFilter
{
private readonly IConfiguration _configuration;
private readonly ILogger<ApiSecurityAsyncFilter> _logger;
public ApiSecurityAsyncFilter(IConfiguration configuration,
ILogger<ApiSecurityAsyncFilter> logger)
{
_configuration = configuration;
_logger = logger;
}
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
if (!context.Filters.Any(filterMetadata => filterMetadata.GetType() == typeof(ApiSecurityAttribute)))
{
return;
}
var request = context.HttpContext.Request;
if (!request.Method.ToLower().Equals("post"))
{
context.Result = new ObjectResult(ApiResult.Failed("ApiSecurityAsyncFilter只支持POST方法!", 10004));
return;
}
var clientKeys = _configuration.GetSection("ClientKey").Get<List<ClientKey>>();
if (clientKeys == null || !clientKeys.Any())
{
context.Result = new ObjectResult(ApiResult.Failed("ClientKey没有配置!", 10003));
return;
}
var clientid = request.Headers["clientid"].ToString();
var sign = request.Headers["sign"].ToString();
if (string.IsNullOrEmpty(clientid) || string.IsNullOrEmpty(sign))
{
context.Result = new ObjectResult(ApiResult.Failed("请求头clientid或sign不能为空!", 10003));
return;
}
var client = clientKeys.First(x => x.Id == clientid);
request.EnableBuffering();
var stream = request.Body;
var buffer = new byte[request.ContentLength.Value];
await stream.ReadAsync(buffer);
var bodyJson = Encoding.UTF8.GetString(buffer);
stream.Position = 0;
var signData = SignData(bodyJson, client.NewAccessKey);
if (!signData.Equals(sign))
{
context.Result = new ObjectResult(ApiResult.Failed("签名不合法!", 10001));
return;
}
try
{
var contextAes = DecryptByAES(client, bodyJson);
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNameCaseInsensitive = true
};
var dataContext = Encoding.UTF8.GetBytes(contextAes);
var requestBodyStream = new MemoryStream();//创建一个流
requestBodyStream.Seek(0, SeekOrigin.Begin);//设置从0开始读取
requestBodyStream.Write(dataContext, 0, dataContext.Length);//把修改写入流中
request.Body = requestBodyStream;//把修改后的内容赋值给请求body
request.Body.Seek(0, SeekOrigin.Begin);
request.Body.Position = 0;
}
catch (Exception ex)
{
_logger.LogInformation(ex, "报错解密");
}
}
private static string SignData(string ciphertext, string accessKey)
{
Encoding utf = new UTF8Encoding();
HMACMD5 hmac = new HMACMD5(utf.GetBytes(accessKey));
byte[] hashValue = hmac.ComputeHash(utf.GetBytes(ciphertext));
return Convert.ToBase64String(hashValue);
}
private static string DecryptByAES(ClientKey client, string bodyJson)
{
return DecryptByAES(bodyJson, client.NewAccessKey, client.Vi);
}
/// <summary>
/// AES解密
/// </summary>
/// <param name="input">密文字节数组</param>
/// <returns>返回解密后的字符串</returns>
private static string DecryptByAES(string input, string key, string iv)
{
if (string.IsNullOrWhiteSpace(input))
{
return input;
}
var buffer = Convert.FromBase64String(input);
using Aes aes = Aes.Create();
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.FeedbackSize = 128;
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = Encoding.UTF8.GetBytes(iv);
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using MemoryStream msEncrypt = new(buffer);
using CryptoStream csEncrypt = new(msEncrypt, decryptor, CryptoStreamMode.Read);
using StreamReader srEncrypt = new(csEncrypt);
return srEncrypt.ReadToEnd();
}
}
}

View File

@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
/// <summary>
/// 安全API
/// </summary>
public class ApiSecurityAttribute : Attribute, IFilterMetadata
{
public ApiSecurityAttribute()
{
}
}
}

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
internal class ClientKey
{
public ClientKey(string id, string name, string accessKey, string vi, string newAccessKey)
{
Id = id;
Name = name;
AccessKey = accessKey;
Vi = vi;
NewAccessKey = newAccessKey;
}
public ClientKey()
{
}
public string Id { get; set; }
public string Name { get; set; }
public string AccessKey { get; set; }
public string Vi { get; set; }
public string NewAccessKey { get; set; }
}
}

View File

@ -0,0 +1,65 @@
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
namespace DG.Core
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddMapper(this IServiceCollection services)
=> services.AddSingleton<IMapper, MapperManager>();
/// <summary>
/// Add auto ioc services
/// </summary>
/// <param name="services"></param>
/// <param name="baseType"></param>
/// <param name="lifeCycle"></param>
/// <returns></returns>
public static IServiceCollection AddAutoIoc(this IServiceCollection services, Type baseType, LifeCycle lifeCycle)
{
if (!baseType.IsInterface)
{
throw new TypeLoadException("The status code must be an enumerated type");
}
var path = AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
var referencedAssemblies = System.IO.Directory.GetFiles(path, "*.dll").Select(Assembly.LoadFrom).ToArray();
var types = referencedAssemblies
.SelectMany(a => a.DefinedTypes)
.Select(type => type.AsType())
.Where(x => x != baseType && baseType.IsAssignableFrom(x)).ToArray();
var implementTypes = types.Where(x => x.IsClass).ToArray();
var interfaceTypes = types.Where(x => x.IsInterface).ToArray();
foreach (var implementType in implementTypes)
{
var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
if (interfaceType != null)
switch (lifeCycle)
{
case LifeCycle.Singleton:
services.AddSingleton(interfaceType, implementType);
break;
case LifeCycle.Transient:
services.AddTransient(interfaceType, implementType);
break;
case LifeCycle.Scoped:
services.AddScoped(interfaceType, implementType);
break;
default:
throw new ArgumentOutOfRangeException(nameof(lifeCycle), lifeCycle, null);
}
}
return services;
}
public static IServiceCollection AddDGHttpClient(this IServiceCollection services)
{
return services.AddHttpClient()
.AddTransient<IHttpClient, HttpClient>();
}
}
}

View File

@ -0,0 +1,138 @@
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using System.Text.Json.Serialization;
namespace DG.Core
{
public class ApiSignatureAsyncFilterAttribute : IAsyncAuthorizationFilter
{
private readonly IConfiguration _configuration;
public ApiSignatureAsyncFilterAttribute(IConfiguration configuration)
{
_configuration = configuration;
}
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
if (context.Filters.Any(filterMetadata =>
filterMetadata.GetType() == typeof(ApiSignatureFilterForbidAttribute) ||
filterMetadata.GetType() == typeof(ApiSecurityAttribute) ||
filterMetadata.GetType() == typeof(ApiTimeSecurityAttribute)))
{
return;
}
var request = context.HttpContext.Request;
var appId = _configuration.GetSection("SignConfig:AppId").Value;
var secret = _configuration.GetSection("SignConfig:Secret").Value;
if (string.IsNullOrWhiteSpace(appId) || string.IsNullOrWhiteSpace(secret))
{
context.Result = new ObjectResult(ApiResult.Failed("appId或secret没有配置!", 10003));
return;
}
var authorization = request.Headers["Authorization"].ToString();
var timestamps = request.Headers["timestamps"].ToString();
if (string.IsNullOrEmpty(authorization) || string.IsNullOrEmpty(timestamps))
{
context.Result = new ObjectResult(ApiResult.Failed("请求头authorization或timestamps不能为空!", 10003));
return;
}
string? bodyJson;
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNameCaseInsensitive = true
};
if (request.Method.ToLower().Equals("get") || request.Method.ToLower().Equals("delete"))
{
var query = request.Query;
var parames = new Dictionary<string, object>();
foreach (var item in query)
{
parames.Add(item.Key, item.Value.ToString());
}
parames = parames.OrderBy(m => m.Key).ToDictionary(m => m.Key, n => n.Value);
bodyJson = JsonSerializer.Serialize(parames, options);
}
else
{
request.EnableBuffering();
var stream = request.Body;
var buffer = new byte[request.ContentLength.Value];
await stream.ReadAsync(buffer);
bodyJson = Encoding.UTF8.GetString(buffer);
stream.Position = 0;
}
var md = CreateSign(appId, bodyJson, secret, timestamps);
if (authorization != $"{appId}:{md}")
{
context.Result = new ObjectResult(ApiResult.Failed("签名不合法!", 10001));
}
else
{
var nowTime = GetTimeStamp();
var diff = Convert.ToInt32(nowTime) - Convert.ToInt32(timestamps);
if (diff > 1800)
{
context.Result = new ObjectResult(ApiResult.Failed("签名已过期!", 10002));
}
}
}
/// <summary>
/// 生成签名
/// </summary>
/// <param name="appId"></param>
/// <param name="bodyJson"></param>
/// <param name="secret"></param>
/// <param name="timestamps"></param>
/// <returns></returns>
private static string CreateSign(string appId, string bodyJson, string secret, string timestamps)
{
var enStrList = new string[] { appId, bodyJson, secret, timestamps };
Array.Sort(enStrList, string.CompareOrdinal);
var enStr = string.Join("", enStrList);
var md = GetMd5Hash(enStr);
return md;
}
/// <summary>
/// 计算 md5
/// </summary>
/// <param name="enCode"></param>
/// <returns></returns>
private static string GetMd5Hash(string enCode)
{
string res = "";
byte[] data = Encoding.GetEncoding("utf-8").GetBytes(enCode);
MD5 md5 = MD5.Create();
byte[] bytes = md5.ComputeHash(data);
for (int i = 0; i < bytes.Length; i++)
{
res += bytes[i].ToString("x2");
}
return res;
}
/// <summary>
/// 获取时间戳
/// </summary>
/// <returns></returns>
public static string GetTimeStamp()
{
TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds).ToString();
}
}
}

View File

@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
/// <summary>
/// API屏蔽签名
/// </summary>
public class ApiSignatureFilterForbidAttribute : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
}
}
}

View File

@ -0,0 +1,171 @@
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Text.Json.Serialization;
using System.Collections.Specialized;
namespace DG.Core
{
public class ApiTimeSecurityAsyncFilter : IAsyncAuthorizationFilter
{
private readonly IConfiguration _configuration;
private readonly ILogger<ApiTimeSecurityAsyncFilter> _logger;
public ApiTimeSecurityAsyncFilter(IConfiguration configuration,
ILogger<ApiTimeSecurityAsyncFilter> logger)
{
_configuration = configuration;
_logger = logger;
}
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
if (!context.Filters.Any(filterMetadata => filterMetadata.GetType() == typeof(ApiTimeSecurityAttribute)))
{
return;
}
var request = context.HttpContext.Request;
var clientKeys = _configuration.GetSection("ClientKey").Get<List<ClientKey>>();
if (clientKeys == null || !clientKeys.Any())
{
context.Result = new ObjectResult(ApiResult.Failed("ClientKey没有配置!", 10003));
return;
}
var clientid = request.Headers["clientid"].ToString();
var sign = request.Headers["sign"].ToString();
var timestamps = request.Headers["timestamps"].ToString();
if (string.IsNullOrEmpty(clientid) || string.IsNullOrEmpty(sign) || string.IsNullOrEmpty(timestamps))
{
context.Result = new ObjectResult(ApiResult.Failed("请求头clientid或sign或timestamps不能为空!", 10003));
return;
}
var client = clientKeys.First(x => x.Id == clientid);
string? bodyJson;
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNameCaseInsensitive = true
};
if (request.Method.ToLower().Equals("get") || request.Method.ToLower().Equals("delete"))
{
var query = request.Query;
var parames = new Dictionary<string, object>();
foreach (var item in query)
{
parames.Add(item.Key, item.Value.ToString());
}
bodyJson = JsonSerializer.Serialize(parames, options);
}
else
{
request.EnableBuffering();
var stream = request.Body;
var buffer = new byte[request.ContentLength.Value];
await stream.ReadAsync(buffer);
bodyJson = Encoding.UTF8.GetString(buffer);
stream.Position = 0;
var content = JsonSerializer.Deserialize<ContentDto>(bodyJson);
bodyJson = content.Content;
}
var signData = SignData(bodyJson, client.NewAccessKey);
if (!signData.Equals(sign))
{
context.Result = new ObjectResult(ApiResult.Failed("签名不合法!", 10001));
return;
}
else
{
var nowTime = GetTimeStamp();
var diff = Convert.ToInt32(nowTime) - Convert.ToInt32(timestamps);
if (diff > 1800)
{
context.Result = new ObjectResult(ApiResult.Failed("签名已过期!", 10002));
}
}
try
{
var contextAes = DecryptByAES(client, bodyJson);
var dataContext = Encoding.UTF8.GetBytes(contextAes);
var requestBodyStream = new MemoryStream();//创建一个流
requestBodyStream.Seek(0, SeekOrigin.Begin);//设置从0开始读取
requestBodyStream.Write(dataContext, 0, dataContext.Length);//把修改写入流中
request.Body = requestBodyStream;//把修改后的内容赋值给请求body
request.Body.Seek(0, SeekOrigin.Begin);
request.Body.Position = 0;
}
catch (Exception ex)
{
_logger.LogInformation(ex, "报错解密");
}
}
private static string SignData(string ciphertext, string accessKey)
{
Encoding utf = new UTF8Encoding();
HMACMD5 hmac = new HMACMD5(utf.GetBytes(accessKey));
byte[] hashValue = hmac.ComputeHash(utf.GetBytes(ciphertext));
return Convert.ToBase64String(hashValue);
}
private static string DecryptByAES(ClientKey client, string bodyJson)
{
return DecryptByAES(bodyJson, client.NewAccessKey, client.Vi);
}
/// <summary>
/// AES解密
/// </summary>
/// <param name="input">密文字节数组</param>
/// <returns>返回解密后的字符串</returns>
private static string DecryptByAES(string input, string key, string iv)
{
if (string.IsNullOrWhiteSpace(input))
{
return input;
}
var buffer = Convert.FromBase64String(input);
using Aes aes = Aes.Create();
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.FeedbackSize = 128;
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = Encoding.UTF8.GetBytes(iv);
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using MemoryStream msEncrypt = new(buffer);
using CryptoStream csEncrypt = new(msEncrypt, decryptor, CryptoStreamMode.Read);
using StreamReader srEncrypt = new(csEncrypt);
return srEncrypt.ReadToEnd();
}
/// <summary>
/// 获取时间戳
/// </summary>
/// <returns></returns>
public static string GetTimeStamp()
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds).ToString();
}
}
public class ContentDto
{
public string Content { get; set; }
}
}

View File

@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
/// <summary>
/// 安全API
/// </summary>
public class ApiTimeSecurityAttribute : Attribute, IFilterMetadata
{
public ApiTimeSecurityAttribute()
{
}
}
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public class DecimalValidationAttribute : ValidationAttribute
{
public int? Length { get; }
private bool LengthError { get; set; }
public DecimalValidationAttribute()
{
}
public DecimalValidationAttribute(int? length)
{
Length = length;
}
/// <summary>
/// IsValid 为 false 时,提示得 error 信息
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public override string FormatErrorMessage(string name)
{
var lengthMessage = LengthError ? $",且长度不能超过{Length}" : "";
return $"{name}必须输入数字{lengthMessage},请重新输入!";
}
/// <summary>
/// 验证当前字段得结果
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public override bool IsValid(object? value)
{
if (value == null) return true;
if (decimal.TryParse(Convert.ToString(value), out decimal num))
{
if (Length != null && num.ToString().Length > Length)
{
LengthError = true;
return false;
}
return true;
}
else
{
return false;
}
}
}
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public class IntValidationAttribute : ValidationAttribute
{
public int? Length { get; }
private bool LengthError { get; set; }
public IntValidationAttribute()
{
}
public IntValidationAttribute(int? length)
{
Length = length;
}
/// <summary>
/// IsValid 为 false 时,提示得 error 信息
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public override string FormatErrorMessage(string name)
{
var lengthMessage = LengthError ? $",且长度不能超过{Length}" : "";
return $"{name}必须输入数字{lengthMessage},请重新输入!";
}
/// <summary>
/// 验证当前字段得结果
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public override bool IsValid(object? value)
{
if (value == null) return true;
if (int.TryParse(Convert.ToString(value), out int num))
{
if (Length != null && num.ToString().Length > Length)
{
LengthError = true;
return false;
}
return true;
}
else
{
return false;
}
}
}
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Core
{
public class LongValidationAttribute : ValidationAttribute
{
public int? Length { get; }
private bool LengthError { get; set; }
public LongValidationAttribute()
{
}
public LongValidationAttribute(int? length)
{
Length = length;
}
/// <summary>
/// IsValid 为 false 时,提示得 error 信息
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public override string FormatErrorMessage(string name)
{
var lengthMessage = LengthError ? $",且长度不能超过{Length}" : "";
return $"{name}必须输入数字{lengthMessage},请重新输入!";
}
/// <summary>
/// 验证当前字段得结果
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public override bool IsValid(object? value)
{
if (value == null) return true;
if (long.TryParse(Convert.ToString(value), out long num))
{
if (Length != null && num.ToString().Length > Length)
{
LengthError = true;
return false;
}
return true;
}
else
{
return false;
}
}
}
}

View File

@ -0,0 +1,96 @@
using DG.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Zxd.Core.Shared.Dto;
namespace Hg.Complaint.Domain
{
internal class CacheDomain : ICacheDomain
{
private readonly IRedisManager _redisManager;
private readonly IBaseRepository<DncmsbaseDbContext> _dncmsbaseRepository;
private readonly IHttpClient _httpClient;
private readonly SystemConfig _systemConfig;
private readonly IConfiguration _configuration;
public CacheDomain(IRedisManager redisManager,
IBaseRepository<DncmsbaseDbContext> dncmsbaseRepository,
IHttpClient httpClient,
IConfiguration configuration)
{
_configuration = configuration;
_redisManager = redisManager;
_dncmsbaseRepository = dncmsbaseRepository;
_httpClient = httpClient;
_systemConfig = configuration.GetSection("SystemConfig").Get<SystemConfig>();
}
public async Task<List<DeptmentDto>> GetDeptments()
{
var key = $"{CacheKeys.ZxdParameterList}";
if (!await _redisManager.ExistsAsync(key))
{
var response = await _httpClient.GetAsync<ApiResult<List<DeptmentDto>>>($"{_systemConfig.ZxdCoreUrl}/Api/Deptment/Depts");
if (response.Code == 0)
{
await _redisManager.SetAsync(key, response.Data, TimeSpan.FromDays(1));
return response.Data;
}
}
else
{
return await _redisManager.GetListAsync<DeptmentDto>(key);
}
return new List<DeptmentDto>();
}
public async Task<List<DeptmentGroupDto>> GetDeptmentGroups()
{
var key = CacheKeys.DeptmentGroup;
if (!await _redisManager.ExistsAsync(key))
{
var query = _dncmsbaseRepository.GetRepository<DeptmentGroup>().Query()
.Include(x => x.Deptments)
.Select(x => new DeptmentGroupDto
{
Id = x.Id,
CrmAppid = x.CrmAppid,
GroupName = x.GroupName,
Deptments = x.Deptments,
});
var data = await query.ToListAsync();
data.Add(new DeptmentGroupDto
{
Id = 0,
CrmAppid = "",
GroupName = "总部",
Deptments = new List<Deptment> {
new Deptment {
Id = 0,
Title = "总部"
}
}
});
await _redisManager.SetAsync(key, data, TimeSpan.FromDays(1));
return data;
}
else
{
var data = await _redisManager.GetListAsync<DeptmentGroupDto>(key);
return data;
}
}
public async Task<DeptmentGroupDto?> GetDeptmentGroupByDeptid(int? deptid)
{
if (deptid == null) return null;
var list = await GetDeptmentGroups();
return list.FirstOrDefault(x => x.Deptments.Any(y => y.Id == deptid)) ?? null;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain
{
/// <summary>
/// 投诉处理类
/// </summary>
public class ComplaintEventSingleton
{
private List<ComplaintEventDto> _complaintEvents = new();
/// <summary>
/// 投诉处理类
/// </summary>
public ComplaintEventSingleton()
{
}
/// <summary>
/// 获取任务队列
/// </summary>
/// <returns></returns>
public List<ComplaintEventDto> GetComplaintEvents() => _complaintEvents;
/// <summary>
/// 移除任务队列
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public List<ComplaintEventDto> RemoveComplaint(int id)
{
if (_complaintEvents.Any(x => x.Id.Equals(id)))
{
_complaintEvents.Remove(_complaintEvents.First(x => x.Id == id));
}
return _complaintEvents;
}
/// <summary>
/// 批量移除任务队列
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
public List<ComplaintEventDto> RemoveComplaints(List<int> ids)
{
foreach (var id in ids)
{
RemoveComplaint(id);
}
return _complaintEvents;
}
/// <summary>
/// 获取到期的未处理投诉
/// </summary>
/// <returns></returns>
public List<ComplaintEventDto> GetDueComplaint()
{
var now = DateTime.Now;
var dueHours = DefaultHelper.DueHours;
var data = new List<ComplaintEventDto>();
foreach (var complaintEvent in _complaintEvents)
{
if (complaintEvent.Ctime.HasValue && (now - complaintEvent.Ctime.Value).TotalHours >= dueHours)
{
data.Add(complaintEvent);
}
}
return data;
}
/// <summary>
/// 添加需要计时的投诉处理
/// </summary>
/// <param name="complaintEvent"></param>
public void AddComplaintEvent(ComplaintEventDto complaintEvent)
{
RemoveComplaint(complaintEvent.Id);
_complaintEvents.Add(complaintEvent);
}
/// <summary>
/// 初始化需要计时的投诉处理
/// </summary>
/// <param name="complaintEvents"></param>
/// <returns></returns>
public List<ComplaintEventDto> InitComplaintEvents(List<ComplaintEventDto> complaintEvents)
{
_complaintEvents = complaintEvents;
return _complaintEvents;
}
}
}

View File

@ -0,0 +1,25 @@
using System;
namespace Hg.Complaint.Domain.Config
{
internal class CacheKeys
{
public const string SsoOrganization = "cache:sso_organization";
public const string CompanyVirtual = "cache:company_virtual";
public const string ParameterList = "cache:parameter_list";
public const string CsvrCallrecordCount = "csvr_callrecord_count";
public const string ProductList = "cache:szzyproduct_list";
public const string SubProductList = "cache:szzysubproduct_list";
public const string ComplaintMessageEnqueue = "enqueue:complaint_message";
public const string DeptmentGroup = "cache:deptment_group";
public const string ZxdParameterList = "cache:zxd_parameter_list";
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Config
{
public class Consumer
{
public string? Host { get; set; }
public string? GroupId { get; set; }
public string? Topic { get; set; }
}
}

View File

@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Config
{
public class SystemConfig
{
public SystemConfig(string appid, string appSecret, string ssoUrl, string ssoOrganizationUrl, string[] clearCacheUrls, string userCenterRiaServiceUrl, string previewUrl, List<ClientKey>? clientKey)
{
Appid = appid;
AppSecret = appSecret;
SsoUrl = ssoUrl;
SsoOrganizationUrl = ssoOrganizationUrl;
ClearCacheUrls = clearCacheUrls;
UserCenterRiaServiceUrl = userCenterRiaServiceUrl;
PreviewUrl = previewUrl;
ClientKey = clientKey;
}
public SystemConfig()
{
}
public string Appid { get; set; }
public string AppSecret { get; set; }
public string SsoUrl { get; set; }
public string SsoOrganizationUrl { get; set; }
public string UserCenterRiaServiceUrl { get; set; }
public string PreviewUrl { get; set; }
public string AuditUrl { get; set; }
public string? Riskinfo { get; set; }
public string? ContractStatus { get; set; }
/// <summary>
/// 客户端密钥
/// </summary>
public List<ClientKey>? ClientKey { get; set; }
public List<Company> CompanyList { get; set; }
public string ZxdUrl { get; set; }
public string ZxdCoreUrl { get; set; }
public string CrmUrl { get; set; }
public string[] ClearCacheUrls { get; set; }
public string GetAccessKey(string id)
{
return ClientKey?.First(x => x.Id == id).AccessKey ?? "";
}
public string GetRiskinfo()
{
return UserCenterRiaServiceUrl + Riskinfo;
}
public string PostContractStatus(string content, string sign, string clientId)
{
return $"{UserCenterRiaServiceUrl}{ContractStatus}?content={content}&sign={sign}&clientId={clientId}";
}
public string? GetCompanyVideoUrl(CompanyList company)
{
return CompanyList.Where(x => x.Code == company.GetDescription()).Select(x => x.VideoUrl).FirstOrDefault();
}
public string? GetCompanyWxMessageUrl(CompanyList company)
{
return CompanyList.Where(x => x.Code == company.GetDescription()).Select(x => x.WxMessageUrl).FirstOrDefault();
}
public string? GetCompanyName(CompanyList company)
{
return CompanyList.Where(x => x.Code == company.GetDescription()).Select(x => x.Name).FirstOrDefault();
}
public string? GetCompanyCode(CompanyList company)
{
return CompanyList.Where(x => x.Code == company.GetDescription()).Select(x => x.Code).FirstOrDefault();
}
public string GetBusinessLineByDeptMentIds(string? groupIds, string? deptId)
{
return $"{CrmUrl}/Api/Customer/BusinessLineByDeptMentIds?GroupIds={groupIds}&DeptId={deptId}";
}
public string GetEidsByDeptMentIds(string? groupIds, string? deptId)
{
return $"{CrmUrl}/Api/Customer/EidsByDeptMentIds?GroupIds={groupIds}&DeptId={deptId}";
}
public string GetUserInfoByEIds(string? eids)
{
return $"{CrmUrl}/Api/Customer/GetUserInfoByEIds?eids={eids}";
}
}
public class ClientKey
{
public string Id { get; set; }
public string Name { get; set; }
public string AccessKey { get; set; }
}
public class Company
{
public string? Code { get; set; }
public string? Name { get; set; }
public string? VideoUrl { get; set; }
public string? WxMessageUrl { get; set; }
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain
{
/// <summary>
/// 默认
/// </summary>
public static class DefaultHelper
{
/// <summary>
/// 到期时间(小时)
/// </summary>
public static int DueHours = 24;
}
}

View File

@ -0,0 +1,95 @@

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
/// <summary>
/// 投诉申请
/// </summary>
public class ApplyComplaintDto
{
/// <summary>
/// 平台id
/// </summary>
public string? Appid { get; set; }
/// <summary>
/// 用户账号比如软件平台username,服务号openid
/// </summary>
public string? Appuserid { get; set; }
/// <summary>
/// 联合ID
/// </summary>
public string? Unionid { get; set; }
/// <summary>
/// 事业线ID
/// </summary>
public int? Deptid { get; set; }
/// <summary>
/// 坐席的appid
/// </summary>
public string? CrmAppid { get; set; }
/// <summary>
/// 投诉来源
/// 1、官网
/// 2、公众号投诉
/// 3、企微客服名片投诉
/// 4、PC软件
/// </summary>
public ComplaintSource? Source { get; set; }
/// <summary>
/// 客户号码ID
/// </summary>
public string? Resid { get; set; }
/// <summary>
/// 投诉内容(json形式)
/// </summary>
public string? Content { get; set; }
/// <summary>
/// 手机号
/// </summary>
public string? Mobile { get; set; }
/// <summary>
/// 投诉类型
/// </summary>
public ComplaintSignType? SignType { get; set; }
/// <summary>
/// 投诉方式
/// </summary>
public ComplaintSignWay? SignWay { get; set; }
/// <summary>
/// 渠道号
/// </summary>
public int? Channel { get; set; }
/// <summary>
/// 工号
/// </summary>
public int? Eid { get; set; }
/// <summary>
/// 员工姓名
/// </summary>
public string? Ename { get; set; }
/// <summary>
/// 水军类型
/// </summary>
public ComplaintSignReason? Reason { get; set; }
}
}

View File

@ -0,0 +1,16 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class BatchAssignComplaintDto
{
public List<int> Ids { get; set; }
public int Eid { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class BusinessLineDto
{
public bool IsLine { get; set; }
public int DeptId { get; set; }
public List<int> EidInfo { get; set; }
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class ComplaintDetailDto
{
public int Id { get; set; }
public int Fid { get; set; }
public int? Deptid { get; set; }
public string? Resid { get; set; }
public string Uname { get; set; }
/// <summary>
/// 状态
/// </summary>
public ComplaintStatus Status { get; set; }
/// <summary>
/// 状态
/// </summary>
public string StatusStr { get
{
return Status.GetDescription();
}
}
/// <summary>
/// 风险标记
/// </summary>
public List<ComplaintLogDetailDto>? ComplaintLogDetails { get; set; }
/// <summary>
/// 跟进情况
/// </summary>
public List<ComplaintFollowDetailDto>? ComplaintFollowDetails { get; set; }
}
}

View File

@ -0,0 +1,188 @@
using Hg.Complaint.Domain.Dto.ContentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class ComplaintDto
{
public int Id { get; set; }
/// <summary>
/// 事业部id
/// </summary>
public int? DeptId { get; set; }
/// <summary>
/// 事业部
/// </summary>
public string? DeptName { get; set; }
/// <summary>
/// 事业部组
/// </summary>
public string? DeptGroupName { get; set; }
/// <summary>
/// 客户id
/// </summary>
public string? ResId { get; set; }
/// <summary>
/// 客户id
/// </summary>
public string? UMID { get; set; }
/// <summary>
/// 客户姓名
/// </summary>
public string? Uname { get; set; }
/// <summary>
/// 归属员工
/// </summary>
public string? BelongEname { get; set; }
/// <summary>
/// 标记类型
/// </summary>
[JsonIgnore]
public ComplaintSignType? SignType { get; set; }
/// <summary>
/// summary
/// </summary>
public string? SignTypeStr
{ get { return SignType?.GetDescription(); } }
/// <summary>
/// 标记方式
/// </summary>
[JsonIgnore]
public ComplaintSignWay? SignWay { get; set; }
/// <summary>
/// 标记方式
/// </summary>
public string? SignWayStr
{ get { return SignWay?.GetDescription(); } }
/// <summary>
/// 标记时间
/// </summary>
public DateTime? Ctime { get; set; }
/// <summary>
/// 投诉来源
/// </summary>
[JsonIgnore]
public ComplaintSource? Source { get; set; }
/// <summary>
/// 投诉来源
/// </summary>
public string? SourceStr
{ get { return Source?.GetDescription(); } }
/// <summary>
/// 状态
/// </summary>
[JsonIgnore]
public ComplaintStatus? Status { get; set; }
/// <summary>
/// 状态
/// </summary>
public string? StatusStr
{ get { return Status?.GetDescription(); } }
/// <summary>
/// 倒计时
/// </summary>
public string? CountDown
{
get
{
if (Status.HasValue && (Status == ComplaintStatus. || Status == ComplaintStatus.))
{
return "";
}
var timespan = Ctime?.AddDays(1) - DateTime.Now;
if (timespan.HasValue)
{
var hour = timespan.Value.Hours;
var minute = timespan.Value.Minutes;
var second = timespan.Value.Seconds;
if (hour >= 0 && minute >= 0 && second >= 0)
{
return $"{hour}小时{minute}分{second}秒";
}
}
return "已超时";
}
}
/// <summary>
/// 工单内容
/// </summary>
public string? FollowContent { get; set; }
/// <summary>
/// 跟进时间
/// </summary>
public DateTime? FollowTime { get; set; }
/// <summary>
/// 跟进人工号
/// </summary>
public int? Eid { get; set; }
/// <summary>
/// 跟进人
/// </summary>
public string? EName { get; set; }
/// <summary>
/// 潜在投诉内容
/// </summary>
public string? Content { get; set; }
/// <summary>
/// 投诉内容
/// </summary>
[JsonIgnore]
public string? ContentJson { get; set; }
/// <summary>
/// 违规关键字
/// </summary>
public string? Keywords { get; set; }
/// <summary>
/// 是否存在订单
/// </summary>
public bool HasOrder { get; set; }
/// <summary>
/// 是否存在分配
/// </summary>
public bool? HasAssign { get; set; }
/// <summary>
/// appid
/// </summary>
public string? Appid { get; set; }
/// <summary>
/// appuserid
/// </summary>
public string? Appuserid { get; set; }
/// <summary>
/// 水军类型
/// </summary>
public ComplaintSignReason? Reason { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class ComplaintEventDto
{
public int Id { get; set; }
public int? Fid { get; set; }
public DateTime? Ctime { get; set; }
}
}

View File

@ -0,0 +1,28 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class ComplaintFollowDetailDto
{
public int Id { get; set; }
public int? Eid { get; set; }
public string? Ename { get; set; }
public string? Crmappid { get; set; }
public string? Title { get; set; }
public string? Deptname { get; set; }
public string? Content { get; set; }
public DateTime? Ctime { get; set; }
}
}

View File

@ -0,0 +1,69 @@
using Hg.Complaint.Domain.Dto.ContentModel;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class ComplaintLogDetailDto
{
public int Id { get; set; }
[JsonIgnore]
public ComplaintSignType? SignType { get; set; }
public string? SignTypeStr { get { return SignType?.GetDescription(); } }
[JsonIgnore]
public ComplaintSignWay? SignWay { get; set; }
public string? SignWayStr { get { return SignWay?.GetDescription(); } }
[JsonIgnore]
public ComplaintSource? Source { get; set; }
public string? SourceStr { get { return Source?.GetDescription(); } }
public string? Type { get; set; }
public DateTime? Ctime { get; set; }
public string? Content { get; set; }
/// <summary>
/// 企微聊天id
/// </summary>
public string? Msgid { get; set; }
public string? Appid { get; set; }
public string? Appuserid { get; set; }
public string? InternalAppuserid { get; set; }
public string? Nickname { get; set; }
public string? InternalNickname { get; set; }
[JsonIgnore]
public string? ContentJson { get; set; }
public string? Keywords { get; set; }
public string? Deptname { get; set; }
public string? Signer { get; set; }
public int? Deptid { get; set; }
public int? Eid { get; set; }
/// <summary>
/// 水军类型
/// </summary>
public ComplaintSignReason? Reason { get; set; }
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
/// <summary>
/// 投诉日志队列
/// </summary>
public class ComplaintLogMessageDto : ComplaintLog
{
/// <summary>
/// 重试次数
/// </summary>
public int RetryCount { get; set; } = 0;
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto.ContentModel
{
internal class FormContentModelDto
{
[JsonPropertyName("comment")]
public string? Comment { get; set; }
[JsonPropertyName("content")]
public string? Content { get; set; }
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto.ContentModel
{
public class NegativeContentModelDto : NegativeMessageDto
{
}
}

View File

@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class CreateLiveAuditDto
{
/// <summary>
/// 审核id
/// </summary>
public int AuditId { get; set; }
/// <summary>
/// 操作人
/// </summary>
public string? Operator { get; set; }
/// <summary>
/// 审核备注
/// </summary>
public string? AuditRemark { get; set; }
/// <summary>
/// 驳回理由
/// </summary>
public LiveAuditRejectReason? RejectReason { get; set; }
/// <summary>
/// 审核附件
/// </summary>
public string? AuditAccessory { get; set; }
/// <summary>
/// 审核状态
/// </summary>
public LiveAuditStatus AuditStatus { get; set; }
}
public class AddLiveAuditDto
{
/// <summary>
/// 回放ids
/// </summary>
public string playBackIds { get; set; }
/// <summary>
/// 直播间id
/// </summary>
public int? SchedulesId { get; set; }
/// <summary>
/// 操作人
/// </summary>
public string? Operator { get; set; }
/// <summary>
/// 审核备注
/// </summary>
public string? AuditRemark { get; set; }
/// <summary>
/// 驳回理由
/// </summary>
public LiveAuditRejectReason? RejectReason { get; set; }
/// <summary>
/// 审核附件
/// </summary>
public string? AuditAccessory { get; set; }
/// <summary>
/// 审核状态
/// </summary>
public LiveAuditStatus AuditStatus { get; set; }
}
public class SolveLiveAuditDto
{
/// <summary>
/// 回放ids
/// </summary>
public int logId { get; set; }
/// <summary>
/// 操作人
/// </summary>
public string? Operator { get; set; }
/// <summary>
/// 解决备注
/// </summary>
public string? SolveRemark { get; set; }
/// <summary>
/// 解决附件
/// </summary>
public string? SolveAccessory { get; set; }
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class CreateLiveAuditRectificationDto
{
/// <summary>
/// 审核id
/// </summary>
public int AuditId { get; set; }
/// <summary>
/// 操作人
/// </summary>
public string? Operator { get; set; }
/// <summary>
/// 整改附件
/// </summary>
public string? RectificationAccessory { get; set; }
/// <summary>
/// 整改备注
/// </summary>
public string? RectificationRemark { get; set; }
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
internal class CrmUserInfoDto
{
public int? Pkid { get; set; }
public int? Eid { get; set; }
public string? Uname { get; set; }
public int? DeptId { get; set; }
public string? DeptName { get; set; }
public int? GroupId { get; set; }
public string? GroupName { get; set; }
}
}

View File

@ -0,0 +1,14 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class DeptmentGroupDto : DeptmentGroup
{
public virtual List<Deptment>? Deptments { get; set; }
}
}

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class LiveAuditDetailDto
{
public int Id { get; set; }
/// <summary>
/// 直播链接
/// </summary>
public string? LiveUrl { get; set; }
/// <summary>
/// 直播信息
/// </summary>
public string? LiveRoomInfo { get; set; }
/// <summary>
///
/// </summary>
public LiveAuditStatus AuditStatus { get; set; }
/// <summary>
/// 整改附件
/// </summary>
public string? RectificationAccessory { get; set; }
/// <summary>
/// 整改备注
/// </summary>
public string? RectificationRemark { get; set; }
/// <summary>
/// 直播课程id
/// </summary>
public int? ScheduleId { get; set; }
/// <summary>
/// 直播日期
/// </summary>
public string? LiveDate { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using Hg.Core.Entity.Hgaction;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class LiveAuditDto : LiveAudit
{
public string? Deptname { get; set; }
public string? Event { get { return AuditStatus.GetDescription(); } }
}
}

View File

@ -0,0 +1,15 @@
using Hg.Core.Entity.Hgaction;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class LiveAuditLogDto : LiveAuditLog
{
public string? Event { get { return AuditStatus.GetDescription(); } }
}
}

View File

@ -0,0 +1,166 @@
using Hg.Core.Entity.Hgaction;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class LiveInfoDto : LiveInfo
{
/// <summary>
/// 违规次数
/// </summary>
public int RejectCount { get; set; }
public string? Deptname { get; set; }
/// <summary>
/// 审核状态
/// </summary>
public LiveAuditStatus AuditStatus { get; set; }
/// <summary>
/// 审核状态
/// </summary>
public string? AuditStatusStr { get { return AuditStatus.GetDescription(); } }
/// <summary>
/// 待审核次数
/// </summary>
public int ToBeReviewedCount { get; set; }
/// <summary>
/// 已整改次数
/// </summary>
public int RectifyCount { get; set; }
/// <summary>
/// 审核通过次数
/// </summary>
public int PassCount { get; set; }
}
public class LiveAuditSchedules {
/// <summary>
/// 课程id
/// </summary>
public int? schedulesId { get; set; }
/// <summary>
/// 课程标题
/// </summary>
public string? title { get; set; }
/// <summary>
/// 第一列备注
/// </summary>
public string? cmsmome { get; set; }
/// <summary>
/// 简介
/// </summary>
public string? mome { get; set; }
/// <summary>
/// 管理事业部
/// </summary>
public string? deptid { get; set; }
/// <summary>
/// 事业部名称
/// </summary>
public string? deptname { get; set; }
/// <summary>
/// 直播开始时间
/// </summary>
public string? starttime { get; set; }
/// <summary>
/// 直播结束时间
/// </summary>
public string? endtime { get; set; }
/// <summary>
/// 直播老师
/// </summary>
public string? Liver { get; set; }
/// <summary>
/// 预热时间
/// </summary>
public int? pretime { get; set; }
/// <summary>
/// 直播周
/// </summary>
public string? weeks { get; set; }
/// <summary>
/// 营销类型:
/// 1、免费营销
/// 10、升级营销
/// 100、付费服务
/// </summary>
public MarketingType? marketingtype { get; set; }
/// <summary>
/// 直播流id
/// </summary>
public int? livestreamid { get; set; }
/// <summary>
/// 投放方式
/// 1: 直播间
/// 2工作室
/// </summary>
public RoomType? roomtype { get; set; }
/// <summary>
/// 待审核 直播
/// </summary>
public int waitAudit { get; set; } = 0;
/// <summary>
/// 违规次数
/// </summary>
public int violation { get; set; } = 0;
}
public class LiveAuditPlayBack {
public int? pbid { get; set; }
public int scheduleid { get; set; }
public string? title { get; set; }
public string? thumb { get; set; }
public string? intro { get; set; }
public DateTime? ctime { get; set; }
public string? url { get; set; }
public int? recordid { get; set; }
public int? status { get; set; }
public int? istop { get; set; }
public DateTime? zbdate { get; set; }
public LiveAuditStatus? auditStatus { get; set; }
}
public class PlayBackAuditLog
{
public int? playBackId { get; set; }
public int? schedulesId { get; set; }
public LiveAuditStatus auditStatus { get; set; }
public LiveAuditRejectReason? rejectReason { get; set; }
public string? auditRemark { get; set; }
public string? auditAccessory { get; set; }
public string? creater { get; set; }
public DateTime? ctime { get; set; }
/// <summary>
/// 处理人
/// </summary>
public string? solver { get; set; }
/// <summary>
/// 处理事件
/// </summary>
public DateTime? solveTime { get; set; }
public string? solveRemark { get; set; }
public string? solveAccessory { get; set; }
}
public enum WeeksEnum
{
= 0,
= 1,
= 2,
= 3,
= 4,
= 5,
= 6
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class MarkCustomerDto
{
/// <summary>
/// 1为合规 2 为业务
/// </summary>
public int? Type { get; set; }
public bool IsResid { get; set; }
public string? PostId { get; set; }
public ComplaintSignType? Signtype { get; set; }
public string? Content { get; set; }
public string? DeptId { get; set; }
/// <summary>
/// 工号
/// </summary>
public int? Eid { get; set; }
/// <summary>
/// 员工姓名
/// </summary>
public string? Ename { get; set; }
}
}

View File

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class MarkTrollsDto
{
/// <summary>
/// 平台id
/// </summary>
public string? Appid { get; set; }
/// <summary>
/// 用户账号比如软件平台username,服务号openid
/// </summary>
public string? Appuserid { get; set; }
public int? Eid { get; set; }
public ComplaintSignReason? Reason { get; set; }
public string? Content { get; set; }
public string? Deptid { get; set; }
}
}

View File

@ -0,0 +1,155 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
/// <summary>
/// 消极消息
/// </summary>
public class NegativeMessageDto
{
/// <summary>
/// 消息ID
/// </summary>
public string? Msgid { get; set; }
/// <summary>
/// 消息类型
/// </summary>
public string? Msgtype { get; set; }
/// <summary>
/// 接收时间
/// </summary>
public long? Recvtime { get; set; }
/// <summary>
/// 发送时间
/// </summary>
public long? Msgtime { get; set; }
/// <summary>
/// 企微ID
/// </summary>
public string? Corpid { get; set; }
/// <summary>
/// 企微名称
/// </summary>
public string? Corpname { get; set; }
/// <summary>
/// 群消息接收者列表
/// </summary>
public string? Tolist { get; set; }
/// <summary>
/// 房间ID
/// </summary>
public string? Roomid { get; set; }
/// <summary>
/// 发送序列号
/// </summary>
public long? Seq { get; set; }
/// <summary>
/// 发送或接收,1:发送,2:接收
/// </summary>
[JsonPropertyName("send_type")]
public int? SendType { get; set; }
/// <summary>
/// 聊天内容
/// </summary>
[JsonPropertyName("text_content")]
public string? TextContent { get; set; }
/// <summary>
/// 不合规词汇ID位图
/// </summary>
[JsonPropertyName("illegal_words")]
public string[]? IllegalWords { get; set; }
/// <summary>
/// 外部联系人客户ID
/// </summary>
[JsonPropertyName("external_resid")]
public string? ExternalResid { get; set; }
/// <summary>
/// 外部联系人联合ID
/// </summary>
[JsonPropertyName("external_unionid")]
public string? ExternalUnionid { get; set; }
/// <summary>
/// 外部联系人账号或群ID
/// </summary>
[JsonPropertyName("external_appuserid")]
public string? EnternalAppuserid { get; set; }
/// <summary>
/// 外部联系人昵称或群名称
/// </summary>
[JsonPropertyName("external_nickname")]
public string? ExternalNickname { get; set; }
/// <summary>
/// 内部联系人工号
/// </summary>
[JsonPropertyName("internal_eid")]
public int? EnternalEid { get; set; }
/// <summary>
/// 内部联系人小组ID
/// </summary>
[JsonPropertyName("internal_groupid")]
public int? InternalGroupid { get; set; }
/// <summary>
/// 内部联系人部门ID
/// </summary>
[JsonPropertyName("internal_deptid")]
public int? InternalDeptid { get; set; }
/// <summary>
/// 内部联系人员工姓名
/// </summary>
[JsonPropertyName("internal_employee_name")]
public string? InternalEmployeeName { get; set; }
/// <summary>
/// 内部联系人小组名称
/// </summary>
[JsonPropertyName("internal_group_name")]
public string? InternalGroupName { get; set; }
/// <summary>
/// 内部联系人业务部名称
/// </summary>
[JsonPropertyName("internal_dept_name")]
public string? InternalDeptName { get; set; }
/// <summary>
/// 内部联系人联合ID
/// </summary>
[JsonPropertyName("internal_unionid")]
public string? InternalUnionid { get; set; }
/// <summary>
/// 内部联系人昵称或群名称
/// </summary>
[JsonPropertyName("internal_nickname")]
public string? InternalNickname { get; set; }
/// <summary>
/// 内部联系人ID
/// </summary>
[JsonPropertyName("internal_appuserid")]
public string? InternalAppuserid { get; set; }
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class RiskInfoDto
{
public int Ret { get; set; }
public string? Answer { get; set; }
public long CreateTime { get; set; }
public string? IdCard { get; set; }
public string? Name { get; set; }
public string? Key { get; set; }
public string? Style { get; set; }
public int Index { get; set; }
public string? Businesstype { get; set; }
}
}

View File

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class SearchComplaintDto : SearchPageBase
{
public string? DeptId { get; set; }
public string? ResId { get; set; }
public string? UMID { get; set; }
public string? UName { get; set; }
/// <summary>
/// 来源
/// </summary>
public ComplaintSource? Source { get; set; }
/// <summary>
/// 标记类型 标记类型1.电话 2. 违规关键词 3.企微投诉连接
/// </summary>
public ComplaintSignType? SignType { get; set; }
/// <summary>
/// 标记方法1.人工标记 2. 系统标记 3.客户投诉
/// </summary>
public ComplaintSignWay? SignWay { get; set; }
/// <summary>
/// 状态
/// </summary>
public ComplaintStatus? Status { get; set; }
public DateTime? STime { get; set; }
public DateTime? ETime { get; set; }
[JsonPropertyName("txt_groupIds")]
public string? Txt_groupIds { get; set; }
[JsonPropertyName("txt_deptId")]
public string? Txt_deptId { get; set; }
/// <summary>
/// 是否有成交
/// </summary>
public bool? HasOrder { get; set; }
/// <summary>
/// 是否有分配
/// </summary>
public bool? HasAssign { get; set; }
[JsonPropertyName("txt_deptId")]
public int? Txt_userId { get; set; }
public string? Content { get; set; }
}
}

View File

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class SearchLiveAuditDto : SearchPageBase
{
/// <summary>
/// 直播间id
/// </summary>
public int? LiveId { get; set; }
/// <summary>
/// 事业部
/// </summary>
public int? Deptid { get; set; }
/// <summary>
/// 备注
/// </summary>
public string? Remark { get; set; }
/// <summary>
/// 直播人
/// </summary>
public string? Liver { get; set; }
/// <summary>
/// 时间
/// </summary>
public DateTime? TimeFrom { get; set; }
/// <summary>
/// 课程
/// </summary>
public string? Schedule { get; set; }
/// <summary>
/// 时间
/// </summary>
public DateTime? TimeTo { get; set; }
/// <summary>
/// 审核人
/// </summary>
public string? Auditer { get; set; }
/// <summary>
/// 审核状态
/// </summary>
public LiveAuditStatus? AuditStatus { get; set; }
/// <summary>
/// 审核时间
/// </summary>
public DateTime? AuditTimeFrom { get; set; }
/// <summary>
/// 审核时间
/// </summary>
public DateTime? AuditTimeTo { get; set; }
}
}

View File

@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class SearchLiveDto : SearchPageBase
{
/// <summary>
/// 事业部
/// </summary>
public int? Deptid { get; set; }
/// <summary>
/// 直播间id
/// </summary>
public int? LiveId { get; set; }
/// <summary>
/// 平台
/// </summary>
public int? PlatformId { get; set; }
/// <summary>
/// 课程
/// </summary>
public string? Schedule { get; set; }
/// <summary>
/// 直播人
/// </summary>
public string? Liver { get; set; }
/// <summary>
/// 时间
/// </summary>
public DateTime? TimeFrom { get; set; }
/// <summary>
/// 时间
/// </summary>
public DateTime? TimeTo { get; set; }
/// <summary>
/// 审核状态
/// </summary>
public LiveAuditStatus? AuditStatus { get; set; }
}
public class SearchLiveScheduleDto : SearchPageBase
{
/// <summary>
/// 平台(暂不传)
/// </summary>
public int? PlatformId { get; set; }
/// <summary>
/// 事业部
/// </summary>
public int? Deptid { get; set; }
/// <summary>
/// 课程
/// </summary>
public string? Schedule { get; set; }
/// <summary>
/// 直播人
/// </summary>
public string? Liver { get; set; }
///// <summary>
///// 时间
///// </summary>
//public DateTime? TimeFrom { get; set; }
///// <summary>
///// 时间
///// </summary>
//public DateTime? TimeTo { get; set; }
/// <summary>
/// 审核状态
/// </summary>
// public LiveAuditStatus? AuditStatus { get; set; }
}
public class SearchLivePlayBackDto : SearchPageBase
{
/// <summary>
/// 课程表id
/// </summary>
public int? schedulesId { get; set; }
public DateTime? stime { get; set; }
public DateTime? etime { get; set; }
public int? LiveAuditStatus { get; set; }
}
public class SearchPlayBackAuditLogDto : SearchPageBase
{
/// <summary>
/// playBackId
/// </summary>
public int? playBackId { get; set; }
}
}

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class UpdateComplaintStatusDto
{
public int Id { get; set; }
public int? Eid { get; set; }
public string? Ename { get; set; }
/// <summary>
/// 状态
/// </summary>
public ComplaintStatus Status { get; set; }
/// <summary>
/// 内容
/// </summary>
public string? Content { get; set; }
}
public class ComplainStatusModel
{
public int Id { get; set; }
/// <summary>
/// 状态
/// </summary>
public ComplaintStatus Status { get; set; }
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Dto
{
public class UserInfoDto
{
[JsonPropertyName("uid")]
public string? Uid { get; set; }
[JsonPropertyName("appid")]
public string? Appid { get; set; }
[JsonPropertyName("appuserid")]
public string? Appuserid { get; set; }
[JsonPropertyName("customerid")]
public string? Customerid { get; set; }
[JsonPropertyName("resid")]
public string? Resid { get; set; }
[JsonPropertyName("unionid")]
public string? Unionid { get; set; }
public string? Ch { get; set; }
}
}

View File

@ -0,0 +1,51 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Confluent.Kafka" Version="1.9.2" />
<PackageReference Include="DG.Redis" Version="1.0.17" />
<PackageReference Include="DG.Tool" Version="1.0.9" />
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Serilog.AspNetCore" Version="6.0.1" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Exceptionless.AspNetCore" Version="6.0.1" />
<PackageReference Include="Serilog.Sinks.Exceptionless" Version="4.0.0" />
<PackageReference Include="DG.Core" Version="1.0.25" />
<PackageReference Include="Zxd.Core.Shared" Version="1.0.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Hg.Core.EntityFramework\Hg.Core.EntityFramework.csproj" />
<ProjectReference Include="..\Hg.Core.Shared\Hg.Core.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="DG.EntityFramework" />
<Using Include="Exceptionless" />
<Using Include="System.Text.Json" />
<Using Include="DG.Core" />
<Using Include="DG.Redis" />
<Using Include="DG.Tool" />
<Using Include="Serilog" />
<Using Include="System.Text" />
<Using Include="System.Text.Json.Serialization" />
<Using Include="Microsoft.EntityFrameworkCore" />
<Using Include="Microsoft.Extensions.Configuration" />
<Using Include="System.Linq.Expressions" />
<Using Include="Hg.Core.EntityFramework" />
<Using Include="Hg.Core.Entity" />
<Using Include="Hg.Complaint.Domain.Impl" />
<Using Include="Hg.Complaint.Domain.Config" />
<Using Include="Hg.Complaint.Domain.Dto" />
<Using Include="Hg.Core.Entity.HgCrm" />
<Using Include="Hg.Core.Entity.Complaint" />
<Using Include="Hg.Core.Entity.Dncmsbase" />
<Using Include="Hg.Core.Entity.UserCenter" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Zxd.Core.Shared.Dto;
namespace Hg.Complaint.Domain.Impl
{
internal interface ICacheDomain : ITransientDependency
{
Task<List<DeptmentDto>> GetDeptments();
Task<List<DeptmentGroupDto>> GetDeptmentGroups();
Task<DeptmentGroupDto?> GetDeptmentGroupByDeptid(int? deptid);
}
}

View File

@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Impl
{
public interface IComplaintDomain : IScopedDependency
{
/// <summary>
/// 分析消极消息
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
Task<bool> AnalyseNegativeMessage(NegativeMessageDto dto);
/// <summary>
/// 申请投诉
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
Task<bool> ApplyComplaint(ApplyComplaintDto dto);
/// <summary>
/// 分析投诉日志
/// </summary>
/// <param name="messageDto"></param>
/// <returns></returns>
Task AnalyseComplaintLog(ComplaintLogMessageDto messageDto);
/// <summary>
/// 获取队列数量
/// </summary>
/// <returns></returns>
Task<long> GetQueue();
/// <summary>
/// 消费单个队列对象
/// </summary>
/// <returns></returns>
Task<ComplaintLogMessageDto> DequeueQueue();
/// <summary>
/// 获取队列信息
/// </summary>
/// <returns></returns>
Task<List<ComplaintLogMessageDto>> GetQueues();
/// <summary>
/// 分页
/// </summary>
/// <param name="dto"></param>
/// <param name="appid"></param>
/// <returns></returns>
Task<PageResult<ComplaintDto>> GetPage(SearchComplaintDto dto, string? appid);
/// <summary>
/// 重新发起队列
/// </summary>
/// <returns></returns>
Task<List<ComplaintLogMessageDto>> SyncQueue();
/// <summary>
/// 获取投诉详情
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Task<ComplaintDetailDto> GetComplaintDetail(int id);
/// <summary>
/// 根据资源id和业务线获取投诉详情
/// </summary>
/// <param name="resid"></param>
/// <param name="deptid"></param>
/// <returns></returns>
Task<ComplaintDetailDto> GetComplaintDetail(string? resid, int? deptid);
/// <summary>
/// 根据appid和业务线获取投诉详情
/// </summary>
/// <param name="appid"></param>
/// <param name="appuserid"></param>
/// <param name="deptid"></param>
/// <returns></returns>
Task<ComplaintDetailDto> GetComplaintDetail(string? appid, string? appuserid, int? deptid);
/// <summary>
/// 更新投诉状态
/// </summary>
/// <param name="dto"></param>
/// <param name="appid"></param>
/// <returns></returns>
Task<bool> UpdateComplaintStatus(UpdateComplaintStatusDto dto, string? appid);
/// <summary>
/// 获取跟进状态
/// </summary>
/// <param name="udid"></param>
/// <returns></returns>
Task<ComplainStatusModel> GetComplaintStatus(int udid);
/// <summary>
/// 标记用户
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
Task<bool> MarkCustomer(MarkCustomerDto dto);
/// <summary>
/// 初始化处理过期未处理投诉
/// </summary>
/// <returns></returns>
Task InitOverdueComplaint();
/// <summary>
/// 处理过期未处理投诉
/// </summary>
/// <returns></returns>
Task OverdueComplaint();
/// <summary>
/// 刷新resid
/// </summary>
/// <returns></returns>
Task SyncResid();
/// <summary>
/// 批量分配客户给客服
/// </summary>
/// <param name="dto"></param>
/// <param name="appid"></param>
/// <returns></returns>
Task<bool> BatchAssignComplaint(BatchAssignComplaintDto dto, string? appid);
/// <summary>
///
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
Task<bool> MarkTrolls(MarkTrollsDto dto);
Task<int> ComplaintLabel(string resId);
}
}

View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hg.Complaint.Domain.Impl
{
public interface ILiveAuditDomain : IScopedDependency
{
Task<PageResult<LiveAuditDto>> GetLiveAuditPage(SearchLiveAuditDto dto);
Task<List<LiveAuditLogDto>> GetLiveAuditLogs(int auditId, string? date);
Task CreateLiveAudit(CreateLiveAuditDto dto);
Task CreateLiveAuditRectification(CreateLiveAuditRectificationDto dto);
List<SelectItem> GetRejectReasonSelect();
List<SelectItem> GetAuditStatusSelect();
Task<List<SelectItem>> GetPlatformSelect();
Task<LiveAuditDetailDto> GetLiveAuditDetail(int auditId);
Task SyncLiveData();
Task<PageResult<LiveInfoDto>> GetLivePage(SearchLiveDto dto);
Task AddLiveAuditLog(AddLiveAuditDto dto);
Task SolveLiveAudit(SolveLiveAuditDto dto);
Task<PageResult<LiveAuditSchedules>> GetLiveSchedulePage(SearchLiveScheduleDto dto);
Task<PageResult<LiveAuditPlayBack>> GetLivePlayBackPage(SearchLivePlayBackDto dto);
Task<PageResult<PlayBackAuditLog>> GetPlayBackAuditLogPage(SearchPlayBackAuditLogDto dto);
}
}

View File

@ -0,0 +1,879 @@
using DG.Core;
using Exceptionless.Models;
using Hg.Core.Entity.Dncmsbase;
using Hg.Core.Entity.Hgaction;
using Hg.Core.Entity.Views.DNG8;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database;
namespace Hg.Complaint.Domain
{
internal class LiveAuditDomain : ILiveAuditDomain
{
private readonly IServiceProvider _serviceProvider;
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 LiveAuditDomain(
IRedisManager redisManager,
IConfiguration configuration,
IMapper mapper,
IHttpClient httpClient,
ICacheDomain cacheDomain,
IServiceProvider serviceProvider)
{
_systemConfig = configuration.GetSection("SystemConfig").Get<SystemConfig>();
_redisManager = redisManager;
_mapper = mapper;
_httpClient = httpClient;
_configuration = configuration;
_cacheDomain = cacheDomain;
_serviceProvider = serviceProvider;
}
public async Task<PageResult<LiveInfoDto>> GetLivePage(SearchLiveDto dto)
{
var deptments = await _cacheDomain.GetDeptments();
using var scope = _serviceProvider.CreateAsyncScope();
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
var logQuery = hgActionRepository.GetRepository<LiveAuditLog>().Query();
var auditQuery = hgActionRepository.GetRepository<LiveAudit>().Query();
#region
var query = from a in hgActionRepository.GetRepository<LiveInfo>().Query()
select new LiveInfoDto
{
Id = a.Id,
Deptid = a.Deptid,
PlatformId = a.PlatformId,
Platform = a.Platform,
LiveUrl = a.LiveUrl,
LiveId = a.LiveId,
Liver = a.Liver,
Schedule = a.Schedule,
ScheduleId = a.ScheduleId,
LiveTitle = a.LiveTitle,
Ctime = a.Ctime,
PassCount = (
from c in auditQuery
where c.LiveId == a.LiveId && c.AuditStatus == LiveAuditStatus.
select c.Id).Count(),
RectifyCount = (
from c in auditQuery
where c.LiveId == a.LiveId && c.AuditStatus == LiveAuditStatus.
select c.Id).Count(),
RejectCount = (
from c in auditQuery
where c.LiveId == a.LiveId && c.AuditStatus == LiveAuditStatus.
select c.Id).Count(),
ToBeReviewedCount = (
from c in auditQuery
where c.LiveId == a.LiveId && c.AuditStatus == LiveAuditStatus.
select c.Id).Count()
};
query = query.GroupBy(x => new
{
x.Id,
x.Deptid,
x.PlatformId,
x.Platform,
x.LiveUrl,
x.LiveId,
x.Liver,
x.Schedule,
x.ScheduleId,
x.LiveTitle,
x.Ctime,
}).Select(x => new LiveInfoDto
{
Id = x.Key.Id,
Deptid = x.Key.Deptid,
PlatformId = x.Key.PlatformId,
Platform = x.Key.Platform,
LiveUrl = x.Key.LiveUrl,
LiveId = x.Key.LiveId,
Liver = x.Key.Liver,
Schedule = x.Key.Schedule,
ScheduleId = x.Key.ScheduleId,
LiveTitle = x.Key.LiveTitle,
Ctime = x.Key.Ctime,
PassCount = x.Sum(x => x.PassCount),
RectifyCount = x.Sum(x => x.RectifyCount),
RejectCount = x.Sum(x => x.RejectCount),
ToBeReviewedCount = x.Sum(x => x.ToBeReviewedCount),
});
#endregion
query = query.If(!string.IsNullOrEmpty(dto.Liver), x => x.Where(x => x.Liver.Contains(dto.Liver)))
.If(!string.IsNullOrEmpty(dto.Schedule), x => x.Where(x => x.Schedule.Contains(dto.Schedule)))
.If(dto.Deptid.HasValue, x => x.Where(x => x.Deptid == dto.Deptid.Value))
.If(dto.LiveId.HasValue, x => x.Where(x => x.LiveId == dto.LiveId.Value))
.If(dto.PlatformId.HasValue, x => x.Where(x => x.PlatformId == dto.PlatformId.Value))
.If(dto.TimeFrom.HasValue, x => x.Where(x => x.Ctime >= dto.TimeFrom.Value))
.If(dto.TimeTo.HasValue, x => x.Where(x => x.Ctime < dto.TimeTo.Value));
var total = await query.CountAsync();
var data = await query.OrderByDescending(x => x.Id)
.Skip((dto.PageIndex - 1) * dto.PageSize)
.Take(dto.PageSize)
.ToListAsync();
var lecturerids = string.Join(",", data.Select(x => x.Liver)).Split(',').Select(x => int.Parse(x)).ToList();
var lecturers = await dncmsbaseRepository.GetRepository<Lecturer>().Query()
.Where(x => lecturerids.Contains(x.Id))
.Select(x => new
{
x.Id,
x.Title
}).ToListAsync();
foreach (var item in data)
{
item.Deptname = deptments.Where(y => y.Id == item.Deptid).Select(x => x.Title).FirstOrDefault();
var liver = item.Liver?.Split(',').Select(x => int.Parse(x)).ToList();
var lecturer = lecturers.Where(x => liver.Contains(x.Id)).ToList();
item.Liver = string.Join(",", lecturer.Select(x => x.Title).ToList());
var audit = await auditQuery.Where(y => y.LiveId == item.LiveId).OrderByDescending(y => y.Id).FirstOrDefaultAsync();
if (audit == null)
{
item.AuditStatus = LiveAuditStatus.;
}
else
{
var log = await logQuery.Where(y => y.LiveId == item.LiveId).OrderByDescending(y => y.Id).FirstOrDefaultAsync();
item.AuditStatus = log != null && audit.AuditStatus == LiveAuditStatus. && log.AuditStatus != LiveAuditStatus. ? log.AuditStatus
: audit.AuditStatus;
}
}
return new PageResult<LiveInfoDto>(dto.PageIndex, dto.PageSize, total, data);
}
public async Task<PageResult<LiveAuditDto>> GetLiveAuditPage(SearchLiveAuditDto dto)
{
var deptments = await _cacheDomain.GetDeptments();
using var scope = _serviceProvider.CreateAsyncScope();
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
var query = hgActionRepository.GetRepository<LiveAudit>().Query()
.If(dto.LiveId.HasValue, x => x.Where(x => x.LiveId == dto.LiveId))
.If(dto.Deptid.HasValue, x => x.Where(x => x.Deptid == dto.Deptid))
.If(!string.IsNullOrEmpty(dto.Schedule), x => x.Where(x => x.Schedule.Contains(dto.Schedule)))
.If(!string.IsNullOrEmpty(dto.Auditer), x => x.Where(x => x.Auditer.Contains(dto.Auditer)))
.If(!string.IsNullOrEmpty(dto.Liver), x => x.Where(x => x.Liver.Contains(dto.Liver)))
.If(!string.IsNullOrEmpty(dto.Remark), x => x.Where(x => x.Remark.Contains(dto.Remark)))
.If(dto.TimeFrom.HasValue, x => x.Where(x => x.Ctime >= dto.TimeFrom.Value))
.If(dto.TimeTo.HasValue, x => x.Where(x => x.Ctime < dto.TimeTo.Value))
.If(dto.AuditTimeFrom.HasValue, x => x.Where(x => x.AuditTime >= dto.AuditTimeFrom.Value))
.If(dto.AuditTimeTo.HasValue, x => x.Where(x => x.AuditTime < dto.AuditTimeTo.Value))
.If(dto.AuditStatus.HasValue, x => x.Where(x => x.AuditStatus == dto.AuditStatus));
var total = await query.CountAsync();
var list = await query.OrderByDescending(x => x.Id)
.Skip((dto.PageIndex - 1) * dto.PageSize)
.Take(dto.PageSize)
.ToListAsync();
var data = _mapper.Map<LiveAudit, LiveAuditDto>(list);
var ids = data.Select(x => x.Id).ToList();
data.ForEach(x =>
{
x.Deptname = deptments.Where(y => y.Id == x.Deptid).Select(x => x.Title).FirstOrDefault();
});
return new PageResult<LiveAuditDto>(dto.PageIndex, dto.PageSize, total, data);
}
public async Task<List<LiveAuditLogDto>> GetLiveAuditLogs(int auditId, string? date)
{
using var scope = _serviceProvider.CreateAsyncScope();
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
var liveAudit = await hgActionRepository.GetRepository<LiveAudit>().Query()
.FirstOrDefaultAsync(x => x.Id == auditId) ?? throw new ApiException("审核记录不存在或已删除!");
var auditIds = await hgActionRepository.GetRepository<LiveAudit>().Query()
.Where(x => x.LiveId == liveAudit.LiveId && x.LiveDate == date)
.Select(x => x.Id)
.ToListAsync();
var list = await hgActionRepository.GetRepository<LiveAuditLog>().Query()
.Where(x => auditIds.Contains(x.AuditId)).ToListAsync();
var data = _mapper.Map<LiveAuditLog, LiveAuditLogDto>(list);
return data;
}
public async Task CreateLiveAudit(CreateLiveAuditDto dto)
{
using var scope = _serviceProvider.CreateAsyncScope();
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
var data = await hgActionRepository.GetRepository<LiveAudit>().Query()
.FirstOrDefaultAsync(x => x.Id == dto.AuditId) ?? throw new ApiException("审核记录不存在或已删除!");
data.AuditStatus = dto.AuditStatus;
data.Auditer = dto.Operator;
data.AuditTime = DateTime.Now;
var log = _mapper.Map<CreateLiveAuditDto, LiveAuditLog>(dto);
log.Ctime = DateTime.Now;
log.Schedule = data.Schedule;
log.LiveId = data.LiveId;
using var transaction = await hgActionRepository.BeginTransactionAsync();
try
{
await hgActionRepository.GetRepository<LiveAudit>().UpdateAsync(data, x => new
{
x.AuditStatus,
x.AuditTime,
x.Auditer
});
await hgActionRepository.GetRepository<LiveAuditLog>().InsertAsync(log);
await transaction.CommitAsync();
}
catch (Exception)
{
await transaction.RollbackAsync();
await transaction.DisposeAsync();
throw;
}
}
#region 2
private DateTime limitDate = DateTime.Parse("2023-11-07");
//private DateTime limitDate = DateTime.Parse("1993-11-07");
/// <summary>
/// 课程表列表
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<PageResult<LiveAuditSchedules>> GetLiveSchedulePage(SearchLiveScheduleDto dto)
{
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
var deptments = await dncmsbaseRepository.GetRepository<Deptment>().Query().ToListAsync();
var zxdRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<ZxdDbContext>>();
var setting = await zxdRepository.GetRepository<BasParameter>().Query().FirstOrDefaultAsync(n => n.PARAKEY == "LiveAudtiDeptIgnoreSetting");
List<string> notCaontainDeptids = new List<string>();
if (setting != null)
{
var deptids = setting.PARAVALUE.Split(',').ToList();
notCaontainDeptids.AddRange(deptids);
}
#region
var query = from a in dncmsbaseRepository.GetRepository<ZhiboSystemSchedules>().Query().Where(m => m.Status == 1 && !notCaontainDeptids.Contains(m.Dept))
select new LiveAuditSchedules
{
schedulesId = a.Id,
title = a.Title,
cmsmome = a.Cmsmemo,
mome = a.Memo,
deptid = a.Dept,
starttime = a.Starttime,
endtime = a.Endtime,
Liver = a.Lecturer,
pretime = a.Pretime,
weeks = a.Weeks,
marketingtype = a.Marketingtype,
livestreamid = a.Livestreamid,
roomtype = a.Roomtype
};
query = query.If(!string.IsNullOrEmpty(dto.Liver), x => x.Where(x => x.Liver.Contains(dto.Liver)))
.If(!string.IsNullOrEmpty(dto.Schedule), x => x.Where(x => x.title.Contains(dto.Schedule)))
.If(dto.Deptid.HasValue, x => x.Where(x => x.deptid.Contains(dto.Deptid.Value.ToString())));
// .If(dto.PlatformId.HasValue, x => x.Where(x => x. == dto.PlatformId.Value))
// .If(dto.TimeFrom.HasValue, x => x.Where(x => x.Ctime >= dto.TimeFrom.Value))
// .If(dto.TimeTo.HasValue, x => x.Where(x => x.Ctime < dto.TimeTo.Value));
#endregion
var total = await query.CountAsync();
var data = await query.OrderByDescending(x => x.schedulesId)
.Skip((dto.PageIndex - 1) * dto.PageSize)
.Take(dto.PageSize)
.ToListAsync();
var lecturerids = string.Join(",", data.Select(x => x.Liver)).Split(',').Select(x => int.Parse(x)).ToList();
var lecturers = await dncmsbaseRepository.GetRepository<Lecturer>().Query()
.Where(x => lecturerids.Contains(x.Id))
.Select(x => new
{
x.Id,
x.Title
}).ToListAsync();
var schedulesIds = data.Select(m => m.schedulesId).ToList();
//var log = dncmsbaseRepository.GetRepository<CMS_LiveAuditLog>().Query().Where(m => schedulesIds.Contains(m.schedulesId)).ToList();
var pb = dncmsbaseRepository.GetRepository<ZhiboSystemSchedulesPlayback>().Query()
.Include(x => x.LiveAuditLogPlayBack)
.ThenInclude(x => x.CMS_LiveAuditLog)
.Where(m => schedulesIds.Contains(m.scheduleid) && m.zbdate > limitDate && m.status == 1).ToList();
foreach (var item in data)
{
var liver = item.Liver?.Split(',').Select(x => int.Parse(x)).ToList();
var lecturer = lecturers.Where(x => liver.Contains(x.Id)).ToList();
var mypb = pb.Where(m => m.scheduleid == item.schedulesId).ToList();
var mylog = mypb.SelectMany(n => n.LiveAuditLogPlayBack).Where(n => n.CMS_LiveAuditLog != null).Select(x => x.CMS_LiveAuditLog).ToList();
//var wlog = mylog.Where(i => i.auditStatus == LiveAuditStatus.待审核);
item.deptname = deptments.Where(y => item.deptid.Split(',').Contains(y.Id.ToString())).Select(x => x.Title).FirstOrDefault();//汉化事业部
item.Liver = string.Join(",", lecturer.Select(x => x.Title).ToList());//汉化老师
item.weeks = GetWeeksName(item.weeks);//汉化周
item.violation = mylog.Where(m => m.auditStatus == LiveAuditStatus. || m.auditStatus == LiveAuditStatus.).Count();//提交违规次数
//无提交审核记录的 和未审核 都算未审核
item.waitAudit = mypb.Count(n => !n.LiveAuditLogPlayBack.Any() || n.LiveAuditLogPlayBack.Exists(x => x.CMS_LiveAuditLog.auditStatus == LiveAuditStatus.));
//item.waitAudit = mypb.Count(m => !mylog.Select(n => n.schedulesId).Contains(m.scheduleid) || wlog.Select(n => n.schedulesId).Contains(m.scheduleid));
}
return new PageResult<LiveAuditSchedules>(dto.PageIndex, dto.PageSize, total, data);
}
/// <summary>
/// 直播回放列表
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<PageResult<LiveAuditPlayBack>> GetLivePlayBackPage(SearchLivePlayBackDto dto)
{
var deptments = await _cacheDomain.GetDeptments();
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
var query = from a in dncmsbaseRepository.GetRepository<ZhiboSystemSchedulesPlayback>().Query()
.Include(x => x.LiveAuditLogPlayBack)
.ThenInclude(x => x.CMS_LiveAuditLog)
.Where(m => m.scheduleid == dto.schedulesId && m.zbdate > limitDate && m.status == 1)
.If(dto.stime.HasValue, x => x.Where(x => x.ctime >= dto.stime))
.If(dto.etime.HasValue, x => x.Where(x => x.ctime <= dto.etime))
.If(dto.LiveAuditStatus.HasValue && dto.LiveAuditStatus == (int)LiveAuditStatus., x => x.Where(n => n.LiveAuditLogPlayBack.Any(x => x.CMS_LiveAuditLog.auditStatus == LiveAuditStatus.)))
.If(dto.LiveAuditStatus.HasValue && dto.LiveAuditStatus == (int)LiveAuditStatus., x => x.Where(n => !n.LiveAuditLogPlayBack.Any(x => x.CMS_LiveAuditLog.auditStatus == LiveAuditStatus.) && n.LiveAuditLogPlayBack.Any(x => x.CMS_LiveAuditLog.auditStatus == LiveAuditStatus.)))
.If(dto.LiveAuditStatus.HasValue && dto.LiveAuditStatus == (int)LiveAuditStatus., x => x.Where(n => n.LiveAuditLogPlayBack.All(x => x.CMS_LiveAuditLog.auditStatus == LiveAuditStatus.)))
.If(dto.LiveAuditStatus.HasValue && dto.LiveAuditStatus == (int)LiveAuditStatus., x => x.Where(n => !n.LiveAuditLogPlayBack.Any()))
select new LiveAuditPlayBack
{
pbid = a.id,
scheduleid = a.scheduleid,
title = a.title,
thumb = a.thumb,
intro = a.intro,
ctime = a.ctime,
url = a.url,
recordid = a.recordid,
status = a.status,
istop = a.istop,
zbdate = a.zbdate
};
var total = await query.CountAsync();
var data = await query.OrderByDescending(x => x.ctime)
.Skip((dto.PageIndex - 1) * dto.PageSize)
.Take(dto.PageSize)
.ToListAsync();
//判断回放记录当前状态
var lpbids = data.Select(m => m.pbid).ToList();
var mylpb = dncmsbaseRepository.GetRepository<LiveAuditLogPlayBack>().Query().Where(m => lpbids.Contains(m.playBackId)).ToList();//关系数据
var lpbGupLogid = mylpb.GroupBy(m => m.logId).ToDictionary(m => m.Key, n => n);
var myLog = dncmsbaseRepository.GetRepository<CMS_LiveAuditLog>().Query().Where(m => lpbGupLogid.Select(i => i.Key).Contains(m.id)).ToList();
//处理 回放记录状态
foreach (var item in data)
{
var lpb = mylpb.Where(m => m.playBackId == item.pbid);//
//没有审核记录
if (!lpb.Any()) item.auditStatus = LiveAuditStatus.;
else
{
var log = myLog.Where(m => lpb.Select(n => n.logId).Contains(m.id));//我的审核日志
if (log.Any(m => m.auditStatus == LiveAuditStatus.))
{
item.auditStatus = LiveAuditStatus.;
}
else if (log.Any(m => m.auditStatus == LiveAuditStatus.))
{
item.auditStatus = LiveAuditStatus.;
}
else
{
item.auditStatus = LiveAuditStatus.;
}
}
}
return new PageResult<LiveAuditPlayBack>(dto.PageIndex, dto.PageSize, total, data);
}
/// <summary>
/// 回放审核记录
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<PageResult<PlayBackAuditLog>> GetPlayBackAuditLogPage(SearchPlayBackAuditLogDto dto)
{
var deptments = await _cacheDomain.GetDeptments();
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
var query = from a in dncmsbaseRepository.GetRepository<LiveAuditLogPlayBack>().Query().Where(m => m.playBackId == dto.playBackId)
join b in dncmsbaseRepository.GetRepository<CMS_LiveAuditLog>().Query() on a.logId equals b.id into lapb
from pb in lapb.DefaultIfEmpty()
select new PlayBackAuditLog
{
playBackId = a.playBackId,
schedulesId = pb.schedulesId,
auditStatus = pb.auditStatus,
rejectReason = pb.rejectReason,
auditRemark = pb.auditRemark,
auditAccessory = pb.auditAccessory,
creater = pb.creater,
ctime = pb.ctime,
solver = pb.solver,
solveTime = pb.solveTime,
solveRemark = pb.solveRemark,
solveAccessory = pb.solveAccessory
};
var total = await query.CountAsync();
var data = await query.OrderByDescending(x => x.ctime)
.Skip((dto.PageIndex - 1) * dto.PageSize)
.Take(dto.PageSize)
.ToListAsync();
return new PageResult<PlayBackAuditLog>(dto.PageIndex, dto.PageSize, total, data);
}
public async Task AddLiveAuditLog(AddLiveAuditDto dto)
{
if (dto.AuditStatus != LiveAuditStatus. && dto.AuditStatus != LiveAuditStatus.)
{
throw new ApiException("只接受:违规和合规!");
}
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
var pbids = dto.playBackIds.Split(',').Select(x => int.Parse(x.Trim())).ToList();
if (!pbids.Any())
{
throw new ApiException("请选择审核录像!");
}
CMS_LiveAuditLog log = new CMS_LiveAuditLog();
log.auditRemark = dto.AuditRemark;
log.auditAccessory = dto.AuditAccessory;
log.auditStatus = LiveAuditStatus.;
log.rejectReason = dto.RejectReason;
log.schedulesId = dto.SchedulesId;
log.creater = dto.Operator;
log.ctime = DateTime.Now;
log.auditStatus = dto.AuditStatus;
using var transaction = await dncmsbaseRepository.BeginTransactionAsync();
try
{
await dncmsbaseRepository.GetRepository<CMS_LiveAuditLog>().InsertAsync(log);
List<LiveAuditLogPlayBack> pblist = pbids.Select(m => new LiveAuditLogPlayBack() { logId = log.id, playBackId = m }).ToList();
await dncmsbaseRepository.GetRepository<LiveAuditLogPlayBack>().BatchInsertAsync(pblist);
await transaction.CommitAsync();
}
catch (Exception)
{
await transaction.RollbackAsync();
await transaction.DisposeAsync();
throw;
}
}
public async Task SolveLiveAudit(SolveLiveAuditDto dto)
{
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
var data = await dncmsbaseRepository.GetRepository<CMS_LiveAuditLog>().Query()
.FirstOrDefaultAsync(x => x.id == dto.logId) ?? throw new ApiException("审核记录不存在或已删除!");
data.solveAccessory = dto.SolveAccessory;
data.solver = dto.Operator;
data.solveRemark = dto.SolveRemark;
data.solveTime = DateTime.Now;
data.auditStatus = LiveAuditStatus.;
using var transaction = await dncmsbaseRepository.BeginTransactionAsync();
try
{
await dncmsbaseRepository.GetRepository<CMS_LiveAuditLog>().UpdateAsync(data);
await transaction.CommitAsync();
}
catch (Exception)
{
await transaction.RollbackAsync();
await transaction.DisposeAsync();
throw;
}
}
private static string GetWeeksName(string weeks)
{
if (string.IsNullOrEmpty(weeks)) return "";
string str = "";
string[] s = Regex.Split(weeks, @",", RegexOptions.IgnoreCase);
for (int i = 0; i < s.Length; i++)
{
if (string.IsNullOrEmpty(s[i])) continue;
str += Enum.GetName(typeof(WeeksEnum), Convert.ToInt32(s[i])) + "";
}
return str;
}
#endregion 2
public async Task CreateLiveAuditRectification(CreateLiveAuditRectificationDto dto)
{
using var scope = _serviceProvider.CreateAsyncScope();
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
var data = await hgActionRepository.GetRepository<LiveAudit>().Query()
.FirstOrDefaultAsync(x => x.Id == dto.AuditId) ?? throw new ApiException("审核记录不存在或已删除!");
data.AuditStatus = LiveAuditStatus.;
var log = _mapper.Map<CreateLiveAuditRectificationDto, LiveAuditLog>(dto);
log.Ctime = DateTime.Now;
log.Schedule = data.Schedule;
log.StarTime = data.StarTime;
log.AuditStatus = data.AuditStatus;
log.LiveId = data.LiveId;
using var transaction = await hgActionRepository.BeginTransactionAsync();
try
{
await hgActionRepository.GetRepository<LiveAudit>().UpdateAsync(data, x => new
{
x.AuditStatus,
x.AuditTime,
x.Auditer
});
await hgActionRepository.GetRepository<LiveAuditLog>().InsertAsync(log);
await transaction.CommitAsync();
}
catch (Exception)
{
await transaction.RollbackAsync();
await transaction.DisposeAsync();
throw;
}
}
public List<SelectItem> GetRejectReasonSelect()
{
var result = new List<SelectItem>();
foreach (LiveAuditRejectReason item in Enum.GetValues(typeof(LiveAuditRejectReason)))
{
result.Add(new SelectItem(item, item.GetDescription()));
}
return result;
}
public List<SelectItem> GetAuditStatusSelect()
{
var result = new List<SelectItem>();
foreach (LiveAuditStatus item in Enum.GetValues(typeof(LiveAuditStatus)))
{
result.Add(new SelectItem(item, item.GetDescription()));
}
return result;
}
public async Task<List<SelectItem>> GetPlatformSelect()
{
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
var result = await dncmsbaseRepository.GetRepository<LivePlatform>().Query()
.Select(x => new SelectItem(x.Id, x.Platform))
.ToListAsync();
return result;
}
public async Task<LiveAuditDetailDto> GetLiveAuditDetail(int auditId)
{
using var scope = _serviceProvider.CreateAsyncScope();
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
var data = await hgActionRepository.GetRepository<LiveAudit>().Query()
.FirstOrDefaultAsync(x => x.Id == auditId) ?? throw new ApiException("审核记录不存在或已删除!");
var log = await hgActionRepository.GetRepository<LiveAuditLog>().Query()
.OrderByDescending(x => x.Ctime)
.FirstOrDefaultAsync(x => x.AuditId == auditId && x.AuditStatus == LiveAuditStatus.);
var result = new LiveAuditDetailDto()
{
AuditStatus = data.AuditStatus,
Id = auditId,
LiveRoomInfo = data.LiveRoomInfo,
LiveUrl = data.LiveUrl,
RectificationAccessory = log?.RectificationAccessory,
RectificationRemark = log?.RectificationRemark,
LiveDate = data.LiveDate,
ScheduleId = data.ScheduleId,
};
return result;
}
/// <summary>
/// 同步直播数据到审核数据
/// </summary>
/// <returns></returns>
public async Task SyncLiveData()
{
await CreateOrUpdateLiveInfo();
var now = DateTime.Now;
var date = now.ToString("yyyy-MM-dd");
var week = now.DayOfWeek.ToString("d");
using var scope = _serviceProvider.CreateAsyncScope();
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
var data = new List<LiveAudit>();
// 固化数据
if (!await hgActionRepository.GetRepository<LiveAudit>().Query().AnyAsync(x => x.LiveDate == date))
{
data = await (from a in dncmsbaseRepository.GetRepository<ZhiboSystemSchedules>().Query()
join b in dncmsbaseRepository.GetRepository<ZhiboSystemSchedulesToRoom>().Query() on a.Id equals b.Scheduleid into temAB
from b in temAB.DefaultIfEmpty()
join c in dncmsbaseRepository.GetRepository<ZhiboSystemRoom>().Query() on b.Roomid equals c.Id into temBC
from c in temBC.DefaultIfEmpty()
where a.Status == 1 && a.Weeks.Contains(week) && c.Status == 1
select new LiveAudit
{
Deptid = int.Parse(a.Dept),
LiveDate = date,
Ctime = now,
ScheduleId = a.Id,
Schedule = a.Title,
LiveId = c.Id,
Liver = a.Lecturer,
LiveRoomInfo = JsonHelper.ToJson(new { name = c.Title }),
StarTime = DateTime.Parse($"{date} {a.Starttime}"),
EndTime = DateTime.Parse($"{date} {a.Endtime}"),
LiveUrl = $"https://app.hc.dn8188.com/zhibo.html?roomid={c.Id}&agenttype=2"
}).ToListAsync();
await CreateLiveAudits(data, now);
return;
}
var source = await hgActionRepository.GetRepository<LiveAudit>().Query()
.Where(x => x.LiveDate == date)
.ToListAsync();
var liveids = source.Select(x => x.LiveId).ToList();
data = await (from a in dncmsbaseRepository.GetRepository<ZhiboSystemSchedules>().Query()
join b in dncmsbaseRepository.GetRepository<ZhiboSystemSchedulesToRoom>().Query() on a.Id equals b.Scheduleid into temAB
from b in temAB.DefaultIfEmpty()
join c in dncmsbaseRepository.GetRepository<ZhiboSystemRoom>().Query() on b.Roomid equals c.Id into temBC
from c in temBC.DefaultIfEmpty()
where a.Status == 1 && a.Weeks.Contains(week) && c.Status == 1
select new LiveAudit
{
Deptid = int.Parse(a.Dept),
LiveDate = date,
Ctime = now,
ScheduleId = a.Id,
Schedule = a.Title,
LiveId = c.Id,
Liver = a.Lecturer,
LiveRoomInfo = JsonHelper.ToJson(new { name = c.Title }),
StarTime = DateTime.Parse($"{date} {a.Starttime}"),
EndTime = DateTime.Parse($"{date} {a.Endtime}"),
LiveUrl = $"https://app.hc.dn8188.com/zhibo.html?roomid={c.Id}&agenttype=2"
}).ToListAsync();
// 差异新增
if (data.Any(x => !liveids.Contains(x.LiveId)))
{
await CreateLiveAudits(data.Where(x => !liveids.Contains(x.LiveId)).ToList(), now);
}
// 差异修改
if (data.Any(x => liveids.Contains(x.LiveId)))
{
await UpdateLiveAudits(data, source);
}
}
/// <summary>
/// 创建直播审核数据
/// </summary>
/// <param name="data"></param>
/// <param name="now"></param>
/// <returns></returns>
private async Task CreateLiveAudits(List<LiveAudit> data, DateTime now)
{
using var scope = _serviceProvider.CreateAsyncScope();
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
var lecturerids = GetLecturerids(data);
var lecturers = await dncmsbaseRepository.GetRepository<Lecturer>().Query()
.Where(x => lecturerids.Contains(x.Id))
.Select(x => new
{
x.Id,
x.Title
}).ToListAsync();
var liveids = data.Select(x => x.LiveId).Distinct().ToList();
var sourceData = await hgActionRepository.GetRepository<LiveAudit>().Query()
.Where(x => x.LiveDate == now.AddDays(-1).ToString("yyyy-MM-dd") && liveids.Contains(x.LiveId))
.Select(x => new
{
x.LiveId,
x.AuditStatus,
x.AuditTime,
x.Auditer
}).ToListAsync();
var platform = await dncmsbaseRepository.GetRepository<LivePlatform>().Query().FirstAsync();
foreach (var item in data)
{
var livers = item.Liver.Split(',').Select(x => int.Parse(x)).ToList();
item.Liver = string.Join(",", lecturers.Where(x => livers.Contains(x.Id)).Select(x => x.Title).ToList());
var source = sourceData.FirstOrDefault(x => x.LiveId == item.LiveId);
item.PlatformId = platform.Id;
item.Platform = platform.Platform;
item.AuditStatus = LiveAuditStatus.;
}
await hgActionRepository.GetRepository<LiveAudit>().BatchInsertAsync(data);
}
/// <summary>
/// 更新直播审核数据
/// </summary>
/// <param name="data"></param>
/// <param name="source"></param>
/// <returns></returns>
private async Task UpdateLiveAudits(List<LiveAudit> data, List<LiveAudit> source)
{
using var scope = _serviceProvider.CreateAsyncScope();
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
var lecturerids = GetLecturerids(data);
var lecturers = await dncmsbaseRepository.GetRepository<Lecturer>().Query()
.Where(x => lecturerids.Contains(x.Id))
.Select(x => new
{
x.Id,
x.Title
}).ToListAsync();
foreach (var item in data)
{
var livers = item.Liver.Split(',').Select(x => int.Parse(x)).ToList();
item.Liver = string.Join(",", lecturers.Where(x => livers.Contains(x.Id)).Select(x => x.Title).ToList());
}
var editData = new List<LiveAudit>();
foreach (var sourceItem in source)
{
var item = data.FirstOrDefault(x => x.LiveId == sourceItem.LiveId);
if (item != null && (sourceItem.EndTime != item.EndTime
|| sourceItem.StarTime != item.StarTime
|| sourceItem.Liver != item.Liver
|| sourceItem.LiveRoomInfo != item.LiveRoomInfo))
{
item.StarTime = sourceItem.StarTime;
item.EndTime = sourceItem.EndTime;
item.Liver = sourceItem.Liver;
item.LiveRoomInfo = sourceItem.LiveRoomInfo;
editData.Add(item);
}
}
// 差异修改
if (editData.Any())
{
await hgActionRepository.GetRepository<LiveAudit>().BatchUpdateAsync(editData, x => new
{
x.StarTime,
x.EndTime,
x.Liver,
x.LiveRoomInfo
});
}
}
/// <summary>
/// 获取老师id
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private static List<int> GetLecturerids(List<LiveAudit> data)
{
var lecturerids = new List<int>();
var lectureridList = data.Select(y => y.Liver.Split(',').Select(x => int.Parse(x)).ToList()).ToList();
foreach (var lecturerid in lectureridList)
{
lecturerids.AddRange(lecturerid);
}
lecturerids = lecturerids.Distinct().ToList();
return lecturerids;
}
/// <summary>
/// 同步直播数据
/// </summary>
/// <returns></returns>
private async Task CreateOrUpdateLiveInfo()
{
using var scope = _serviceProvider.CreateAsyncScope();
var hgActionRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<HgAtionDbContext>>();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
var platform = await dncmsbaseRepository.GetRepository<LivePlatform>().Query().FirstAsync();
var lives = await hgActionRepository.GetRepository<LiveInfo>().Query().ToListAsync();
var query = from a in dncmsbaseRepository.GetRepository<ZhiboSystemSchedules>().Query()
join b in dncmsbaseRepository.GetRepository<ZhiboSystemSchedulesToRoom>().Query() on a.Id equals b.Scheduleid into temAB
from b in temAB.DefaultIfEmpty()
join c in dncmsbaseRepository.GetRepository<ZhiboSystemRoom>().Query() on b.Roomid equals c.Id into temBC
from c in temBC.DefaultIfEmpty()
where a.Status == 1 && c.Status == 1
select new LiveInfo
{
LiveId = c.Id,
Deptid = int.Parse(a.Dept),
ScheduleId = a.Id,
Schedule = a.Title,
Liver = a.Lecturer,
LiveUrl = $"https://app.hc.dn8188.com/zhibo.html?roomid={c.Id}&agenttype=2",
PlatformId = platform.Id,
Platform = platform.Platform,
LiveTitle = c.Title,
Ctime = DateTime.Now,
};
var data = await query.If(lives.Any(), x => x.Where(x => !lives.Select(y => y.LiveId).Contains(x.LiveId)))
.ToListAsync();
if (data.Any())
{
await hgActionRepository.GetRepository<LiveInfo>().BatchInsertAsync(data);
}
}
}
}

View File

@ -0,0 +1,8 @@
namespace Hg.Complaint.WebApi.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class BaseController : ControllerBase
{
}
}

View File

@ -0,0 +1,155 @@
using System.ComponentModel.DataAnnotations;
namespace Hg.Complaint.WebApi.Controllers
{
/// <summary>
/// 投诉处理
/// </summary>
[ApiSignatureFilterForbid]
public class ComplaintController : BaseController
{
private readonly IComplaintDomain _complaintDomain;
/// <summary>
/// 投诉处理
/// </summary>
/// <param name="complaintDomain"></param>
public ComplaintController(IComplaintDomain complaintDomain)
{
_complaintDomain = complaintDomain;
}
/// <summary>
/// 投诉申请接口
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("apply")]
public async Task<bool> ApplyComplaint([FromBody] ApplyComplaintDto dto)
{
return await _complaintDomain.ApplyComplaint(dto);
}
/// <summary>
/// 获取队列信息
/// </summary>
/// <returns></returns>
[HttpGet("Queues")]
public async Task<List<ComplaintLogMessageDto>> GetQueues()
=> await _complaintDomain.GetQueues();
/// <summary>
/// 获取投诉申请分页
/// </summary>
/// <param name="dto"></param>
/// <param name="appid"></param>
/// <returns></returns>
[HttpGet("Page")]
public async Task<PageResult<ComplaintDto>> GetPage([FromQuery] SearchComplaintDto dto, [FromHeader] string? appid)
=> await _complaintDomain.GetPage(dto, appid);
/// <summary>
/// 重新发起队列
/// </summary>
/// <returns></returns>
[HttpGet("SyncQueue")]
public async Task<List<ComplaintLogMessageDto>> SyncQueue()
=> await _complaintDomain.SyncQueue();
/// <summary>
/// 获取投诉详情
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet("Detail")]
public async Task<ComplaintDetailDto> GetComplaintDetail(int id)
=> await _complaintDomain.GetComplaintDetail(id);
/// <summary>
/// 根据资源id和业务线获取投诉详情
/// </summary>
/// <param name="resid"></param>
/// <param name="deptid"></param>
/// <returns></returns>
[HttpGet("DetailByRes")]
public async Task<ComplaintDetailDto> GetComplaintDetail(string? resid, int deptid)
=> await _complaintDomain.GetComplaintDetail(resid, deptid);
/// <summary>
/// 根据appid和业务线获取投诉详情
/// </summary>
/// <param name="appid"></param>
/// <param name="appuserid"></param>
/// <param name="deptid"></param>
/// <returns></returns>
[HttpGet("DetailByAppid")]
public async Task<ComplaintDetailDto> GetComplaintDetail(string? appid, string? appuserid, int deptid)
=> await _complaintDomain.GetComplaintDetail(appid, appuserid, deptid);
/// <summary>
/// 更新投诉状态
/// </summary>
/// <param name="dto"></param>
/// <param name="appid"></param>
/// <returns></returns>
[HttpPost("Status")]
public async Task<bool> UpdateComplaintStatus([FromBody] UpdateComplaintStatusDto dto, [FromHeader] string? appid)
=> await _complaintDomain.UpdateComplaintStatus(dto, appid);
/// <summary>
/// 获取投诉状态
/// </summary>
/// <param name="udid"></param>
/// <returns></returns>
[HttpGet("StatusDetail")]
public async Task<ComplainStatusModel> GetComplaintStatus(int udid)
{
return await _complaintDomain.GetComplaintStatus(udid);
}
/// <summary>
/// 标记客户
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("MarkCustomer")]
public async Task<bool> MarkCustomer([FromBody] MarkCustomerDto dto)
{
return await _complaintDomain.MarkCustomer(dto);
}
/// <summary>
/// 批量分配客户给客服
/// </summary>
/// <param name="dto"></param>
/// <param name="appid"></param>
/// <returns></returns>
[HttpPost("BatchAssign")]
public async Task<bool> BatchAssignComplaint([FromBody] BatchAssignComplaintDto dto, [FromHeader] string? appid)
{
return await _complaintDomain.BatchAssignComplaint(dto, appid);
}
/// <summary>
/// 标记水军
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("MarkTrolls")]
public async Task<bool> MarkTrolls([FromBody] MarkTrollsDto dto)
{
return await _complaintDomain.MarkTrolls(dto);
}
/// <summary>
/// 获取潜在风险用户标签
/// </summary>
/// <param name="resId"></param>
/// <returns></returns>
[HttpGet("Label")]
public async Task<int> ComplaintLabel([Required(ErrorMessage = "请输入客户ID")] string resId)
{
return await _complaintDomain.ComplaintLabel(resId);
}
}
}

View File

@ -0,0 +1,172 @@
using System.ComponentModel.DataAnnotations;
namespace Hg.Complaint.WebApi.Controllers
{
/// <summary>
/// 直播审核
/// </summary>
[ApiSignatureFilterForbid]
public class LiveAuditController : BaseController
{
private readonly ILiveAuditDomain _liveAuditDomain;
#region 1.0
/// <summary>
/// 直播审核
/// </summary>
/// <param name="liveAuditDomain"></param>
public LiveAuditController(ILiveAuditDomain liveAuditDomain)
{
_liveAuditDomain = liveAuditDomain;
}
/// <summary>
/// 直播审核统计分页
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpGet("live/page")]
public async Task<PageResult<LiveInfoDto>> GetLivePage([FromQuery] SearchLiveDto dto)
{
return await _liveAuditDomain.GetLivePage(dto);
}
/// <summary>
/// 直播审核
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpGet("page")]
public async Task<PageResult<LiveAuditDto>> GetLiveAuditPage([FromQuery] SearchLiveAuditDto dto)
{
return await _liveAuditDomain.GetLiveAuditPage(dto);
}
/// <summary>
/// 直播审核日志
/// </summary>
/// <param name="auditId"></param>
/// <param name="date"></param>
/// <returns></returns>
[HttpGet("logs")]
public async Task<List<LiveAuditLogDto>> GetLiveAuditLogs([Required] int auditId, [Required] string? date)
{
return await _liveAuditDomain.GetLiveAuditLogs(auditId, date);
}
/// <summary>
/// 添加审核记录
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("audit")]
public async Task CreateLiveAudit([FromBody] CreateLiveAuditDto dto)
{
await _liveAuditDomain.CreateLiveAudit(dto);
}
/// <summary>
/// 添加整改记录
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("rectification")]
public async Task CreateLiveAuditRectification([FromBody] CreateLiveAuditRectificationDto dto)
{
await _liveAuditDomain.CreateLiveAuditRectification(dto);
}
/// <summary>
/// 驳回理由选项
/// </summary>
/// <returns></returns>
[HttpGet("select/reason")]
public List<SelectItem> GetRejectReasonSelect()
{
return _liveAuditDomain.GetRejectReasonSelect();
}
/// <summary>
/// 审核状态选项
/// </summary>
/// <returns></returns>
[HttpGet("select/status")]
public List<SelectItem> GetAuditStatusSelect()
{
return _liveAuditDomain.GetAuditStatusSelect();
}
/// <summary>
/// 获取直播平台选项
/// </summary>
/// <returns></returns>
[HttpGet("select/platform")]
public async Task<List<SelectItem>> GetPlatformSelect()
{
return await _liveAuditDomain.GetPlatformSelect();
}
/// <summary>
/// 获取审核明细
/// </summary>
/// <param name="auditId"></param>
/// <returns></returns>
[HttpGet("detail")]
public async Task<LiveAuditDetailDto> GetLiveAuditDetail([Required] int auditId)
{
return await _liveAuditDomain.GetLiveAuditDetail(auditId);
}
#endregion
/// <summary>
/// 直播审核统计分页
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpGet("live/schedulePage")]
public async Task<PageResult<LiveAuditSchedules>> GetLiveSchedulePage([FromQuery] SearchLiveScheduleDto dto)
{
return await _liveAuditDomain.GetLiveSchedulePage(dto);
}
/// <summary>
/// 添加违规记录
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("addAudit")]
public async Task AddAudit([FromBody] AddLiveAuditDto dto)
{
await _liveAuditDomain.AddLiveAuditLog(dto);
}
/// <summary>
/// 解决违规
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("solveAudit")]
public async Task SolveAudit([FromBody] SolveLiveAuditDto dto)
{
await _liveAuditDomain.SolveLiveAudit(dto);
}
/// <summary>
/// 直播回放
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpGet("live/playBackPage")]
public async Task<PageResult<LiveAuditPlayBack>> GetLivePlayBackPage([FromQuery] SearchLivePlayBackDto dto)
{
return await _liveAuditDomain.GetLivePlayBackPage(dto);
}
/// <summary>
/// 回放审核记录
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpGet("live/playBacklogPage")]
public async Task<PageResult<PlayBackAuditLog>> GetBackAuditLogPage([FromQuery] SearchPlayBackAuditLogDto dto)
{
return await _liveAuditDomain.GetPlayBackAuditLogPage(dto);
}
}
}

View File

@ -0,0 +1,41 @@
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
#设置时间为中国上海
ENV TZ=Asia/Shanghai
ENV DEBIAN_FRONTEND noninteractive
# 设置包源为阿里
RUN sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list \
&& apt-get clean
# 安装 tzdata 软件包
RUN apt-get update \
&& apt-get install -y tzdata \
&& ln -fs /usr/share/zoneinfo/$TZ /etc/localtime \
&& rm -rf /var/lib/apt/lists/ \
&& dpkg-reconfigure -f noninteractive tzdata
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["Hg.Complaint.WebApi/Hg.Complaint.WebApi.csproj", "Hg.Complaint.WebApi/"]
COPY ["Hg.Complaint.Domain/Hg.Complaint.Domain.csproj", "Hg.Complaint.Domain/"]
COPY ["Hg.Core.EntityFramework/Hg.Core.EntityFramework.csproj", "Hg.Core.EntityFramework/"]
COPY ["Hg.Core.Entity/Hg.Core.Entity.csproj", "Hg.Core.Entity/"]
COPY ["Hg.Core.Shared/Hg.Core.Shared.csproj", "Hg.Core.Shared/"]
RUN dotnet restore "Hg.Complaint.WebApi/Hg.Complaint.WebApi.csproj"
COPY . .
WORKDIR "/src/Hg.Complaint.WebApi"
RUN dotnet build "Hg.Complaint.WebApi.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "Hg.Complaint.WebApi.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Hg.Complaint.WebApi.dll"]

View File

@ -0,0 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>24a88d5e-8b6a-403a-a290-9c54069b6af2</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<_ContentIncludedByDefault Remove="appsettings.Production.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Hg.Complaint.Domain\Hg.Complaint.Domain.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="DG.EntityFramework" />
<Using Include="DG.Core" />
<Using Include="DG.Redis" />
<Using Include="Hg.Complaint.Domain" />
<Using Include="Hg.Complaint.Domain.Impl" />
<Using Include="Hg.Core.EntityFramework" />
<Using Include="Microsoft.EntityFrameworkCore" />
<Using Include="System.Text.Json" />
<Using Include="Serilog" />
<Using Include="Microsoft.AspNetCore.Mvc" />
<Using Include="Hg.Complaint.Domain.Config" />
<Using Include="Exceptionless" />
<Using Include="Hg.Complaint.Domain.Dto" />
<Using Include="Hg.Complaint.WebApi.Workers" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
</Project>

View File

@ -0,0 +1,124 @@
using Hg.Complaint.WebApi.workers;
using Microsoft.AspNetCore.ResponseCompression;
try
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("Serilog.json")
.AddJsonFile($"Serilog.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", true)
.Build();
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.WriteTo.Exceptionless(builder.Configuration.GetValue<string>("Exceptionless:ApiKey"), builder.Configuration.GetValue<string>("Exceptionless:ServerUrl"), new string[] { "hg-complaint-webapi" })
.CreateLogger();
Log.Logger = logger;
builder.Services.AddLogging(logging =>
{
logging.ClearProviders();
logging.AddSerilog(logger);
});
// Add services to the container.
builder.Services.AddDGEntityFramework<ZxdDbContext>(options =>
{
options.UseMySql(builder.Configuration.GetConnectionString("zxdcrm"), ServerVersion.AutoDetect(builder.Configuration.GetConnectionString("zxdcrm")));
});
builder.Services.AddDGEntityFramework<CrmDbContext>(options =>
{
options.UseMySql(builder.Configuration.GetConnectionString("crm"), ServerVersion.AutoDetect(builder.Configuration.GetConnectionString("crm")));
});
builder.Services.AddDGEntityFramework<HgAtionDbContext>(options =>
{
options.UseMySql(builder.Configuration.GetConnectionString("hgaction"), ServerVersion.AutoDetect(builder.Configuration.GetConnectionString("hgaction")));
});
builder.Services.AddDGEntityFramework<DncmsbaseDbContext>(options =>
{
options.UseMySql(builder.Configuration.GetConnectionString("dncmsbase"), ServerVersion.AutoDetect(builder.Configuration.GetConnectionString("dncmsbase")));
});
builder.Services.AddDGEntityFramework<UserCenterDbContext>(options =>
{
options.UseMySql(builder.Configuration.GetConnectionString("usercenter"), ServerVersion.AutoDetect(builder.Configuration.GetConnectionString("usercenter")));
});
builder.Services.AddRedis(builder.Configuration);
builder.Services
.AddAutoIoc(typeof(IScopedDependency), LifeCycle.Scoped)
.AddAutoIoc(typeof(ISingletonDependency), LifeCycle.Singleton)
.AddAutoIoc(typeof(ITransientDependency), LifeCycle.Transient)
.AddMapper();
builder.Services.AddCors(option =>
{
option.AddPolicy(MyAllowSpecificOrigins,
policy =>
{
policy.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddExceptionless(builder.Configuration);
builder.Services.AddResponseCompression(opts => //添加压缩中间件服务
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/octet-stream" });
})
.AddControllers()
.AddApiResult()
.AddApiSignature()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new JsonOptionsExtensions());
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
{
Version = "v1",
Title = "HG COMPLAINT API",
Description = "HG COMPLAINT API"
});
var xmlFilename = $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml";
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "Hg.Complaint.Domain.xml"));
});
builder.Services.AddDGHttpClient();
//if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") != "PreProduction")
//{
// builder.Services.AddHostedService<ComplaintMessageWorker>();
// builder.Services.AddHostedService<NegativeMessageWorker>();
//}
builder.Services.AddSingleton<ComplaintEventSingleton>();
builder.Services.AddHostedService<DueComplaintWorker>();
builder.Services.AddHostedService<ComplaintMessageWorker>();
builder.Services.AddHostedService<NegativeMessageWorker>();
builder.Services.AddHostedService<LiveDataWorker>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment() || Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "PreProduction")
{
app.UseSwagger();
app.UseSwaggerUI();
//app.UseHttpLogging();
}
app.UseCors(MyAllowSpecificOrigins);
app.UseHttpsRedirection();
app.UseExceptionless();
app.UseAuthorization();
app.MapControllers();
app.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}

View File

@ -0,0 +1,41 @@
{
"profiles": {
"Hg.Complaint.WebApi": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "https://localhost:7263;http://localhost:5174"
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
"environmentVariables": {},
"publishAllPorts": true,
"useSSL": true,
"httpPort": 5261,
"sslPort": 5262
}
},
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:43953",
"sslPort": 44321
}
}
}

View File

@ -0,0 +1,33 @@
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File", "Serilog.AspNetCore" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Information",
"Microsoft.EntityFrameworkCore": "Information",
"System.Net.Http.HttpClient": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"path": "logs/log.log",
"rollingInterval": "3",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
}

View File

@ -0,0 +1,33 @@
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File", "Serilog.AspNetCore" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Information",
"Microsoft.EntityFrameworkCore": "Information",
"System.Net.Http.HttpClient": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"path": "logs/log.log",
"rollingInterval": "3",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}",
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
}

View File

@ -0,0 +1,162 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"zxdcrm": "Data Source=mysql98ff96c3dffa.rds.ivolces.com;Port=3306;Initial Catalog=zxdcrm;user id=qianbenjie;password=Hcqianbenjie@123;Old Guids=true;SslMode=None",
"crm": "Data Source=mysql98ff96c3dffa.rds.ivolces.com;Port=3306;Initial Catalog=db_crm;user id=qianbenjie;password=Hcqianbenjie@123;Old Guids=true;SslMode=None",
"dncmsbase": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=dncmsbase;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None",
"hgaction": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=hgaction;user id=hguser;password=nH5L$&Hxxco;SslMode=None",
"usercenter": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=usercenter;user id=hguser;password=nH5L$&Hxxco;SslMode=None"
},
"AllowedHosts": "*",
"Exceptionless": {
"ServerUrl": "http://10.22.11.9:5000",
"ApiKey": "3j2K4Vew67kXOjwuZN2C5KG3Ca5SLo6YSZ9fWVJI"
},
"Consumers": [
{
"Host": "172.18.11.77:9092",
"GroupId": "crm",
"Topic": "dwd-negative_kmessage"
},
{
"Host": "172.18.11.76:9092",
"GroupId": "crm",
"Topic": "dwd-negative_kmessage"
},
{
"Host": "172.18.11.75:9092",
"GroupId": "crm",
"Topic": "dwd-negative_kmessage"
}
],
"SystemConfig": {
"Appid": "qt_compliance",
"AppSecret": "AdC+vVj58wG48swyjyW2WM6aC6CxYNKt8pP0c8sdozs=",
"SsoUrl": "http://conf.soft.dn8188.com",
"ZxdUrl": "http://centerapi.soft.dn8188.com",
"ZxdCoreUrl": "http://120.77.165.155:8089",
"CrmUrl": "http://api.crm.tcfortune.com:8282",
"SsoOrganizationUrl": "/v1/api/open/data/sync/organization",
"UserCenterRiaServiceUrl": "https://contract.soft.dn8188.com",
"PreviewUrl": "/v1/api/certification/idcard/image/preview/",
"AuditUrl": "/v1/api/certification/idcard/image/audit",
"Riskinfo": "/v1/api/h5/get_riskinfo",
"ContractStatus": "/v1/api/contract/update/contract/status",
"ClientKey": [
{
"Id": "UPWEBSITE",
"Name": "UP网站",
"AccessKey": "1622a92d"
},
{
"Id": "TDORDERSITE",
"Name": "订单接口",
"AccessKey": "622a92d1"
},
{
"Id": "UPPRODUCT",
"Name": "UP产品端",
"AccessKey": "c268a2cd"
},
{
"Id": "gd_crm",
"Name": "UPCRM",
"AccessKey": "upchina."
},
{
"Id": "nj_crm",
"Name": "UPCRM",
"AccessKey": "pchina.1"
},
{
"Id": "AYCRM2_CTI",
"Name": "CTI",
"AccessKey": "ac910f51"
}
],
"ClearCacheUrls": [
"http://hg.soft.dn8188.com/cache/clear"
],
"CompanyList": [
{
"Code": "QBJZ",
"Name": "北京一部",
"VideoUrl": "http://bj1rec.crm.tcfortune.com:8282",
"WxMessageUrl": "http://1.202.216.226:909"
},
{
"Code": "QBJX",
"Name": "北京二部",
"VideoUrl": "http://bj2rec.crm.tcfortune.com:8282",
"WxMessageUrl": ""
},
{
"Code": "DNZZ",
"Name": "东方软件一、二中心",
"VideoUrl": "http://120.238.224.24:8098",
"WxMessageUrl": "http://120.236.219.28:909"
},
{
"Code": "DNYY",
"Name": "东方软件三中心",
"VideoUrl": "http://120.238.224.24:38080",
"WxMessageUrl": "http://192.168.11.216:909"
},
{
"Code": "SHZZ",
"Name": "首华",
"VideoUrl": "http://120.238.224.24:28080",
"WxMessageUrl": "http://120.238.224.24:4906"
},
{
"Code": "DNG8",
"Name": "投顾业务中心",
"VideoUrl": "http://120.238.224.24:2080",
"WxMessageUrl": "http://120.238.224.24:2909"
},
{
"Code": "DNBB",
"Name": "内容二、三中心",
"VideoUrl": "",
"WxMessageUrl": "http://192.168.11.226:909"
},
{
"Code": "PTD2",
"Name": "平台2",
"VideoUrl": "http://10.22.11.122",
"WxMessageUrl": ""
},
{
"Code": "HGBM",
"Name": "合规",
"VideoUrl": "http://120.238.224.24:2332",
"WxMessageUrl": "http://192.168.11.66:909"
}
]
},
"SignConfig": {
"AppId": "qt_compliance",
"Secret": "AdC+vVj58wG48swyjyW2WM6aC6CxYNKt8pP0c8sdozs="
},
"Redis": [
{
"Name": "Hg",
"HostName": "redis-cngzdnddztrevahsz.redis.ivolces.com",
"Port": "6379",
"Password": "dn8sCe@mxTvzx",
"Defaultdatabase": "0"
},
{
"Name": "UserCenter",
"HostName": "redis-cngzdnddztrevahsz.redis.ivolces.com",
"Port": "6379",
"Password": "dn8sCe@mxTvzx",
"Defaultdatabase": "0"
}
]
}

View File

@ -0,0 +1,162 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"zxdcrm": "Server=192.168.11.141;Database=zxdcrm;UserId=tafadmin;Password=tafadmin2017;port=3306;",
"crm": "Server=192.168.11.141;Database=db_crm;UserId=tafadmin;Password=tafadmin2017;port=3306;",
"dncmsbase": "Server=192.168.11.41;Database=dncmsbase;UserId=root;Password=sa123456.;port=3306;",
"hgaction": "Server=192.168.11.41;Database=hgaction;UserId=root;Password=sa123456.;port=3306;",
"usercenter": "Server=192.168.11.41;Database=usercenter;UserId=tafadmin;Password=tafadmin2017;port=3306;"
},
"AllowedHosts": "*",
"Exceptionless": {
"ServerUrl": "http://10.22.12.9:5000",
"ApiKey": "BWuxVItUb5KaBwi6AQZ0RFZlRNtiSTWw2p7bjqx0"
},
"Consumers": [
{
"Host": "192.168.11.100:9092",
"GroupId": "crm",
"Topic": "dwd-negative_kmessage"
},
{
"Host": "192.168.11.101:9092",
"GroupId": "crm",
"Topic": "dwd-negative_kmessage"
},
{
"Host": "192.168.11.104:9092",
"GroupId": "crm",
"Topic": "dwd-negative_kmessage"
}
],
"SystemConfig": {
"Appid": "qt_compliance",
"AppSecret": "CqlNBxRof0yl7eJM1f4IbOBhgribfooZJ1zwuIj5NzQ=",
"SsoUrl": "http://192.168.11.141:24434",
"SsoOrganizationUrl": "/v1/api/open/data/sync/organization",
"UserCenterRiaServiceUrl": "https://certcontract.wantest.tcfortune.com:11188",
"ZxdUrl": "http://192.168.11.81:8087",
"ZxdCoreUrl": "http://192.168.11.81:8089",
"CrmUrl": "http://192.168.11.81:8088",
"PreviewUrl": "/v1/api/certification/idcard/image/preview/",
"AuditUrl": "/v1/api/certification/idcard/image/audit",
"Riskinfo": "/v1/api/h5/get_riskinfo",
"ContractStatus": "/v1/api/contract/update/contract/status",
"ClientKey": [
{
"Id": "UPWEBSITE",
"Name": "UP网站",
"AccessKey": "1622a92d"
},
{
"Id": "TDORDERSITE",
"Name": "订单接口",
"AccessKey": "622a92d1"
},
{
"Id": "UPPRODUCT",
"Name": "UP产品端",
"AccessKey": "c268a2cd"
},
{
"Id": "gd_crm",
"Name": "UPCRM",
"AccessKey": "upchina."
},
{
"Id": "nj_crm",
"Name": "UPCRM",
"AccessKey": "pchina.1"
},
{
"Id": "AYCRM2_CTI",
"Name": "CTI",
"AccessKey": "ac910f51"
}
],
"ClearCacheUrls": [
"http://192.168.11.46:28099/cache/clear"
],
"CompanyList": [
{
"Code": "QBJZ",
"Name": "北京一部",
"VideoUrl": "http://bj1rec.crm.tcfortune.com:8282",
"WxMessageUrl": "http://1.202.216.226:909"
},
{
"Code": "QBJX",
"Name": "北京二部",
"VideoUrl": "http://bj2rec.crm.tcfortune.com:8282",
"WxMessageUrl": ""
},
{
"Code": "DNZZ",
"Name": "东方软件一、二中心",
"VideoUrl": "http://120.238.224.24:8098",
"WxMessageUrl": "http://120.236.219.28:909"
},
{
"Code": "DNYY",
"Name": "东方软件三中心",
"VideoUrl": "http://120.238.224.24:38080",
"WxMessageUrl": "http://192.168.11.216:909"
},
{
"Code": "SHZZ",
"Name": "首华",
"VideoUrl": "http://120.238.224.24:28080",
"WxMessageUrl": "http://120.238.224.24:4906"
},
{
"Code": "DNG8",
"Name": "投顾业务中心",
"VideoUrl": "http://120.238.224.24:2080",
"WxMessageUrl": "http://120.238.224.24:2909"
},
{
"Code": "DNBB",
"Name": "内容二、三中心",
"VideoUrl": "",
"WxMessageUrl": "http://192.168.11.226:909"
},
{
"Code": "PTD2",
"Name": "平台2",
"VideoUrl": "http://10.22.11.122",
"WxMessageUrl": ""
},
{
"Code": "HGBM",
"Name": "合规",
"VideoUrl": "http://120.238.224.24:2332",
"WxMessageUrl": "http://192.168.11.66:909"
}
]
},
"SignConfig": {
"AppId": "qt_compliance",
"Secret": "CqlNBxRof0yl7eJM1f4IbOBhgribfooZJ1zwuIj5NzQ="
},
"Redis": [
{
"Name": "Hg",
"HostName": "192.168.11.81",
"Port": "6379",
"Password": "Abc@123456",
"Defaultdatabase": "1"
},
{
"Name": "UserCenter",
"HostName": "192.168.11.103",
"Port": "6379",
"Password": "123",
"Defaultdatabase": "0"
}
]
}

View File

@ -0,0 +1,38 @@
namespace Hg.Complaint.WebApi.Workers
{
internal class ComplaintMessageWorker : BackgroundService
{
private readonly IServiceProvider _serviceProvider;
public ComplaintMessageWorker(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var scope = _serviceProvider.CreateScope();
var complaintDomain = scope.ServiceProvider.GetRequiredService<IComplaintDomain>();
while (true)
{
try
{
var queue = await complaintDomain.GetQueue();
if (queue > 0)
{
var dto = await complaintDomain.DequeueQueue();
if (dto != null && dto.Id > 0)
{
await complaintDomain.AnalyseComplaintLog(dto);
}
}
}
catch (Exception ex)
{
Log.Error(ex, "分析投诉任务报错!");
}
await Task.Delay(500, stoppingToken);
}
}
}
}

View File

@ -0,0 +1,44 @@
namespace Hg.Complaint.WebApi.workers
{
internal class DueComplaintWorker : BackgroundService
{
private readonly IServiceProvider _serviceProvider;
public DueComplaintWorker(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var firstTime = true;
var scope = _serviceProvider.CreateScope();
var complaintDomain = scope.ServiceProvider.GetRequiredService<IComplaintDomain>();
try
{
//await complaintDomain.SyncResid();
while (true)
{
try
{
if (firstTime)
{
await complaintDomain.InitOverdueComplaint();
}
await complaintDomain.OverdueComplaint();
}
catch (Exception ex)
{
Log.Error(ex, "分析投诉任务报错!");
}
await Task.Delay(60 * 1000, stoppingToken);
}
}
catch (Exception ex)
{
Log.Error(ex, "分析投诉任务报错!");
}
}
}
}

View File

@ -0,0 +1,31 @@
namespace Hg.Complaint.WebApi.workers
{
internal class LiveDataWorker : BackgroundService
{
private readonly IServiceProvider _serviceProvider;
public LiveDataWorker(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var scope = _serviceProvider.CreateScope();
var liveAuditDomain = scope.ServiceProvider.GetRequiredService<ILiveAuditDomain>();
while (true)
{
try
{
Log.Information("直播数据分析任务");
await liveAuditDomain.SyncLiveData();
}
catch (Exception ex)
{
Log.Error(ex, "直播数据分析任务报错!");
}
await Task.Delay(5 * 60 * 1000, stoppingToken);
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More