commit a9c888e9e763e1c0586fb56e544300d5c91f0fe0
Author: zhuxiaojiong <645680426@qq.com>
Date: Sat Jun 28 11:01:51 2025 +0800
init
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..1ff0c42
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,63 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs diff=csharp
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln merge=binary
+#*.csproj merge=binary
+#*.vbproj merge=binary
+#*.vcxproj merge=binary
+#*.vcproj merge=binary
+#*.dbproj merge=binary
+#*.fsproj merge=binary
+#*.lsproj merge=binary
+#*.wixproj merge=binary
+#*.modelproj merge=binary
+#*.sqlproj merge=binary
+#*.wwaproj merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg binary
+#*.png binary
+#*.gif binary
+
+###############################################################################
+# diff behavior for common document formats
+#
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the
+# entries below.
+###############################################################################
+#*.doc diff=astextplain
+#*.DOC diff=astextplain
+#*.docx diff=astextplain
+#*.DOCX diff=astextplain
+#*.dot diff=astextplain
+#*.DOT diff=astextplain
+#*.pdf diff=astextplain
+#*.PDF diff=astextplain
+#*.rtf diff=astextplain
+#*.RTF diff=astextplain
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9491a2f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,363 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Oo]ut/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# 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
+# Note: 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
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# 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
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# 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
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# 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
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
+
+# Fody - auto-generated XML schema
+FodyWeavers.xsd
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..64ed695
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# DG.DotNet
+
diff --git a/dg.dotnet/.dockerignore b/dg.dotnet/.dockerignore
new file mode 100644
index 0000000..3729ff0
--- /dev/null
+++ b/dg.dotnet/.dockerignore
@@ -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
\ No newline at end of file
diff --git a/dg.dotnet/DG.Core/AutoIoc/LifeCycle.cs b/dg.dotnet/DG.Core/AutoIoc/LifeCycle.cs
new file mode 100644
index 0000000..55176aa
--- /dev/null
+++ b/dg.dotnet/DG.Core/AutoIoc/LifeCycle.cs
@@ -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,
+ }
+}
diff --git a/dg.dotnet/DG.Core/DG.Core.csproj b/dg.dotnet/DG.Core/DG.Core.csproj
new file mode 100644
index 0000000..2ce593f
--- /dev/null
+++ b/dg.dotnet/DG.Core/DG.Core.csproj
@@ -0,0 +1,16 @@
+
+
+
+ net6.0
+ enable
+ enable
+ True
+ 1.1.6
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dg.dotnet/DG.Core/Dependency/IScopedDependency.cs b/dg.dotnet/DG.Core/Dependency/IScopedDependency.cs
new file mode 100644
index 0000000..33f3fff
--- /dev/null
+++ b/dg.dotnet/DG.Core/Dependency/IScopedDependency.cs
@@ -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
+ {
+ }
+}
diff --git a/dg.dotnet/DG.Core/Dependency/ISingletonDependency.cs b/dg.dotnet/DG.Core/Dependency/ISingletonDependency.cs
new file mode 100644
index 0000000..762d8b7
--- /dev/null
+++ b/dg.dotnet/DG.Core/Dependency/ISingletonDependency.cs
@@ -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
+ {
+ }
+}
diff --git a/dg.dotnet/DG.Core/Dependency/ITransientDependency.cs b/dg.dotnet/DG.Core/Dependency/ITransientDependency.cs
new file mode 100644
index 0000000..d76b34e
--- /dev/null
+++ b/dg.dotnet/DG.Core/Dependency/ITransientDependency.cs
@@ -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
+ {
+ }
+}
diff --git a/dg.dotnet/DG.Core/Exceptions/ApiException.cs b/dg.dotnet/DG.Core/Exceptions/ApiException.cs
new file mode 100644
index 0000000..5e5e370
--- /dev/null
+++ b/dg.dotnet/DG.Core/Exceptions/ApiException.cs
@@ -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);
+ }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Exceptions/DGException.cs b/dg.dotnet/DG.Core/Exceptions/DGException.cs
new file mode 100644
index 0000000..3015f72
--- /dev/null
+++ b/dg.dotnet/DG.Core/Exceptions/DGException.cs
@@ -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);
+ }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Extensions/HttpExtensions.cs b/dg.dotnet/DG.Core/Extensions/HttpExtensions.cs
new file mode 100644
index 0000000..706209b
--- /dev/null
+++ b/dg.dotnet/DG.Core/Extensions/HttpExtensions.cs
@@ -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;
+ }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Extensions/JsonOptionsExtensions.cs b/dg.dotnet/DG.Core/Extensions/JsonOptionsExtensions.cs
new file mode 100644
index 0000000..19c66b5
--- /dev/null
+++ b/dg.dotnet/DG.Core/Extensions/JsonOptionsExtensions.cs
@@ -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
+ {
+ 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;
+ }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Extensions/LinqMethodExtensions.cs b/dg.dotnet/DG.Core/Extensions/LinqMethodExtensions.cs
new file mode 100644
index 0000000..5951569
--- /dev/null
+++ b/dg.dotnet/DG.Core/Extensions/LinqMethodExtensions.cs
@@ -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
+ {
+ ///
+ /// 使用自定linq扩展执行排序,查询,分页功能 item1: 未分页结果,item2:分页后的结果
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static IQueryable UseCoditionFind(this IQueryable source, bool condition, Action> action)
+ {
+ if (condition)
+ {
+ action(source);
+ }
+
+ return source;
+ }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Extensions/PredicateExtensionses.cs b/dg.dotnet/DG.Core/Extensions/PredicateExtensionses.cs
new file mode 100644
index 0000000..9667cd1
--- /dev/null
+++ b/dg.dotnet/DG.Core/Extensions/PredicateExtensionses.cs
@@ -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> True() { return f => true; }
+
+ public static Expression> False() { return f => false; }
+
+ public static Expression> And(this Expression> expLeft, Expression> 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>(body, candidateExpr);
+ }
+
+ public static Expression> Or(this Expression> expLeft, Expression> 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>(body, candidateExpr);
+ }
+ ///
+ /// 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))
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static Expression> AndListOr(this Expression> expLeft, Expression>[] predicates)
+ {
+ var candidateExpr = Expression.Parameter(typeof(T), "candidate");
+ var parameterReplacer = new ParameterReplacer(candidateExpr);
+ var left = parameterReplacer.Replace(expLeft.Body);
+ Expression> 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>(body, candidateExpr);
+ }
+
+ ///
+ /// 传入条件之间为OR查询
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static IQueryable WhereOR(this IQueryable source, params Expression>[] 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>(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;
+ }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Extensions/SystemKeyExtensions.cs b/dg.dotnet/DG.Core/Extensions/SystemKeyExtensions.cs
new file mode 100644
index 0000000..3d03812
--- /dev/null
+++ b/dg.dotnet/DG.Core/Extensions/SystemKeyExtensions.cs
@@ -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
+ {
+ ///
+ /// If extensions
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static T If(this T t, bool condition, Action action) where T : class
+ {
+ if (condition)
+ {
+ action(t);
+ }
+
+ return t;
+ }
+
+ ///
+ /// If extensions
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static T If(this T t, Predicate predicate, Action action) where T : class
+ {
+ if (t == null)
+ {
+ throw new ArgumentNullException();
+ }
+
+ if (predicate(t))
+ {
+ action(t);
+ }
+
+ return t;
+ }
+
+ ///
+ /// If extensions
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static T If(this T t, bool condition, Func func) where T : class => condition ? func(t) : t;
+
+ ///
+ /// If extensions
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static T If(this T t, Predicate predicate, Func func) where T : class => predicate(t) ? func(t) : t;
+
+ ///
+ /// If and else extensions
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static T IfAndElse(this T t, bool condition, Action ifAction, Action elseAction) where T : class
+ {
+ if (condition)
+ {
+ ifAction(t);
+ }
+ else
+ {
+ elseAction(t);
+ }
+
+ return t;
+ }
+
+ ///
+ /// If and else extensions
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static T IfAndElse(this T t, Predicate predicate, Action ifAction, Action elseAction) where T : class
+ {
+ if (t == null)
+ {
+ throw new ArgumentNullException();
+ }
+
+ if (predicate(t))
+ {
+ ifAction(t);
+ }
+ else
+ {
+ elseAction(t);
+ }
+
+ return t;
+ }
+
+ ///
+ /// If and else extensions
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static T IfAndElse(this T t, bool condition, Func ifFunc, Func elseFunc) where T : class => condition ? ifFunc(t) : elseFunc(t);
+
+ ///
+ /// If and else extensions
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static T IfAndElse(this T t, Predicate predicate, Func ifFunc, Func elseFunc) where T : class => predicate(t) ? ifFunc(t) : elseFunc(t);
+ }
+}
diff --git a/dg.dotnet/DG.Core/HttpClient.cs b/dg.dotnet/DG.Core/HttpClient.cs
new file mode 100644
index 0000000..778db07
--- /dev/null
+++ b/dg.dotnet/DG.Core/HttpClient.cs
@@ -0,0 +1,708 @@
+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 _logger;
+ private static LogLevel _logLevel = LogLevel.Debug;
+
+ public HttpClient(IHttpClientFactory httpClientFactory,
+ ILogger 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);
+ }
+
+ ///
+ /// Post Security
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task 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 stream = await httpResponse.Content.ReadAsStringAsync();
+ Log($"请求结果:{stream}");
+ var response = JsonSerializer.Deserialize(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);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task 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();
+ 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(stream, options);
+ return response;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "POST 方法请求错误!");
+ throw;
+ }
+ }
+
+ ///
+ /// Post Security
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task 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;
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task 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 UploadFileAsync(string url, string fileName, string fullName, Dictionary? 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(stream, options);
+ return response;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "UploadFile 方法请求错误!");
+ throw;
+ }
+ }
+
+ public async Task UploadFileAsync(string url, string fileName, string fullName, Dictionary? 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 正常请求
+
+ ///
+ /// Post
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task 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 stream = await httpResponse.Content.ReadAsStringAsync();
+ Log($"请求结果:{stream}");
+ var response = JsonSerializer.Deserialize(stream, options);
+ return response;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "POST 方法请求错误!");
+ throw;
+ }
+ }
+
+ public async Task 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;
+ }
+ }
+
+ ///
+ /// Get
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task GetAsync(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();
+ 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(stream, options);
+ return response;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "GET 方法请求错误!");
+ throw;
+ }
+ }
+
+ ///
+ /// Get
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task GetAsync(string url, Dictionary 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(stream, options);
+ return response;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "GET 方法请求错误!");
+ throw;
+ }
+ }
+
+ ///
+ /// Get
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task 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();
+ 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;
+ }
+ }
+
+ ///
+ /// Get
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task GetAsync(string url, Dictionary 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;
+ }
+ }
+
+ ///
+ /// 生成签名
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ 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;
+ }
+
+ ///
+ /// 计算 md5
+ ///
+ ///
+ ///
+ 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;
+ }
+
+ ///
+ /// 获取时间戳
+ ///
+ ///
+ public static string GetTimeStamp()
+ {
+ TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0);
+ return Convert.ToInt64(ts.TotalSeconds).ToString();
+ }
+
+ ///
+ /// 加密
+ ///
+ ///
+ ///
+ ///
+ 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);
+ }
+
+ ///
+ /// 解密
+ ///
+ ///
+ ///
+ ///
+ 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);
+ }
+
+ ///
+ /// AES加密算法
+ ///
+ /// 明文字符串
+ /// 字符串
+ 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 正常请求
+ }
+}
\ No newline at end of file
diff --git a/dg.dotnet/DG.Core/IHttpClient.cs b/dg.dotnet/DG.Core/IHttpClient.cs
new file mode 100644
index 0000000..ad744bb
--- /dev/null
+++ b/dg.dotnet/DG.Core/IHttpClient.cs
@@ -0,0 +1,38 @@
+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 PostSecurityAsync(string url, object data, string clientid, string accessKey, string iv);
+
+ Task PostSecurityAsync(string url, object param, object data, string clientid, string accessKey);
+
+ Task PostSecurityAsync(string url, object data, string clientid, string accessKey, string iv);
+
+ Task PostSecurityAsync(string url, object param, object data, string clientid, string accessKey);
+
+ Task PostAsync(string url, object? data = null, string? appId = "", string? appSecret = "", string? mediaType = "application/json");
+
+ Task PostAsync(string url, object? data = null, string? appId = "", string? appSecret = "", string? mediaType = "application/json");
+
+ Task GetAsync(string url, string appId = "", string appSecret = "", int timeout = 10000);
+
+ Task GetAsync(string url, Dictionary param, string appId = "", string appSecret = "");
+
+ Task GetAsync(string url, string appId = "", string appSecret = "");
+
+ Task GetAsync(string url, Dictionary param, string appId = "", string appSecret = "");
+
+ Task UploadFileAsync(string url, string fileName, string fullName, Dictionary? headers = null);
+
+ Task UploadFileAsync(string url, string fileName, string fullName, Dictionary? headers = null);
+ }
+}
\ No newline at end of file
diff --git a/dg.dotnet/DG.Core/IMvcBuilderApiResultExtensions.cs b/dg.dotnet/DG.Core/IMvcBuilderApiResultExtensions.cs
new file mode 100644
index 0000000..e554d35
--- /dev/null
+++ b/dg.dotnet/DG.Core/IMvcBuilderApiResultExtensions.cs
@@ -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
+ {
+ ///
+ /// 启用API标准返回值模式
+ ///
+ ///
+ ///
+ ///
+ 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));
+ });
+ }
+
+ ///
+ /// 启用API签名模式
+ ///
+ ///
+ ///
+ ///
+ 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));
+ });
+ }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Mapper/IMapper.cs b/dg.dotnet/DG.Core/Mapper/IMapper.cs
new file mode 100644
index 0000000..c3b8c5b
--- /dev/null
+++ b/dg.dotnet/DG.Core/Mapper/IMapper.cs
@@ -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 source);
+
+ List Map(List source);
+ }
+}
diff --git a/dg.dotnet/DG.Core/Mapper/Mapper.cs b/dg.dotnet/DG.Core/Mapper/Mapper.cs
new file mode 100644
index 0000000..f3491bb
--- /dev/null
+++ b/dg.dotnet/DG.Core/Mapper/Mapper.cs
@@ -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 source) => CacheModel.Invoke(source);
+
+ public List Map(List sources) => sources.AsParallel().Select(CacheModel.Invoke).ToList();
+
+ internal class CacheModel
+ {
+ private static readonly Func 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>(Expression.MemberInit(Expression.New(typeof(TTarget)), memberBindings), parameterExpression).Compile();
+ }
+
+ public static TTarget Invoke(TSource source) => Func(source);
+ }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Mapper/MapperExtendsions.cs b/dg.dotnet/DG.Core/Mapper/MapperExtendsions.cs
new file mode 100644
index 0000000..f341807
--- /dev/null
+++ b/dg.dotnet/DG.Core/Mapper/MapperExtendsions.cs
@@ -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
+ {
+ ///
+ /// 映射到
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static TTarget Map(this TSource source) => Mapper.Instance.Map(source);
+
+ ///
+ /// 映射到
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static List Map(this List sources) => Mapper.Instance.Map(sources);
+
+ ///
+ /// 复制到
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 因为重名了,所以对方法取别名,同 MapTo
+ public static TTarget Replicate(this TSource source) => source.Map();
+
+ ///
+ /// 复制到
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 因为重名了,所以对方法取别名,同 MapTo
+ public static List Replicate(this List sources) => sources.Map();
+ }
+}
diff --git a/dg.dotnet/DG.Core/Mapper/MapperManager.cs b/dg.dotnet/DG.Core/Mapper/MapperManager.cs
new file mode 100644
index 0000000..b0895da
--- /dev/null
+++ b/dg.dotnet/DG.Core/Mapper/MapperManager.cs
@@ -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 source) => source.Map();
+
+ public List Map(List source) => source.Map();
+ }
+}
diff --git a/dg.dotnet/DG.Core/Mapper/NotMapAttribute.cs b/dg.dotnet/DG.Core/Mapper/NotMapAttribute.cs
new file mode 100644
index 0000000..d45ab19
--- /dev/null
+++ b/dg.dotnet/DG.Core/Mapper/NotMapAttribute.cs
@@ -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()
+ {
+ }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Result/ApiExceptionFilterAttribute.cs b/dg.dotnet/DG.Core/Result/ApiExceptionFilterAttribute.cs
new file mode 100644
index 0000000..acf66ad
--- /dev/null
+++ b/dg.dotnet/DG.Core/Result/ApiExceptionFilterAttribute.cs
@@ -0,0 +1,49 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.Extensions.Logging;
+using System.Text.Json;
+
+namespace DG.Core
+{
+ ///
+ /// 表示处理API异常的筛选器。
+ ///
+ public class ApiExceptionFilterAttribute : Attribute, IExceptionFilter
+ {
+ private readonly ILogger _logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The logger
+ public ApiExceptionFilterAttribute(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ ///
+ /// Called when [exception].
+ ///
+ /// The context.
+ 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;
+
+
+ }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Result/ApiResourceFilter.cs b/dg.dotnet/DG.Core/Result/ApiResourceFilter.cs
new file mode 100644
index 0000000..dc664a4
--- /dev/null
+++ b/dg.dotnet/DG.Core/Result/ApiResourceFilter.cs
@@ -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)
+ {
+ // 执行中的过滤器管道
+ }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Result/ApiResult.cs b/dg.dotnet/DG.Core/Result/ApiResult.cs
new file mode 100644
index 0000000..331c771
--- /dev/null
+++ b/dg.dotnet/DG.Core/Result/ApiResult.cs
@@ -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
+ {
+
+ ///
+ /// Represents an empty .
+ ///
+ public static readonly IApiResult Empty = new ApiResult
+ {
+ Code = 0
+ };
+
+ ///
+ /// Gets or sets the status code.
+ ///
+ /// The status code.
+ [JsonPropertyName("code")]
+ public int Code { get; set; }
+
+ ///
+ /// Gets or sets the message.
+ ///
+ /// The message.
+ [JsonPropertyName("message")]
+ public string? Message { get; set; }
+
+ ///
+ /// Creates a new instance of by the specified result.
+ ///
+ /// The type of the result.
+ /// The result.
+ /// An instance inherited from interface.
+ public static IApiResult Succeed(TData data) => new ApiResult
+ {
+ Code = 0,
+ Data = data
+ };
+
+ ///
+ /// Creates a new instance of by the specified error message.
+ ///
+ /// The message.
+ /// The status code
+ /// An instance inherited from interface.
+ public static IApiResult Failed(string message, int? code = null) => new ApiResult
+ {
+ Code = code ?? -1,
+ Message = message
+ };
+
+ ///
+ /// Creates a new instance of by the specified error message.
+ ///
+ /// The type of the result.
+ /// The error result.
+ /// The message.
+ /// The status code.
+ /// An instance inherited from interface.
+ public static IApiResult Failed(TData data, string message, int? code = null) => new ApiResult
+ {
+ Code = code ?? -1,
+ Message = message,
+ Data = data
+ };
+
+ ///
+ /// Creates a new instance of by the specified status code and message.
+ ///
+ /// The status code.
+ /// The message.
+ /// An instance inherited from interface.
+ public static IApiResult From(int code, string message = null) => new ApiResult
+ {
+ Code = code,
+ Message = message
+ };
+
+ ///
+ /// Creates a new instance of by the specified result.
+ ///
+ /// The type of the result.
+ /// The result.
+ /// The status code.
+ /// The message.
+ /// An instance inherited from interface.
+ public static IApiResult From(TData data, int code, string message) => new ApiResult
+ {
+ Code = code,
+ Message = message,
+ Data = data
+ };
+ }
+}
diff --git a/dg.dotnet/DG.Core/Result/ApiResultFilterAttribute.cs b/dg.dotnet/DG.Core/Result/ApiResultFilterAttribute.cs
new file mode 100644
index 0000000..c944991
--- /dev/null
+++ b/dg.dotnet/DG.Core/Result/ApiResultFilterAttribute.cs
@@ -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;
+ }
+ }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Result/ApiResultFilterForbidAttribute.cs b/dg.dotnet/DG.Core/Result/ApiResultFilterForbidAttribute.cs
new file mode 100644
index 0000000..b56586c
--- /dev/null
+++ b/dg.dotnet/DG.Core/Result/ApiResultFilterForbidAttribute.cs
@@ -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)
+ { }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Result/ApiResultGeneric.cs b/dg.dotnet/DG.Core/Result/ApiResultGeneric.cs
new file mode 100644
index 0000000..9c17e4d
--- /dev/null
+++ b/dg.dotnet/DG.Core/Result/ApiResultGeneric.cs
@@ -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 : ApiResult, IApiResult
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ApiResult() { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The result.
+ /// The status code.
+ public ApiResult(TData data, int? code)
+ {
+ Code = code ?? 0;
+ Data = data;
+ }
+
+ ///
+ /// Gets or sets the result.
+ ///
+ /// The result.
+ [JsonPropertyName("data")]
+ public TData Data { get; set; }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Result/IApiResult.cs b/dg.dotnet/DG.Core/Result/IApiResult.cs
new file mode 100644
index 0000000..e2532e5
--- /dev/null
+++ b/dg.dotnet/DG.Core/Result/IApiResult.cs
@@ -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
+ {
+ ///
+ /// Gets or sets the status code.
+ ///
+ /// The status code.
+ int Code { get; set; }
+
+ ///
+ /// Gets or sets the message.
+ ///
+ /// The message.
+ string Message { get; set; }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Result/IApiResultGeneric.cs b/dg.dotnet/DG.Core/Result/IApiResultGeneric.cs
new file mode 100644
index 0000000..3b6c735
--- /dev/null
+++ b/dg.dotnet/DG.Core/Result/IApiResultGeneric.cs
@@ -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 : IApiResult
+ {
+ ///
+ /// Gets or sets the result.
+ ///
+ /// The result.
+ TData Data { get; set; }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Result/PageResult.cs b/dg.dotnet/DG.Core/Result/PageResult.cs
new file mode 100644
index 0000000..851c690
--- /dev/null
+++ b/dg.dotnet/DG.Core/Result/PageResult.cs
@@ -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 where TData : class
+ {
+ public PageResult(int pageIndex, int pageSize, int total, IList? data)
+ {
+ PageIndex = pageIndex;
+ PageSize = pageSize;
+ Total = total;
+ Data = data;
+ TotalCount = total == 0 ? 0 : (Total / PageSize) + (Total % PageSize) > 0 ? 1 : 0;
+ }
+
+ ///
+ /// 页数
+ ///
+ public int PageIndex { get; set; }
+
+ ///
+ /// 分页大小
+ ///
+ public int PageSize { get; set; }
+
+ ///
+ /// 总数量
+ ///
+ public int Total { get; set; }
+
+ ///
+ /// 分页总数量
+ ///
+ public int TotalCount { get; set; }
+
+ ///
+ /// 数据
+ ///
+ public IList? Data { get; set; }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Result/SearchPageBase.cs b/dg.dotnet/DG.Core/Result/SearchPageBase.cs
new file mode 100644
index 0000000..f918027
--- /dev/null
+++ b/dg.dotnet/DG.Core/Result/SearchPageBase.cs
@@ -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
+ {
+ ///
+ /// 页数
+ ///
+ public int PageIndex { get; set; }
+
+ ///
+ /// 分页大小
+ ///
+ public int PageSize { get; set; }
+
+ ///
+ /// 排序字段,支持逗号隔开
+ ///
+ public string? Sort { get; set; }
+
+ ///
+ /// 升降序,Asc/Desc
+ ///
+ public string? Order { get; set; }
+
+ ///
+ /// 是否导出
+ ///
+ public bool? Export { get; set; }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Result/SelectItem.cs b/dg.dotnet/DG.Core/Result/SelectItem.cs
new file mode 100644
index 0000000..87a22fc
--- /dev/null
+++ b/dg.dotnet/DG.Core/Result/SelectItem.cs
@@ -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; }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Security/ApiSecurityAsyncFilter.cs b/dg.dotnet/DG.Core/Security/ApiSecurityAsyncFilter.cs
new file mode 100644
index 0000000..e8f6512
--- /dev/null
+++ b/dg.dotnet/DG.Core/Security/ApiSecurityAsyncFilter.cs
@@ -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 _logger;
+
+ public ApiSecurityAsyncFilter(IConfiguration configuration,
+ ILogger 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>();
+ 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);
+ }
+
+ ///
+ /// AES解密
+ ///
+ /// 密文字节数组
+ /// 返回解密后的字符串
+ 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();
+ }
+ }
+}
\ No newline at end of file
diff --git a/dg.dotnet/DG.Core/Security/ApiSecurityAttribute.cs b/dg.dotnet/DG.Core/Security/ApiSecurityAttribute.cs
new file mode 100644
index 0000000..fc9747b
--- /dev/null
+++ b/dg.dotnet/DG.Core/Security/ApiSecurityAttribute.cs
@@ -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
+{
+ ///
+ /// 安全API
+ ///
+ public class ApiSecurityAttribute : Attribute, IFilterMetadata
+ {
+ public ApiSecurityAttribute()
+ {
+
+ }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Security/ClientKey.cs b/dg.dotnet/DG.Core/Security/ClientKey.cs
new file mode 100644
index 0000000..e347176
--- /dev/null
+++ b/dg.dotnet/DG.Core/Security/ClientKey.cs
@@ -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; }
+ }
+}
diff --git a/dg.dotnet/DG.Core/ServiceCollectionExtensions.cs b/dg.dotnet/DG.Core/ServiceCollectionExtensions.cs
new file mode 100644
index 0000000..ec61c92
--- /dev/null
+++ b/dg.dotnet/DG.Core/ServiceCollectionExtensions.cs
@@ -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();
+
+ ///
+ /// Add auto ioc services
+ ///
+ ///
+ ///
+ ///
+ ///
+ 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();
+ }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Signature/ApiSignatureAsyncFilterAttribute.cs b/dg.dotnet/DG.Core/Signature/ApiSignatureAsyncFilterAttribute.cs
new file mode 100644
index 0000000..2d88d7d
--- /dev/null
+++ b/dg.dotnet/DG.Core/Signature/ApiSignatureAsyncFilterAttribute.cs
@@ -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();
+ 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));
+ }
+ }
+ }
+
+ ///
+ /// 生成签名
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ 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;
+ }
+
+ ///
+ /// 计算 md5
+ ///
+ ///
+ ///
+ 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;
+ }
+
+ ///
+ /// 获取时间戳
+ ///
+ ///
+ public static string GetTimeStamp()
+ {
+ TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0);
+ return Convert.ToInt64(ts.TotalSeconds).ToString();
+ }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Signature/ApiSignatureFilterForbidAttribute.cs b/dg.dotnet/DG.Core/Signature/ApiSignatureFilterForbidAttribute.cs
new file mode 100644
index 0000000..7b96aec
--- /dev/null
+++ b/dg.dotnet/DG.Core/Signature/ApiSignatureFilterForbidAttribute.cs
@@ -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
+{
+ ///
+ /// API屏蔽签名
+ ///
+ public class ApiSignatureFilterForbidAttribute : Attribute, IAuthorizationFilter
+ {
+ public void OnAuthorization(AuthorizationFilterContext context)
+ {
+ }
+ }
+}
diff --git a/dg.dotnet/DG.Core/TimeSecurity/ApiTimeSecurityAsyncFilter.cs b/dg.dotnet/DG.Core/TimeSecurity/ApiTimeSecurityAsyncFilter.cs
new file mode 100644
index 0000000..a520f12
--- /dev/null
+++ b/dg.dotnet/DG.Core/TimeSecurity/ApiTimeSecurityAsyncFilter.cs
@@ -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 _logger;
+
+ public ApiTimeSecurityAsyncFilter(IConfiguration configuration,
+ ILogger 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>();
+ 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();
+ 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(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);
+ }
+
+ ///
+ /// AES解密
+ ///
+ /// 密文字节数组
+ /// 返回解密后的字符串
+ 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();
+ }
+
+ ///
+ /// 获取时间戳
+ ///
+ ///
+ 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; }
+ }
+}
\ No newline at end of file
diff --git a/dg.dotnet/DG.Core/TimeSecurity/ApiTimeSecurityAttribute.cs b/dg.dotnet/DG.Core/TimeSecurity/ApiTimeSecurityAttribute.cs
new file mode 100644
index 0000000..3ac63b5
--- /dev/null
+++ b/dg.dotnet/DG.Core/TimeSecurity/ApiTimeSecurityAttribute.cs
@@ -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
+{
+ ///
+ /// 安全API
+ ///
+ public class ApiTimeSecurityAttribute : Attribute, IFilterMetadata
+ {
+ public ApiTimeSecurityAttribute()
+ {
+
+ }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Validation/DecimalValidationAttribute.cs b/dg.dotnet/DG.Core/Validation/DecimalValidationAttribute.cs
new file mode 100644
index 0000000..2716441
--- /dev/null
+++ b/dg.dotnet/DG.Core/Validation/DecimalValidationAttribute.cs
@@ -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;
+ }
+
+ ///
+ /// IsValid 为 false 时,提示得 error 信息
+ ///
+ ///
+ ///
+ public override string FormatErrorMessage(string name)
+ {
+ var lengthMessage = LengthError ? $",且长度不能超过{Length}" : "";
+ return $"{name}必须输入数字{lengthMessage},请重新输入!";
+ }
+
+ ///
+ /// 验证当前字段得结果
+ ///
+ ///
+ ///
+ 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;
+ }
+ }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Validation/IntValidationAttribute.cs b/dg.dotnet/DG.Core/Validation/IntValidationAttribute.cs
new file mode 100644
index 0000000..9d33c76
--- /dev/null
+++ b/dg.dotnet/DG.Core/Validation/IntValidationAttribute.cs
@@ -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;
+ }
+
+ ///
+ /// IsValid 为 false 时,提示得 error 信息
+ ///
+ ///
+ ///
+ public override string FormatErrorMessage(string name)
+ {
+ var lengthMessage = LengthError ? $",且长度不能超过{Length}" : "";
+ return $"{name}必须输入数字{lengthMessage},请重新输入!";
+ }
+
+ ///
+ /// 验证当前字段得结果
+ ///
+ ///
+ ///
+ 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;
+ }
+ }
+ }
+}
diff --git a/dg.dotnet/DG.Core/Validation/LongValidationAttribute.cs b/dg.dotnet/DG.Core/Validation/LongValidationAttribute.cs
new file mode 100644
index 0000000..367f549
--- /dev/null
+++ b/dg.dotnet/DG.Core/Validation/LongValidationAttribute.cs
@@ -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;
+ }
+
+ ///
+ /// IsValid 为 false 时,提示得 error 信息
+ ///
+ ///
+ ///
+ public override string FormatErrorMessage(string name)
+ {
+ var lengthMessage = LengthError ? $",且长度不能超过{Length}" : "";
+ return $"{name}必须输入数字{lengthMessage},请重新输入!";
+ }
+
+ ///
+ /// 验证当前字段得结果
+ ///
+ ///
+ ///
+ 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;
+ }
+ }
+ }
+}
diff --git a/dg.dotnet/DG.DotNet.Sample/AppDbContext.cs b/dg.dotnet/DG.DotNet.Sample/AppDbContext.cs
new file mode 100644
index 0000000..80edf19
--- /dev/null
+++ b/dg.dotnet/DG.DotNet.Sample/AppDbContext.cs
@@ -0,0 +1,18 @@
+using DG.DotNet.Sample.Models;
+using Microsoft.EntityFrameworkCore;
+
+namespace DG.DotNet.Sample
+{
+ public class AppDbContext : OracleDbContext
+ {
+ public readonly IConfiguration _configuration;
+
+ public AppDbContext(DbContextOptions options,
+ IAppManager appManager, IConfiguration configuration)
+ : base(options, appManager, configuration) => _configuration = configuration;
+
+ public DbSet BAS_COMPANY { get; set; }
+
+ public DbSet BAS_BUSINESSDEPARTMENT { get; set; }
+ }
+}
diff --git a/dg.dotnet/DG.DotNet.Sample/Controllers/TestController.cs b/dg.dotnet/DG.DotNet.Sample/Controllers/TestController.cs
new file mode 100644
index 0000000..dc94187
--- /dev/null
+++ b/dg.dotnet/DG.DotNet.Sample/Controllers/TestController.cs
@@ -0,0 +1,127 @@
+using DG.DotNet.Sample.Models;
+using DG.EventBus;
+using DG.Core;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.AspNetCore.Authorization;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Text.Encodings.Web;
+using DG.Redis;
+
+namespace DG.DotNet.Sample.Controllers
+{
+ [ApiController]
+ [Route("[controller]")]
+ public class TestController : ControllerBase
+ {
+ private readonly ILogger _logger;
+ private readonly IScopeDbContextManager _manager;
+ private readonly IEventBus _eventBus;
+ private readonly IHttpClient _httpClient;
+ private readonly IRedisManager _redisManager;
+
+ public TestController(IScopeDbContextManager manager,
+ ILogger logger,
+ IEventBus eventBus,
+ IHttpClient httpClient,
+ IRedisManager redisManager)
+ {
+ _logger = logger;
+ _manager = manager;
+ _eventBus = eventBus;
+ _httpClient = httpClient;
+ _redisManager = redisManager;
+ }
+
+ [HttpPost("[action]")]
+ [ApiSignatureFilterForbid]
+ public async Task> Post([FromBody] BAS_COMPANY dto)
+ {
+ //TestEvent testEvent = new TestEvent();
+ //await _eventBus.publicAsync(testEvent);
+ using var repository = _manager.CreateRepository();
+ return await repository.GetRepository().QueryListAsync();
+ }
+
+ [HttpGet("[action]")]
+ [ApiSignatureFilterForbid]
+ public async Task> Get([FromHeader] string? appid = "crm_tg_dng8")
+ {
+ var d = await _redisManager.ExistsAsync("dat");
+ var list = new List();
+ using (var repository = _manager.CreateRepository())
+ {
+ using var transaction = await repository.BeginTransactionAsync();
+ list = await repository.GetRepository().QueryListAsync();
+ list.ForEach(x =>
+ {
+ if (x.COMPANYID == 600000173)
+ {
+ x.CREATEUSER = 600000207;
+ }
+ });
+ await repository.GetRepository().BatchUpdateAsync(list, x => new { x.CREATEUSER });
+ var data = await repository.GetRepository().QueryListAsync();
+ data.ForEach(x =>
+ {
+ x.UTIME = DateTime.Now;
+ });
+ await repository.GetRepository().BatchUpdateAsync(data, x => new { x.UTIME });
+ await transaction.CommitAsync();
+ }
+ using (var repository = _manager.CreateRepository("crm_d1_dnzz"))
+ {
+ var list2 = await repository.GetRepository().QueryListAsync();
+ list.AddRange(list2);
+ }
+ return list;
+ }
+
+ [HttpGet("[action]")]
+ [ApiSignatureFilterForbid]
+ public async Task> GetInnerusers([FromHeader] string appid)
+ {
+ var data = await _httpClient.GetAsync>>("https://localhost:7150/Test/GetInnerusers?eid=5004,4028,123", "qt_compliance", "CqlNBxRof0yl7eJM1f4IbOBhgribfooZJ1zwuIj5NzQ=");
+ return data.Data;
+ }
+
+ [HttpGet("[action]")]
+ [ApiSignatureFilterForbid]
+ public async Task Test([FromQuery] BAS_COMPANY dto)
+ {
+ //int agentid = 1000006;
+ //string appid = "wwd4cd11d60db47118";
+ //string at = "221017-141316-73";
+ //string clientId = "UPWEBSITE";
+ //var options = new JsonSerializerOptions
+ //{
+ // DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+ // PropertyNameCaseInsensitive = true,
+ // Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
+ //};
+ //var model = new
+ //{
+ // appid = appid,
+ // agentid = agentid,
+ // data = JsonSerializer.Serialize(new
+ // {
+ // msgtype = "text",
+ // text = new
+ // {
+ // content = "2222222222"
+ // },
+ // agentid = agentid,
+ // toparty = "",
+ // totag = "",
+ // touser = at,
+ // safe = 0
+ // }, options)
+ //};
+ //var result = await _httpClient.WeworkSendeMsg>("http://post.hc.dn8188.com/Wework/sendmsg.html", model, clientId, "1622a92d");
+ //return result.Data;
+ var result = await _httpClient.PostAsync("https://certcontract.wantest.tcfortune.com:11188qt_compliance?content=8fYIIKMSFVxFY5i2AWz7Awu5dDpKxrp0HGf%2bm1Ij3fa6DcfLHvZbag%3d%3d&sign=9jgR248r5vBlH3yZqrz9%2bg%3d%3d&clientId=UPWEBSITE");
+ return "";
+ }
+ }
+}
\ No newline at end of file
diff --git a/dg.dotnet/DG.DotNet.Sample/DG.DotNet.Sample.csproj b/dg.dotnet/DG.DotNet.Sample/DG.DotNet.Sample.csproj
new file mode 100644
index 0000000..af8143d
--- /dev/null
+++ b/dg.dotnet/DG.DotNet.Sample/DG.DotNet.Sample.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net6.0
+ enable
+ enable
+ 78e44202-6303-41c8-99b3-6abe5c750ef5
+ Linux
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dg.dotnet/DG.DotNet.Sample/Dockerfile b/dg.dotnet/DG.DotNet.Sample/Dockerfile
new file mode 100644
index 0000000..3786c59
--- /dev/null
+++ b/dg.dotnet/DG.DotNet.Sample/Dockerfile
@@ -0,0 +1,37 @@
+#See https://aka.ms/containerfastmode to understand 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 ["DG.DotNet.Sample/DG.DotNet.Sample.csproj", "DG.DotNet.Sample/"]
+RUN dotnet restore "DG.DotNet.Sample/DG.DotNet.Sample.csproj"
+COPY . .
+WORKDIR "/src/DG.DotNet.Sample"
+RUN dotnet build "DG.DotNet.Sample.csproj" -c Release -o /app/build
+
+FROM build AS publish
+RUN dotnet publish "DG.DotNet.Sample.csproj" -c Release -o /app/publish
+
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app/publish .
+ENTRYPOINT ["dotnet", "DG.DotNet.Sample.dll"]
\ No newline at end of file
diff --git a/dg.dotnet/DG.DotNet.Sample/Models/BAS_BUSINESSDEPARTMENT.cs b/dg.dotnet/DG.DotNet.Sample/Models/BAS_BUSINESSDEPARTMENT.cs
new file mode 100644
index 0000000..c7656e7
--- /dev/null
+++ b/dg.dotnet/DG.DotNet.Sample/Models/BAS_BUSINESSDEPARTMENT.cs
@@ -0,0 +1,24 @@
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace DG.DotNet.Sample.Models
+{
+ [Table("BAS_BUSINESSDEPARTMENT")]
+ public class BAS_BUSINESSDEPARTMENT
+ {
+ public decimal ID { get; set; }
+
+ public decimal BUSINESSID { get; set; }
+ ///
+ /// 部门ID
+ ///
+ public decimal DEPTID { get; set; }
+ ///
+ /// 1、主部门 2、子部门
+ ///
+ public int DEPTTYPE { get; set; }
+
+ public DateTime CTIME { get; set; }
+
+ public DateTime UTIME { get; set; }
+ }
+}
diff --git a/dg.dotnet/DG.DotNet.Sample/Models/BAS_COMPANY.cs b/dg.dotnet/DG.DotNet.Sample/Models/BAS_COMPANY.cs
new file mode 100644
index 0000000..3fe5582
--- /dev/null
+++ b/dg.dotnet/DG.DotNet.Sample/Models/BAS_COMPANY.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using System.Threading.Tasks;
+
+namespace DG.DotNet.Sample.Models
+{
+ [Table("BAS_COMPANY")]
+ public class BAS_COMPANY
+ {
+ [Key]
+ public decimal? COMPANYID { get; set; }
+
+ [StringLength(50)]
+ public string? COMPANYNAME { get; set; }
+
+ public short? ISOUTERAGENT { get; set; }
+
+ public decimal? PARENTID { get; set; }
+
+ [StringLength(6)]
+ public string? COMPANYCODE { get; set; }
+
+ public decimal? BUSINESSVALUE { get; set; }
+
+ public DateTime? CTIME { get; set; }
+
+ public decimal? CREATEUSER { get; set; }
+
+ public DateTime? UTIME { get; set; }
+
+ public decimal? UPDATEUSER { get; set; }
+
+ [StringLength(100)]
+ public string? COMMENTS { get; set; }
+
+ [StringLength(1)]
+ public string? SYSTEMCODE { get; set; }
+
+ [StringLength(20)]
+ public string? ORGANNAME { get; set; }
+
+ }
+}
diff --git a/dg.dotnet/DG.DotNet.Sample/Models/Meeting.cs b/dg.dotnet/DG.DotNet.Sample/Models/Meeting.cs
new file mode 100644
index 0000000..33262b6
--- /dev/null
+++ b/dg.dotnet/DG.DotNet.Sample/Models/Meeting.cs
@@ -0,0 +1,60 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace Zxd.Entity.Zxd
+{
+ [Table("t_meeting")]
+ public class Meeting
+ {
+ public Meeting()
+ {
+ MeetingAccessories = new List();
+ MeetingParticipants = new List();
+ }
+
+ [Key]
+ public int Id { get; set; }
+
+ [Column("meeting_name")]
+ public string? MeetingName { get; set; }
+
+ [Column("meeting_type")]
+ public int MeetingType { get; set; }
+
+ [Column("begin_time")]
+ public DateTime BeginTime { get; set; }
+
+ [Column("continue_hour")]
+ public int ContinueHour { get; set; }
+
+ [Column("continue_minute")]
+ public int ContinueMinute { get; set; }
+
+ public string? Site { get; set; }
+
+ public string? Compere { get; set; }
+
+ public string? Remark { get; set; }
+
+ [Column("create_user")]
+ public string? CreateUser { get; set; }
+
+ [Column("create_time")]
+ public DateTime CreateTime { get; set; }
+
+ [Column("is_delete")]
+ public bool IsDelete { get; set; }
+
+ [Column("update_user")]
+ public string? UpdateUser { get; set; }
+
+ [Column("update_time")]
+ public DateTime? UpdateTime { get; set; }
+
+ public virtual List MeetingAccessories { get; set; }
+
+ public virtual List MeetingParticipants { get; set; }
+ }
+}
+
diff --git a/dg.dotnet/DG.DotNet.Sample/Models/MeetingAccessory.cs b/dg.dotnet/DG.DotNet.Sample/Models/MeetingAccessory.cs
new file mode 100644
index 0000000..8ec566c
--- /dev/null
+++ b/dg.dotnet/DG.DotNet.Sample/Models/MeetingAccessory.cs
@@ -0,0 +1,40 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+namespace Zxd.Entity.Zxd
+{
+ [Table("meeting_accessory")]
+ public class MeetingAccessory
+ {
+ public MeetingAccessory()
+ {
+ }
+
+ [Key]
+ public int Id { get; set; }
+
+ [Column("meeting_id")]
+ public int MeetingId { get; set; }
+
+ [Column("file_name")]
+ public string? FileName { get; set; }
+
+ [Column("file_url")]
+ public string? FileUrl { get; set; }
+
+ [Column("file_size")]
+ public string? FileSize { get; set; }
+
+ [Column("uploader")]
+ public string? Uploader { get; set; }
+
+ [Column("upload_eid")]
+ public int UploadEid { get; set; }
+
+ [Column("upload_time")]
+ public DateTime UploadTime { get; set; }
+
+ public virtual Meeting? Meeting { get; set; }
+ }
+}
+
diff --git a/dg.dotnet/DG.DotNet.Sample/Models/MeetingParticipant.cs b/dg.dotnet/DG.DotNet.Sample/Models/MeetingParticipant.cs
new file mode 100644
index 0000000..7e83290
--- /dev/null
+++ b/dg.dotnet/DG.DotNet.Sample/Models/MeetingParticipant.cs
@@ -0,0 +1,28 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+namespace Zxd.Entity.Zxd
+{
+ public class MeetingParticipant
+ {
+ public MeetingParticipant()
+ {
+ }
+
+ [Key]
+ public int Id { get; set; }
+
+ [Column("meeting_id")]
+ public int MeetingId { get; set; }
+
+ public int Eid { get; set; }
+
+ ///
+ /// 参与人
+ ///
+ public string? Paricipant { get; set; }
+
+ public virtual Meeting? Meeting { get; set; }
+ }
+}
+
diff --git a/dg.dotnet/DG.DotNet.Sample/Models/RES_CUSTOMERDETAIL.cs b/dg.dotnet/DG.DotNet.Sample/Models/RES_CUSTOMERDETAIL.cs
new file mode 100644
index 0000000..dc4111a
--- /dev/null
+++ b/dg.dotnet/DG.DotNet.Sample/Models/RES_CUSTOMERDETAIL.cs
@@ -0,0 +1,93 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace DG.DotNet.Sample.Models
+{
+ [Table("RES_CUSTOMERDETAIL")]
+ public partial class RES_CUSTOMERDETAIL
+ {
+ [Key]
+ [StringLength(18)]
+ public string? RESID { get; set; }
+
+ public decimal? CACCOUNT { get; set; }
+
+ [StringLength(100)]
+ public string? EMAIL { get; set; }
+
+ [StringLength(30)]
+ public string? CNAME { get; set; }
+
+ [StringLength(1)]
+ public string? GENDER { get; set; }
+
+ public DateTime? BIRTHDAY { get; set; }
+
+ public decimal? PROVINCEID { get; set; }
+
+ public decimal? CITYID { get; set; }
+
+ [StringLength(200)]
+ public string? ADDRESS { get; set; }
+
+ [StringLength(50)]
+ public string? CUSTOMERTYPEID { get; set; }
+
+ [StringLength(50)]
+ public string? AMOUNTTYPEID { get; set; }
+
+ [StringLength(50)]
+ public string? JOBTYPEID { get; set; }
+
+ [StringLength(50)]
+ public string? OPERATIONTYPE { get; set; }
+
+ [StringLength(100)]
+ public string? MSN { get; set; }
+
+ [StringLength(20)]
+ public string? QQ { get; set; }
+
+ [StringLength(15)]
+ public string? FAX { get; set; }
+
+ [StringLength(100)]
+ public string? CUSTOMERFROMBIG { get; set; }
+
+ [StringLength(200)]
+ public string? PRIMARYNUMBERADDRESS { get; set; }
+
+ [StringLength(100)]
+ public string? SPECIALMEMO { get; set; }
+
+ public decimal? CREATEUSER { get; set; }
+
+ public DateTime? UTIME { get; set; }
+
+ public decimal? UPDATEUSER { get; set; }
+
+ public short ISPRIMARYNUM { get; set; }
+
+ public string? ZX_USERID { get; set; }
+ public string? REMARK { get; set; }
+ public string? CUSTOMERCATEGORY { get; set; }
+ public string? BIRTHDAYAREA { get; set; }
+ public string? DEALER { get; set; }
+ public string? OPERATIONTIME { get; set; }
+ public string? FREQUENCY { get; set; }
+ public string? STOCKPOSITION { get; set; }
+ public decimal? VISITSTATUS { get; set; }
+ public DateTime? VISITTIME { get; set; }
+ public decimal? VISITUSER { get; set; }
+ public string? VISITREMARK { get; set; }
+
+ public string? GUPIAO { get; set; }
+ public string? PROFITANDLOSS { get; set; }
+ public string? RISKTOLERANCE { get; set; }
+ public string? BELONGTOPROVINCE { get; set; }
+ public string? BELONGTOCITY { get; set; }
+
+ public int? RELATION { get; set; }
+ public int? ISBOUND { get; set; }//是否绑定客服
+ }
+}
diff --git a/dg.dotnet/DG.DotNet.Sample/Program.cs b/dg.dotnet/DG.DotNet.Sample/Program.cs
new file mode 100644
index 0000000..574cd72
--- /dev/null
+++ b/dg.dotnet/DG.DotNet.Sample/Program.cs
@@ -0,0 +1,37 @@
+using DG.DotNet.Sample;
+using DG.EventBus;
+using DG.Core;
+using Microsoft.EntityFrameworkCore;
+using DG.DotNet.Sample.Workers;
+
+var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
+var builder = WebApplication.CreateBuilder(args);
+
+// Add services to the container.
+builder.Services.AddDGEntityFrameworkOracle("appid");
+builder.Services.AddControllers()
+ .AddApiResult()
+ .AddApiSignature();
+builder.Services.AddRedis(builder.Configuration);
+builder.Services.AddEventBus();
+builder.Services.AddDGHttpClient();
+// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
+builder.Services.AddEndpointsApiExplorer();
+builder.Services.AddSwaggerGen();
+builder.Services.AddHostedService();
+builder.Services.AddSingleton, TestHandler>();
+var app = builder.Build();
+// Configure the HTTP request pipeline.
+if (app.Environment.IsDevelopment() || Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "PreProduction")
+{
+ app.UseSwagger();
+ app.UseSwaggerUI();
+}
+
+app.UseHttpsRedirection();
+app.UseDGEntityFrameworkOracle("crm_tg_dng8");
+
+
+app.MapControllers();
+
+app.Run();
diff --git a/dg.dotnet/DG.DotNet.Sample/Properties/launchSettings.json b/dg.dotnet/DG.DotNet.Sample/Properties/launchSettings.json
new file mode 100644
index 0000000..c308da0
--- /dev/null
+++ b/dg.dotnet/DG.DotNet.Sample/Properties/launchSettings.json
@@ -0,0 +1,40 @@
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:33425",
+ "sslPort": 44349
+ }
+ },
+ "profiles": {
+ "DG.DotNet.Sample": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:7150;http://localhost:5150",
+ "dotnetRunMessages": true
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "Docker": {
+ "commandName": "Docker",
+ "launchBrowser": true,
+ "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
+ "publishAllPorts": true,
+ "useSSL": true,
+ "httpPort": 5353,
+ "sslPort": 5354
+ }
+ }
+}
\ No newline at end of file
diff --git a/dg.dotnet/DG.DotNet.Sample/TestEvent.cs b/dg.dotnet/DG.DotNet.Sample/TestEvent.cs
new file mode 100644
index 0000000..25dea0a
--- /dev/null
+++ b/dg.dotnet/DG.DotNet.Sample/TestEvent.cs
@@ -0,0 +1,8 @@
+using DG.EventBus;
+
+namespace DG.DotNet.Sample
+{
+ public class TestEvent : IEvent
+ {
+ }
+}
diff --git a/dg.dotnet/DG.DotNet.Sample/TestHandler.cs b/dg.dotnet/DG.DotNet.Sample/TestHandler.cs
new file mode 100644
index 0000000..3a53c58
--- /dev/null
+++ b/dg.dotnet/DG.DotNet.Sample/TestHandler.cs
@@ -0,0 +1,19 @@
+using DG.EventBus;
+
+namespace DG.DotNet.Sample
+{
+ public class TestHandler : IEventHandler
+ {
+ public bool CanHandle(TestEvent @event)
+ {
+ return true;
+ }
+
+ public async Task HandleAsync(TestEvent @event, CancellationToken cancellationToken = default)
+ {
+ await Task.Delay(10000);
+ Console.WriteLine("test");
+ return await Task.FromResult(true);
+ }
+ }
+}
diff --git a/dg.dotnet/DG.DotNet.Sample/Workers/TestWorker.cs b/dg.dotnet/DG.DotNet.Sample/Workers/TestWorker.cs
new file mode 100644
index 0000000..919dd13
--- /dev/null
+++ b/dg.dotnet/DG.DotNet.Sample/Workers/TestWorker.cs
@@ -0,0 +1,70 @@
+using DG.DotNet.Sample.Models;
+using DG.EventBus;
+using DG.Core;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.AspNetCore.Authorization;
+using System.Text.Json;
+using Oracle.ManagedDataAccess.Client;
+using System.Data;
+
+namespace DG.DotNet.Sample.Workers
+{
+ public class TestWorker : BackgroundService
+ {
+ private readonly ILogger _logger;
+ private readonly IServiceProvider _serviceProvider;
+ private readonly IEventBus _eventBus;
+ private readonly IHttpClient _httpClient;
+
+ public TestWorker(IServiceProvider serviceProvider,
+ ILogger logger,
+ IEventBus eventBus,
+ IHttpClient httpClient)
+ {
+ _logger = logger;
+ _serviceProvider = serviceProvider;
+ _eventBus = eventBus;
+ _httpClient = httpClient;
+ }
+
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ using (IServiceScope scope = _serviceProvider.CreateScope())
+ {
+ var repository = scope.ServiceProvider.GetRequiredService>();
+ var sql = @"select
+ a.*
+ from
+ (
+ select
+ *
+ from
+ bas_businessdepartment
+ where
+ deptid in (
+ select
+ saledeptid
+ from
+ bas_salesdepartment start with saledeptid =(
+ select
+ a.deptid
+ from
+ bas_innerusergroup a
+ join bas_inneruser b on a.inneruserid = b.pkid
+ where
+ b.eid = :eid
+ ) connect by prior department_parentid = department_id
+ )
+ ) a
+ join bas_businesslines b on a.businessid = b.businessid";
+ var param = new OracleParameter[] {
+ new OracleParameter() { ParameterName = ":eid", OracleDbType = OracleDbType.Int64, Value = 4028 }
+ };
+ var list = await repository.ExecuteSqlToEntityAsync(sql, param);
+ _logger.LogInformation(JsonSerializer.Serialize(list));
+ }
+
+ }
+ }
+}
diff --git a/dg.dotnet/DG.DotNet.Sample/appsettings.Development.json b/dg.dotnet/DG.DotNet.Sample/appsettings.Development.json
new file mode 100644
index 0000000..0c208ae
--- /dev/null
+++ b/dg.dotnet/DG.DotNet.Sample/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/dg.dotnet/DG.DotNet.Sample/appsettings.json b/dg.dotnet/DG.DotNet.Sample/appsettings.json
new file mode 100644
index 0000000..a4ed52a
--- /dev/null
+++ b/dg.dotnet/DG.DotNet.Sample/appsettings.json
@@ -0,0 +1,48 @@
+{
+ "ConnectionStrings": {
+ "crm_tg_dng8": "DATA SOURCE=192.168.11.41:1521/Orcl_TG;PERSIST SECURITY INFO=True;USER ID=UPDEV;PASSWORD=sa123456.",
+ "crm_d1_dnzz": "DATA SOURCE=192.168.11.82:1521/orcl;PERSIST SECURITY INFO=True;USER ID=UPDEV;PASSWORD=sa123456.",
+ "crm_d3_dnyy": "DATA SOURCE=192.168.11.41:1521/Orcl_CrmFB;PERSIST SECURITY INFO=True;USER ID=UPDEV;PASSWORD=sa123456."
+ },
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*",
+ "SignConfig": {
+ "AppId": "qt_compliance",
+ "Secret": "CqlNBxRof0yl7eJM1f4IbOBhgribfooZJ1zwuIj5NzQ="
+ },
+ "ClientKey": [
+ {
+ "Id": "UPWEBSITE",
+ "Name": "NewWebSite",
+ "AccessKey": "1622a92d",
+ "Vi": "Nx7GqcMxc=F&cpUa",
+ "NewAccessKey": "YafhQn$3gLUl@XDI"
+ },
+ {
+ "Id": "TDORDERSITE",
+ "Name": "订单接口",
+ "AccessKey": "622a92d1"
+ }
+ ],
+ "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"
+ }
+ ]
+}
diff --git a/dg.dotnet/DG.EntityFramework/DG.EntityFramework.csproj b/dg.dotnet/DG.EntityFramework/DG.EntityFramework.csproj
new file mode 100644
index 0000000..8c260d6
--- /dev/null
+++ b/dg.dotnet/DG.EntityFramework/DG.EntityFramework.csproj
@@ -0,0 +1,15 @@
+
+
+
+ net6.0
+ enable
+ enable
+ True
+ 1.0.32
+
+
+
+
+
+
+
diff --git a/dg.dotnet/DG.EntityFramework/DbContextProvider.cs b/dg.dotnet/DG.EntityFramework/DbContextProvider.cs
new file mode 100644
index 0000000..e72d7b2
--- /dev/null
+++ b/dg.dotnet/DG.EntityFramework/DbContextProvider.cs
@@ -0,0 +1,23 @@
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace DG.EntityFramework
+{
+ public class DbContextProvider : IDbContextProvider
+ where TDbContext : DbContext
+ {
+ private readonly TDbContext _dbContext;
+
+ public DbContextProvider(TDbContext dbContext)
+ {
+ _dbContext = dbContext;
+ }
+
+ public TDbContext GetDbContext()
+ {
+ return _dbContext;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dg.dotnet/DG.EntityFramework/EFCoreOptions.cs b/dg.dotnet/DG.EntityFramework/EFCoreOptions.cs
new file mode 100644
index 0000000..342fe23
--- /dev/null
+++ b/dg.dotnet/DG.EntityFramework/EFCoreOptions.cs
@@ -0,0 +1,38 @@
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Data.Common;
+using System.Text;
+
+namespace DG.EntityFramework
+{
+ ///
+ /// Entity Framework Core Options
+ ///
+ public class EFCoreOptions
+ where TDbContext : DbContext
+ {
+ ///
+ /// Gets or sets the database's connection string that will be used to store database entities.
+ ///
+ public string ConnectionString { get; set; }
+
+ ///
+ ///
+ ///
+ public DbConnection ExistingConnection { get; internal set; }
+
+ ///
+ ///
+ ///
+ public DbContextOptionsBuilder DbContextOptions { get; }
+
+ internal string Version { get; set; }
+
+ public EFCoreOptions()
+ {
+ DbContextOptions = new DbContextOptionsBuilder();
+ Version = "1.0.0";
+ }
+ }
+}
\ No newline at end of file
diff --git a/dg.dotnet/DG.EntityFramework/EFCoreOptionsExtension.cs b/dg.dotnet/DG.EntityFramework/EFCoreOptionsExtension.cs
new file mode 100644
index 0000000..0472444
--- /dev/null
+++ b/dg.dotnet/DG.EntityFramework/EFCoreOptionsExtension.cs
@@ -0,0 +1,28 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+
+namespace DG.EntityFramework
+{
+ public class EFCoreOptionsExtension
+ where TDbContext : DbContext
+ {
+ private readonly Action> _configure;
+
+ public EFCoreOptionsExtension(Action> configure)
+ {
+ _configure = configure;
+ }
+
+ public void AddServices(IServiceCollection services)
+ {
+ var options = new EFCoreOptions();
+ _configure(options);
+ services.AddDbContext();
+ services.Configure(_configure);
+ services.AddScoped>();
+ services.AddScoped>();
+ services.AddScoped, DbContextProvider>();
+ }
+ }
+}
\ No newline at end of file
diff --git a/dg.dotnet/DG.EntityFramework/EFDbContext.cs b/dg.dotnet/DG.EntityFramework/EFDbContext.cs
new file mode 100644
index 0000000..6c61b1f
--- /dev/null
+++ b/dg.dotnet/DG.EntityFramework/EFDbContext.cs
@@ -0,0 +1,26 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.Extensions.Logging;
+
+namespace DG.EntityFramework
+{
+ public class EFDbContext : DbContext
+ {
+ public EFDbContext(DbContextOptions options)
+ : base(options)
+ {
+ }
+
+ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ {
+ if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development")
+ {
+ var loggerFactory = new LoggerFactory();
+ loggerFactory.AddProvider(new EFLoggerProvider());
+ optionsBuilder.UseLoggerFactory(loggerFactory);
+ }
+
+ base.OnConfiguring(optionsBuilder);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dg.dotnet/DG.EntityFramework/EFLoggerProvider.cs b/dg.dotnet/DG.EntityFramework/EFLoggerProvider.cs
new file mode 100644
index 0000000..e0968a3
--- /dev/null
+++ b/dg.dotnet/DG.EntityFramework/EFLoggerProvider.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Extensions.Logging;
+
+namespace DG.EntityFramework
+{
+ public class EFLoggerProvider : ILoggerProvider
+ {
+ public ILogger CreateLogger(string categoryName) => new EFLogger(categoryName);
+
+ public void Dispose()
+ {
+ }
+ }
+
+ public class EFLogger : ILogger
+ {
+ private readonly string categoryName;
+
+ public EFLogger(string categoryName) => this.categoryName = categoryName;
+
+ public bool IsEnabled(LogLevel logLevel) => true;
+
+ public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter)
+ {
+ //ef core执行数据库查询时的categoryName为Microsoft.EntityFrameworkCore.Database.Command,日志级别为Information
+ if (categoryName != "Microsoft.EntityFrameworkCore.Database.Command" ||
+ logLevel != LogLevel.Information) return;
+ var logContent = formatter(state, exception);
+ Console.WriteLine("<------------------ sql start ------------------>");
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.Write("sql: ");
+ Console.ResetColor();
+ Console.Write(logContent);
+ Console.ResetColor();
+ Console.WriteLine();
+ Console.WriteLine("<------------------ sql end ------------------>");
+ }
+
+ public IDisposable BeginScope(TState state) => null;
+ }
+}
\ No newline at end of file
diff --git a/dg.dotnet/DG.EntityFramework/IDbContextProvider.cs b/dg.dotnet/DG.EntityFramework/IDbContextProvider.cs
new file mode 100644
index 0000000..6503b12
--- /dev/null
+++ b/dg.dotnet/DG.EntityFramework/IDbContextProvider.cs
@@ -0,0 +1,18 @@
+using Microsoft.EntityFrameworkCore;
+
+namespace DG.EntityFramework
+{
+ ///
+ ///
+ ///
+ ///
+ public interface IDbContextProvider
+ where TDbContext : DbContext
+ {
+ ///
+ ///
+ ///
+ ///
+ TDbContext GetDbContext();
+ }
+}
\ No newline at end of file
diff --git a/dg.dotnet/DG.EntityFramework/Repository/BaseRepository.cs b/dg.dotnet/DG.EntityFramework/Repository/BaseRepository.cs
new file mode 100644
index 0000000..e3371f9
--- /dev/null
+++ b/dg.dotnet/DG.EntityFramework/Repository/BaseRepository.cs
@@ -0,0 +1,394 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.Extensions.DependencyInjection;
+using System.Data;
+using System.Data.Common;
+using System.Diagnostics.CodeAnalysis;
+
+
+namespace DG.EntityFramework
+{
+ public class BaseRepository : IBaseRepository
+ where TDbContext : DbContext
+ {
+ private readonly IServiceProvider _serviceProvider;
+ private readonly TDbContext _context;
+ public BaseRepository(
+ IServiceProvider serviceProvider,
+ TDbContext dbContext
+ )
+ {
+ _serviceProvider = serviceProvider;
+ _context = dbContext;
+ }
+ public IRepositoryBase GetRepository()
+ where TEntity : class
+ => _serviceProvider.GetRequiredService>();
+
+
+ public async Task BeginTransactionAsync()
+ => await _context.Database.BeginTransactionAsync();
+
+ ///
+ /// 创建SqlCommand
+ ///
+ ///
+ ///
+ ///
+ ///
+ private async Task CreateCommand(string sql, params object[] parameters)
+ {
+ var conn = _context.Database.GetDbConnection();
+ if (conn.State != ConnectionState.Open)
+ {
+ await conn.OpenAsync();
+ }
+ var cmd = conn.CreateCommand();
+ cmd.CommandText = sql;
+ cmd.Parameters.AddRange(parameters);
+ return cmd;
+ }
+
+ ///
+ /// 执行存储过程的扩展方法ExecuteSqlCommand
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task ExecuteSqlCommandNonQueryAsync(CommandType commandType, string sql, [NotNull] params object[] parameters)
+ {
+ //创建SqlCommand
+ var command = await CreateCommand(sql, parameters);
+ var conn = _context.Database.GetDbConnection();
+ var transaction = _context.Database.CurrentTransaction?.GetDbTransaction();
+
+ if (conn.State != ConnectionState.Open)
+ {
+ await conn.OpenAsync();
+ }
+ _context.Database.UseTransaction(transaction);
+ command.CommandType = commandType;
+ if (transaction != null)
+ {
+ command.Transaction = transaction;
+ }
+ return await command.ExecuteNonQueryAsync();
+ }
+
+ public async Task> ExecuteSqlCommandAsync(CommandType commandType, string sql, [NotNull] params object[] parameters)
+ where T : class, new()
+ {
+ //创建SqlCommand
+ var command = await CreateCommand(sql, parameters);
+ var conn = _context.Database.GetDbConnection();
+ var transaction = _context.Database.CurrentTransaction?.GetDbTransaction();
+ if (conn.State != ConnectionState.Open)
+ {
+ await conn.OpenAsync();
+ }
+ _context.Database.UseTransaction(transaction);
+ command.CommandType = commandType;
+ if (transaction != null)
+ {
+ command.Transaction = transaction;
+ }
+ var reader = await command.ExecuteReaderAsync();
+ List list = new();
+ Type type = typeof(T);
+ if (reader.HasRows)
+ {
+
+ while (reader.Read())
+ {
+ var note = Activator.CreateInstance(type);
+ var columns = reader.GetColumnSchema();
+ foreach (var item in type.GetProperties())
+ {
+ if (!columns.Any(x => x.ColumnName.ToLower() == item.Name.ToLower())) continue;
+ var value = reader[item.Name];
+ if (!item.CanWrite || value is DBNull || value == DBNull.Value) continue;
+ try
+ {
+ #region SetValue
+ switch (item.PropertyType.ToString())
+ {
+ case "System.String":
+ item.SetValue(note, Convert.ToString(value), null);
+ break;
+ case "System.Int32":
+ item.SetValue(note, Convert.ToInt32(value), null);
+ break;
+ case "System.Int64":
+ item.SetValue(note, Convert.ToInt64(value), null);
+ break;
+ case "System.DateTime":
+ item.SetValue(note, Convert.ToDateTime(value), null);
+ break;
+ case "System.Boolean":
+ item.SetValue(note, Convert.ToBoolean(value), null);
+ break;
+ case "System.Double":
+ item.SetValue(note, Convert.ToDouble(value), null);
+ break;
+ case "System.Decimal":
+ item.SetValue(note, Convert.ToDecimal(value), null);
+ break;
+ default:
+ item.SetValue(note, value, null);
+ break;
+ }
+ #endregion
+ }
+ catch
+ {
+ //throw (new Exception(ex.Message));
+ }
+ }
+
+ list.Add(note as T);
+ }
+ await reader.CloseAsync();
+ await conn.CloseAsync();
+ return list;
+ }
+ return list;
+ }
+
+ public async Task> ExecuteSqlToListAsync(string sql, [MaybeNull] params object[] parameters)
+ where T : class, new()
+ {
+ //创建SqlCommand
+ var command = await CreateCommand(sql, parameters);
+ var conn = _context.Database.GetDbConnection();
+ if (conn.State != ConnectionState.Open)
+ {
+ await conn.OpenAsync();
+ }
+ if (conn.State == ConnectionState.Open)
+ {
+ var reader = await command.ExecuteReaderAsync();
+ List list = new();
+ Type type = typeof(T);
+ if (reader.HasRows)
+ {
+
+ while (reader.Read())
+ {
+ var note = Activator.CreateInstance(type);
+ var columns = reader.GetColumnSchema();
+ foreach (var item in type.GetProperties())
+ {
+ if (!columns.Any(x => x.ColumnName.ToLower() == item.Name.ToLower())) continue;
+ var value = reader[item.Name];
+ if (!item.CanWrite || value is DBNull || value == DBNull.Value) continue;
+ try
+ {
+ #region SetValue
+ switch (item.PropertyType.ToString())
+ {
+ case "System.String":
+ item.SetValue(note, Convert.ToString(value), null);
+ break;
+ case "System.Int32":
+ case "System.Nullable`1[System.Int32]":
+ item.SetValue(note, Convert.ToInt32(value), null);
+ break;
+ case "System.Int64":
+ case "System.Nullable`1[System.Int64]":
+ item.SetValue(note, Convert.ToInt64(value), null);
+ break;
+ case "System.DateTime":
+ case "System.Nullable`1[System.DateTime]":
+ item.SetValue(note, Convert.ToDateTime(value), null);
+ break;
+ case "System.Boolean":
+ case "System.Nullable`1[System.Boolean]":
+ item.SetValue(note, Convert.ToBoolean(value), null);
+ break;
+ case "System.Double":
+ case "System.Nullable`1[System.Double]":
+ item.SetValue(note, Convert.ToDouble(value), null);
+ break;
+ case "System.Decimal":
+ case "System.Nullable`1[System.Decimal]":
+ item.SetValue(note, Convert.ToDecimal(value), null);
+ break;
+ default:
+ item.SetValue(note, value, null);
+ break;
+ }
+ #endregion
+ }
+ catch
+ {
+ //throw (new Exception(ex.Message));
+ }
+ }
+
+ list.Add(note as T);
+ }
+ await reader.CloseAsync();
+ await conn.CloseAsync();
+ return list;
+ }
+ return list;
+
+ }
+ return new List();
+ }
+
+ public async Task ExecuteSqlToEntityAsync(string sql, [MaybeNull] params object[] parameters)
+ where T : class, new()
+ {
+ //创建SqlCommand
+ var command = await CreateCommand(sql, parameters);
+ var conn = _context.Database.GetDbConnection();
+ if (conn.State != ConnectionState.Open)
+ {
+ await conn.OpenAsync();
+ }
+ if (conn.State == ConnectionState.Open)
+ {
+ var reader = await command.ExecuteReaderAsync();
+ T entity = new();
+ Type type = typeof(T);
+ if (reader.HasRows)
+ {
+
+ while (reader.Read())
+ {
+ var note = Activator.CreateInstance(type);
+ var columns = reader.GetColumnSchema();
+ foreach (var item in type.GetProperties())
+ {
+ if (!columns.Any(x => x.ColumnName.ToLower() == item.Name.ToLower())) continue;
+ var value = reader[item.Name];
+ if (!item.CanWrite || value is DBNull || value == DBNull.Value) continue;
+ try
+ {
+ #region SetValue
+ switch (item.PropertyType.ToString())
+ {
+ case "System.String":
+ item.SetValue(note, Convert.ToString(value), null);
+ break;
+ case "System.Int32":
+ case "System.Nullable`1[System.Int32]":
+ item.SetValue(note, Convert.ToInt32(value), null);
+ break;
+ case "System.Int64":
+ case "System.Nullable`1[System.Int64]":
+ item.SetValue(note, Convert.ToInt64(value), null);
+ break;
+ case "System.DateTime":
+ case "System.Nullable`1[System.DateTime]":
+ item.SetValue(note, Convert.ToDateTime(value), null);
+ break;
+ case "System.Boolean":
+ case "System.Nullable`1[System.Boolean]":
+ item.SetValue(note, Convert.ToBoolean(value), null);
+ break;
+ case "System.Double":
+ case "System.Nullable`1[System.Double]":
+ item.SetValue(note, Convert.ToDouble(value), null);
+ break;
+ case "System.Decimal":
+ case "System.Nullable`1[System.Decimal]":
+ item.SetValue(note, Convert.ToDecimal(value), null);
+ break;
+ default:
+ item.SetValue(note, value, null);
+ break;
+ }
+ #endregion
+ }
+ catch
+ {
+ //throw (new Exception(ex.Message));
+ }
+ }
+
+ entity = note as T;
+ }
+ await reader.CloseAsync();
+ await conn.CloseAsync();
+ return entity;
+ }
+ return entity;
+
+ }
+ return new T();
+ }
+
+ public async Task ExecuteSqlToCountAsync(string sql, [MaybeNull] params object[] parameters)
+ {
+ //创建SqlCommand
+ var command = await CreateCommand(sql, parameters);
+ var conn = _context.Database.GetDbConnection();
+ if (conn.State != ConnectionState.Open)
+ {
+ await conn.OpenAsync();
+ }
+ if (conn.State == ConnectionState.Open)
+ {
+ var reader = await command.ExecuteReaderAsync();
+ if (reader.HasRows)
+ {
+
+ while (reader.Read())
+ {
+ var columns = reader.GetColumnSchema();
+ var value = reader[0];
+ if (value is int)
+ {
+ return (int)value;
+ }
+ }
+ await reader.CloseAsync();
+ await conn.CloseAsync();
+ return 0;
+ }
+ return 0;
+
+ }
+ return 0;
+ }
+
+ public async Task ExecuteSqlToCountLongAsync(string sql, [MaybeNull] params object[] parameters)
+ {
+ //创建SqlCommand
+ var command = await CreateCommand(sql, parameters);
+ var conn = _context.Database.GetDbConnection();
+ var result = 0L;
+ if (conn.State != ConnectionState.Open)
+ {
+ await conn.OpenAsync();
+ }
+ if (conn.State == ConnectionState.Open)
+ {
+ var reader = await command.ExecuteReaderAsync();
+ if (reader.HasRows)
+ {
+
+ while (reader.Read())
+ {
+ var value = reader[0];
+ if (value is long)
+ {
+ result = (long)value;
+ }
+ }
+ }
+ await reader.CloseAsync();
+ await conn.CloseAsync();
+ }
+ return result;
+ }
+
+ public async Task BeginTransactionAsync(IsolationLevel isolationLevel)
+ => await _context.Database.BeginTransactionAsync(isolationLevel);
+
+
+ }
+}
diff --git a/dg.dotnet/DG.EntityFramework/Repository/IBaseRepository.cs b/dg.dotnet/DG.EntityFramework/Repository/IBaseRepository.cs
new file mode 100644
index 0000000..e3edbc6
--- /dev/null
+++ b/dg.dotnet/DG.EntityFramework/Repository/IBaseRepository.cs
@@ -0,0 +1,64 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Storage;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.Common;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace DG.EntityFramework
+{
+ public interface IBaseRepository
+ where TDbContext : DbContext
+ {
+ IRepositoryBase GetRepository()
+ where TEntity : class;
+
+ ///
+ /// 执行存储过程的扩展方法ExecuteSqlCommand
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task ExecuteSqlCommandNonQueryAsync(CommandType commandType, string sql, [NotNull] params object[] parameters);
+
+ ///
+ /// 执行存储过程的扩展方法ExecuteSqlCommand
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task> ExecuteSqlCommandAsync(CommandType commandType, string sql, [NotNull] params object[] parameters)
+ where T : class, new();
+
+ ///
+ /// 开启事务
+ ///
+ ///
+ Task BeginTransactionAsync();
+
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task BeginTransactionAsync(IsolationLevel isolationLevel);
+
+ Task> ExecuteSqlToListAsync(string sql, [MaybeNull] params object[] parameters)
+ where T : class, new();
+
+ Task