init
This commit is contained in:
commit
a9c888e9e7
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
|
<Version>1.1.6</Version>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
|
|
@ -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
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public class JsonOptionsExtensions : JsonConverter<DateTime>
|
||||||
|
{
|
||||||
|
private readonly string Format;
|
||||||
|
public JsonOptionsExtensions(string format = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
}
|
||||||
|
public override void Write(Utf8JsonWriter writer, DateTime date, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(date.ToString(Format));
|
||||||
|
}
|
||||||
|
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
// 获取时间类型的字符串
|
||||||
|
var dt = reader.GetString();
|
||||||
|
if (!string.IsNullOrEmpty(dt))
|
||||||
|
{
|
||||||
|
//将日期与时间之间的"T"替换为一个空格,将结尾的"Z"去掉,否则会报错
|
||||||
|
dt = dt.Replace("T", " ").Replace("Z", "");
|
||||||
|
//取到秒,毫秒内容也要去掉,经过测试,不去掉会报错
|
||||||
|
if (dt.Length > 19)
|
||||||
|
{
|
||||||
|
dt = dt.Substring(0, 19);
|
||||||
|
}
|
||||||
|
return DateTime.ParseExact(dt, Format, null);
|
||||||
|
}
|
||||||
|
return DateTime.Now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public static class LinqMethodExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 使用自定linq扩展执行排序,查询,分页功能 item1: 未分页结果,item2:分页后的结果
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="source"></param>
|
||||||
|
/// <param name="coditionEntity"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IQueryable<T> UseCoditionFind<T>(this IQueryable<T> source, bool condition, Action<IQueryable<T>> action)
|
||||||
|
{
|
||||||
|
if (condition)
|
||||||
|
{
|
||||||
|
action(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public static class PredicateExtensionses
|
||||||
|
{
|
||||||
|
public static Expression<Func<T, bool>> True<T>() { return f => true; }
|
||||||
|
|
||||||
|
public static Expression<Func<T, bool>> False<T>() { return f => false; }
|
||||||
|
|
||||||
|
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expLeft, Expression<Func<T, bool>> expRight)
|
||||||
|
{
|
||||||
|
var candidateExpr = Expression.Parameter(typeof(T), "candidate");
|
||||||
|
var parameterReplacer = new ParameterReplacer(candidateExpr);
|
||||||
|
|
||||||
|
var left = parameterReplacer.Replace(expLeft.Body);
|
||||||
|
var right = parameterReplacer.Replace(expRight.Body);
|
||||||
|
var body = Expression.And(left, right);
|
||||||
|
|
||||||
|
return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expLeft, Expression<Func<T, bool>> expRight)
|
||||||
|
{
|
||||||
|
var candidateExpr = Expression.Parameter(typeof(T), "candidate");
|
||||||
|
var parameterReplacer = new ParameterReplacer(candidateExpr);
|
||||||
|
|
||||||
|
var left = parameterReplacer.Replace(expLeft.Body);
|
||||||
|
var right = parameterReplacer.Replace(expRight.Body);
|
||||||
|
var body = Expression.OrElse(left, right);
|
||||||
|
|
||||||
|
return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// And ((a or b )and (x or d))关系,但是and后面里面的关系是Or的关系,如 a.resid='' and ((a.channel>=1 and a.channel<10) or (a.channel>=50 and a.channel<60))
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="expLeft"></param>
|
||||||
|
/// <param name="expRight"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Expression<Func<T, bool>> AndListOr<T>(this Expression<Func<T, bool>> expLeft, Expression<Func<T, bool>>[] predicates)
|
||||||
|
{
|
||||||
|
var candidateExpr = Expression.Parameter(typeof(T), "candidate");
|
||||||
|
var parameterReplacer = new ParameterReplacer(candidateExpr);
|
||||||
|
var left = parameterReplacer.Replace(expLeft.Body);
|
||||||
|
Expression<Func<T, bool>> lambda = predicates[0];
|
||||||
|
for (int i = 1; i < predicates.Length; i++)
|
||||||
|
{
|
||||||
|
lambda = lambda.Or(predicates[i]);
|
||||||
|
}
|
||||||
|
var right = parameterReplacer.Replace(lambda.Body);
|
||||||
|
var body = Expression.And(left, right);
|
||||||
|
|
||||||
|
return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 传入条件之间为OR查询
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="source"></param>
|
||||||
|
/// <param name="predicates"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IQueryable<T> WhereOR<T>(this IQueryable<T> source, params Expression<Func<T, bool>>[] predicates)
|
||||||
|
{
|
||||||
|
if (source == null) throw new ArgumentNullException("source");
|
||||||
|
if (predicates == null) throw new ArgumentNullException("predicates");
|
||||||
|
if (predicates.Length == 0) return source.Where(x => true);
|
||||||
|
if (predicates.Length == 1) return source.Where(predicates[0]);
|
||||||
|
|
||||||
|
var param = Expression.Parameter(typeof(T), "x");
|
||||||
|
Expression body = Expression.Invoke(predicates[0], param);
|
||||||
|
for (int i = 1; i < predicates.Length; i++)
|
||||||
|
{
|
||||||
|
body = Expression.OrElse(body, Expression.Invoke(predicates[i], param));
|
||||||
|
}
|
||||||
|
var lambda = Expression.Lambda<Func<T, bool>>(body, param);
|
||||||
|
return source.Where(lambda);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ParameterReplacer : ExpressionVisitor
|
||||||
|
{
|
||||||
|
public ParameterReplacer(ParameterExpression paramExpr)
|
||||||
|
{
|
||||||
|
this.ParameterExpression = paramExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParameterExpression ParameterExpression { get; private set; }
|
||||||
|
|
||||||
|
public Expression Replace(Expression expr)
|
||||||
|
{
|
||||||
|
return this.Visit(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Expression VisitParameter(ParameterExpression p)
|
||||||
|
{
|
||||||
|
return this.ParameterExpression;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public static class SystemKeyExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// If extensions
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="t"></param>
|
||||||
|
/// <param name="condition"></param>
|
||||||
|
/// <param name="action"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static T If<T>(this T t, bool condition, Action<T> action) where T : class
|
||||||
|
{
|
||||||
|
if (condition)
|
||||||
|
{
|
||||||
|
action(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If extensions
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="t"></param>
|
||||||
|
/// <param name="predicate"></param>
|
||||||
|
/// <param name="action"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static T If<T>(this T t, Predicate<T> predicate, Action<T> action) where T : class
|
||||||
|
{
|
||||||
|
if (t == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (predicate(t))
|
||||||
|
{
|
||||||
|
action(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If extensions
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="t"></param>
|
||||||
|
/// <param name="condition"></param>
|
||||||
|
/// <param name="func"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static T If<T>(this T t, bool condition, Func<T, T> func) where T : class => condition ? func(t) : t;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If extensions
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="t"></param>
|
||||||
|
/// <param name="predicate"></param>
|
||||||
|
/// <param name="func"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static T If<T>(this T t, Predicate<T> predicate, Func<T, T> func) where T : class => predicate(t) ? func(t) : t;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If and else extensions
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="t"></param>
|
||||||
|
/// <param name="condition"></param>
|
||||||
|
/// <param name="ifAction"></param>
|
||||||
|
/// <param name="elseAction"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static T IfAndElse<T>(this T t, bool condition, Action<T> ifAction, Action<T> elseAction) where T : class
|
||||||
|
{
|
||||||
|
if (condition)
|
||||||
|
{
|
||||||
|
ifAction(t);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
elseAction(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If and else extensions
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="t"></param>
|
||||||
|
/// <param name="predicate"></param>
|
||||||
|
/// <param name="ifAction"></param>
|
||||||
|
/// <param name="elseAction"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static T IfAndElse<T>(this T t, Predicate<T> predicate, Action<T> ifAction, Action<T> elseAction) where T : class
|
||||||
|
{
|
||||||
|
if (t == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (predicate(t))
|
||||||
|
{
|
||||||
|
ifAction(t);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
elseAction(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If and else extensions
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="t"></param>
|
||||||
|
/// <param name="condition"></param>
|
||||||
|
/// <param name="ifFunc"></param>
|
||||||
|
/// <param name="elseFunc"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static T IfAndElse<T>(this T t, bool condition, Func<T, T> ifFunc, Func<T, T> elseFunc) where T : class => condition ? ifFunc(t) : elseFunc(t);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If and else extensions
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="t"></param>
|
||||||
|
/// <param name="predicate"></param>
|
||||||
|
/// <param name="ifFunc"></param>
|
||||||
|
/// <param name="elseFunc"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static T IfAndElse<T>(this T t, Predicate<T> predicate, Func<T, T> ifFunc, Func<T, T> elseFunc) where T : class => predicate(t) ? ifFunc(t) : elseFunc(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<HttpClient> _logger;
|
||||||
|
private static LogLevel _logLevel = LogLevel.Debug;
|
||||||
|
|
||||||
|
public HttpClient(IHttpClientFactory httpClientFactory,
|
||||||
|
ILogger<HttpClient> logger)
|
||||||
|
{
|
||||||
|
_httpClientFactory = httpClientFactory;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JsonSerializerOptions options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
PropertyNameCaseInsensitive = true,
|
||||||
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||||
|
};
|
||||||
|
|
||||||
|
public void ChangeLogLevel(LogLevel logLevel)
|
||||||
|
{
|
||||||
|
_logLevel = logLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Log(string message)
|
||||||
|
{
|
||||||
|
_logger.Log(_logLevel, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Post Security
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="url"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="clientid"></param>
|
||||||
|
/// <param name="accessKey"></param>
|
||||||
|
/// <param name="iv"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<T> PostSecurityAsync<T>(string url, object data, string clientid, string accessKey, string iv)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var timeStamp = GetTimeStamp();
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
var param = JsonSerializer.Serialize(data, options);
|
||||||
|
var bodyJson = EncryptByAES(param, accessKey, iv);
|
||||||
|
var sign = SignData(bodyJson, accessKey);
|
||||||
|
var client = _httpClientFactory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.TryAddWithoutValidation("clientid", clientid);
|
||||||
|
client.DefaultRequestHeaders.Add("sign", sign);
|
||||||
|
var httpData = new StringContent(bodyJson, Encoding.UTF8, "application/json");
|
||||||
|
Log($"POST 请求Url:{url}, Body:{bodyJson}");
|
||||||
|
var httpResponse = await client.PostAsync($"{url}", httpData);
|
||||||
|
var stream = await httpResponse.Content.ReadAsStringAsync();
|
||||||
|
Log($"请求结果:{stream}");
|
||||||
|
var response = JsonSerializer.Deserialize<T>(stream, options);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "POST 方法请求错误!");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string SignData(string ciphertext, string accessKey)
|
||||||
|
{
|
||||||
|
Encoding utf = new UTF8Encoding();
|
||||||
|
HMACMD5 hmac = new HMACMD5(utf.GetBytes(accessKey));
|
||||||
|
byte[] hashValue = hmac.ComputeHash(utf.GetBytes(ciphertext));
|
||||||
|
return Convert.ToBase64String(hashValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="url"></param>
|
||||||
|
/// <param name="param"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="clientid"></param>
|
||||||
|
/// <param name="accessKey"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<T> PostSecurityAsync<T>(string url, object param, object data, string clientid, string accessKey)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var timeStamp = GetTimeStamp();
|
||||||
|
|
||||||
|
var paramStr = JsonSerializer.Serialize(param, options);
|
||||||
|
var bodyJson = JsonSerializer.Serialize(data, options);
|
||||||
|
var content = EncyptData(paramStr, accessKey);
|
||||||
|
var sign = SignData(content, accessKey);
|
||||||
|
var client = _httpClientFactory.CreateClient();
|
||||||
|
client.Timeout = TimeSpan.FromSeconds(30);
|
||||||
|
var httpData = new StringContent(bodyJson, Encoding.UTF8, "application/json");
|
||||||
|
url = $"{url}?content={HttpUtility.UrlEncode(content)}&sign={HttpUtility.UrlEncode(sign, Encoding.UTF8)}&clientid={clientid}";
|
||||||
|
Log($"POST 请求Url:{url}, Body:{bodyJson}");
|
||||||
|
var httpResponse = await client.PostAsync(url, httpData);
|
||||||
|
var stream = await httpResponse.Content.ReadAsStringAsync();
|
||||||
|
Log($"请求结果:{stream}");
|
||||||
|
var response = JsonSerializer.Deserialize<T>(stream, options);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "POST 方法请求错误!");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Post Security
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="url"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="clientid"></param>
|
||||||
|
/// <param name="accessKey"></param>
|
||||||
|
/// <param name="iv"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<string> PostSecurityAsync(string url, object data, string clientid, string accessKey, string iv)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var timeStamp = GetTimeStamp();
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
var param = JsonSerializer.Serialize(data, options);
|
||||||
|
var bodyJson = EncryptByAES(param, accessKey, iv);
|
||||||
|
var sign = SignData(bodyJson, accessKey);
|
||||||
|
var client = _httpClientFactory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.TryAddWithoutValidation("clientid", clientid);
|
||||||
|
client.DefaultRequestHeaders.Add("sign", sign);
|
||||||
|
var httpData = new StringContent(bodyJson, Encoding.UTF8, "application/json");
|
||||||
|
Log($"POST 请求Url:{url}, Body:{bodyJson}");
|
||||||
|
var httpResponse = await client.PostAsync($"{url}", httpData);
|
||||||
|
var response = await httpResponse.Content.ReadAsStringAsync();
|
||||||
|
Log($"请求结果:{response}");
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "POST 方法请求错误!");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="url"></param>
|
||||||
|
/// <param name="param"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="clientid"></param>
|
||||||
|
/// <param name="accessKey"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<string> PostSecurityAsync(string url, object param, object data, string clientid, string accessKey)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var timeStamp = GetTimeStamp();
|
||||||
|
var paramStr = JsonSerializer.Serialize(param, options);
|
||||||
|
var bodyJson = JsonSerializer.Serialize(data, options);
|
||||||
|
var content = EncyptData(paramStr, accessKey);
|
||||||
|
var sign = SignData(content, accessKey);
|
||||||
|
var client = _httpClientFactory.CreateClient();
|
||||||
|
var httpData = new StringContent(bodyJson, Encoding.UTF8, "application/json");
|
||||||
|
url = $"{url}?content={HttpUtility.UrlEncode(content)}&sign={HttpUtility.UrlEncode(sign, Encoding.UTF8)}&clientid={clientid}";
|
||||||
|
Log($"POST 请求Url:{url}, Body:{bodyJson}");
|
||||||
|
var httpResponse = await client.PostAsync(url, httpData);
|
||||||
|
var response = await httpResponse.Content.ReadAsStringAsync();
|
||||||
|
Log($"请求结果:{response}");
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "POST 方法请求错误!");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<T> UploadFileAsync<T>(string url, string fileName, string fullName, Dictionary<string, string>? headers = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var buffer = await File.ReadAllBytesAsync(fullName);
|
||||||
|
var client = _httpClientFactory.CreateClient();
|
||||||
|
if (headers != null)
|
||||||
|
{
|
||||||
|
foreach (var header in headers)
|
||||||
|
{
|
||||||
|
client.DefaultRequestHeaders.Add(header.Key, header.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ByteArrayContent fileContent = new ByteArrayContent(buffer);
|
||||||
|
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") { Name = "file", FileName = fileName };
|
||||||
|
MultipartFormDataContent content = new MultipartFormDataContent
|
||||||
|
{
|
||||||
|
fileContent
|
||||||
|
};
|
||||||
|
Log($"UploadFile 文件上传,请求Url:{url}");
|
||||||
|
var httpResponse = await client.PostAsync(url, content);
|
||||||
|
var stream = await httpResponse.Content.ReadAsStringAsync();
|
||||||
|
Log($"请求结果:{stream}");
|
||||||
|
var response = JsonSerializer.Deserialize<T>(stream, options);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "UploadFile 方法请求错误!");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> UploadFileAsync(string url, string fileName, string fullName, Dictionary<string, string>? headers = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var buffer = await File.ReadAllBytesAsync(fullName);
|
||||||
|
var client = _httpClientFactory.CreateClient();
|
||||||
|
if (headers != null)
|
||||||
|
{
|
||||||
|
foreach (var header in headers)
|
||||||
|
{
|
||||||
|
client.DefaultRequestHeaders.Add(header.Key, header.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ByteArrayContent fileContent = new ByteArrayContent(buffer);
|
||||||
|
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") { Name = "file", FileName = fileName };
|
||||||
|
MultipartFormDataContent content = new MultipartFormDataContent
|
||||||
|
{
|
||||||
|
fileContent
|
||||||
|
};
|
||||||
|
Log($"UploadFile 文件上传,请求Url:{url}");
|
||||||
|
var httpResponse = await client.PostAsync(url, content);
|
||||||
|
var response = await httpResponse.Content.ReadAsStringAsync();
|
||||||
|
Log($"请求结果:{response}");
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "UploadFile 方法请求错误!");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region 正常请求
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Post
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="url"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="appId"></param>
|
||||||
|
/// <param name="appSecret"></param>
|
||||||
|
/// <param name="mediaType"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<T> PostAsync<T>(string url, object? data = null, string? appId = "", string? appSecret = "", string? mediaType = "application/json")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var client = _httpClientFactory.CreateClient();
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
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<T>(stream, options);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "POST 方法请求错误!");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> PostAsync(string url, object? data = null, string? appId = "", string? appSecret = "", string? mediaType = "application/json")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var client = _httpClientFactory.CreateClient();
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
var bodyJson = data != null ? JsonSerializer.Serialize(data, options) : "";
|
||||||
|
if (!string.IsNullOrEmpty(appId))
|
||||||
|
{
|
||||||
|
client.DefaultRequestHeaders.Add("appid", appId);
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(appId) && !string.IsNullOrEmpty(appSecret))
|
||||||
|
{
|
||||||
|
var timeStamp = GetTimeStamp();
|
||||||
|
var sign = CreateSign(appId, bodyJson, appSecret, timeStamp);
|
||||||
|
var authorization = $"{appId}:{sign}";
|
||||||
|
client.DefaultRequestHeaders.TryAddWithoutValidation("authorization", authorization);
|
||||||
|
client.DefaultRequestHeaders.Add("timestamps", timeStamp);
|
||||||
|
}
|
||||||
|
var httpData = new StringContent(bodyJson, Encoding.UTF8, mediaType);
|
||||||
|
Log($"POST 请求Url:{url}, Body:{bodyJson}");
|
||||||
|
var httpResponse = await client.PostAsync($"{url}", httpData);
|
||||||
|
var response = await httpResponse.Content.ReadAsStringAsync();
|
||||||
|
Log($"请求结果:{response}");
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "POST 方法请求错误!");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="url"></param>
|
||||||
|
/// <param name="appId"></param>
|
||||||
|
/// <param name="appSecret"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<T> GetAsync<T>(string url, string appId = "", string appSecret = "", int timeout = 10000)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var client = _httpClientFactory.CreateClient();
|
||||||
|
client.Timeout = TimeSpan.FromMilliseconds(timeout);
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
if (!string.IsNullOrEmpty(appId))
|
||||||
|
{
|
||||||
|
client.DefaultRequestHeaders.Add("appid", appId);
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(appId) && !string.IsNullOrEmpty(appSecret))
|
||||||
|
{
|
||||||
|
var uri = new Uri(url);
|
||||||
|
var query = uri.Query;
|
||||||
|
var param = new Dictionary<string, object>();
|
||||||
|
if (query != null)
|
||||||
|
{
|
||||||
|
foreach (var item in query.Split('&'))
|
||||||
|
{
|
||||||
|
var sp = item.Split("=");
|
||||||
|
if (sp.Count() > 1)
|
||||||
|
{
|
||||||
|
param.Add(sp[0].Replace("?", ""), sp[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var timeStamp = GetTimeStamp();
|
||||||
|
param = param.OrderBy(m => m.Key).ToDictionary(m => m.Key, n => n.Value);
|
||||||
|
var paramStr = JsonSerializer.Serialize(param, options);
|
||||||
|
var sign = CreateSign(appId, paramStr, appSecret, timeStamp);
|
||||||
|
|
||||||
|
var authorization = $"{appId}:{sign}";
|
||||||
|
client.DefaultRequestHeaders.TryAddWithoutValidation("authorization", authorization);
|
||||||
|
client.DefaultRequestHeaders.Add("timestamps", timeStamp);
|
||||||
|
}
|
||||||
|
Log($"GET 请求Url:{url}");
|
||||||
|
var httpResponse = await client.GetAsync($"{url}");
|
||||||
|
var stream = await httpResponse.Content.ReadAsStringAsync();
|
||||||
|
Log($"请求结果:{stream}");
|
||||||
|
var response = JsonSerializer.Deserialize<T>(stream, options);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "GET 方法请求错误!");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="url"></param>
|
||||||
|
/// <param name="param"></param>
|
||||||
|
/// <param name="appId"></param>
|
||||||
|
/// <param name="appSecret"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<T> GetAsync<T>(string url, Dictionary<string, object> param, string appId = "", string appSecret = "")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var client = _httpClientFactory.CreateClient();
|
||||||
|
client.Timeout = TimeSpan.FromSeconds(30);
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
var urlParam = string.Join("&", param.Select(m => m.Key + "=" + m.Value));
|
||||||
|
if (url.IndexOf('?') > -1)
|
||||||
|
{
|
||||||
|
url += urlParam;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
url = url + "?" + urlParam;
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(appId))
|
||||||
|
{
|
||||||
|
client.DefaultRequestHeaders.Add("appid", appId);
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(appId) && !string.IsNullOrEmpty(appSecret))
|
||||||
|
{
|
||||||
|
var timeStamp = GetTimeStamp();
|
||||||
|
param = param.OrderBy(m => m.Key).ToDictionary(m => m.Key, n => n.Value);
|
||||||
|
var paramStr = JsonSerializer.Serialize(param, options);
|
||||||
|
var sign = CreateSign(appId, paramStr, appSecret, timeStamp);
|
||||||
|
var authorization = $"{appId}:{sign}";
|
||||||
|
client.DefaultRequestHeaders.TryAddWithoutValidation("authorization", authorization);
|
||||||
|
client.DefaultRequestHeaders.Add("timestamps", timeStamp);
|
||||||
|
}
|
||||||
|
Log($"GET 请求Url:{url}");
|
||||||
|
var httpResponse = await client.GetAsync($"{url}");
|
||||||
|
var stream = await httpResponse.Content.ReadAsStringAsync();
|
||||||
|
Log($"请求结果:{stream}");
|
||||||
|
var response = JsonSerializer.Deserialize<T>(stream, options);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "GET 方法请求错误!");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="url"></param>
|
||||||
|
/// <param name="appId"></param>
|
||||||
|
/// <param name="appSecret"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<string> GetAsync(string url, string appId = "", string appSecret = "")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var client = _httpClientFactory.CreateClient();
|
||||||
|
client.Timeout = TimeSpan.FromSeconds(30);
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
if (!string.IsNullOrEmpty(appId) && !string.IsNullOrEmpty(appSecret))
|
||||||
|
{
|
||||||
|
var uri = new Uri(url);
|
||||||
|
var query = uri.Query;
|
||||||
|
var param = new Dictionary<string, object>();
|
||||||
|
if (query != null)
|
||||||
|
{
|
||||||
|
foreach (var item in query.Split('&'))
|
||||||
|
{
|
||||||
|
var sp = item.Split("=");
|
||||||
|
if (sp.Count() > 1)
|
||||||
|
{
|
||||||
|
param.Add(sp[0].Replace("?", ""), sp[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var timeStamp = GetTimeStamp();
|
||||||
|
param = param.OrderBy(m => m.Key).ToDictionary(m => m.Key, n => n.Value);
|
||||||
|
var paramStr = JsonSerializer.Serialize(param, options);
|
||||||
|
var sign = CreateSign(appId, paramStr, appSecret, timeStamp);
|
||||||
|
|
||||||
|
var authorization = $"{appId}:{sign}";
|
||||||
|
client.DefaultRequestHeaders.TryAddWithoutValidation("authorization", authorization);
|
||||||
|
client.DefaultRequestHeaders.Add("timestamps", timeStamp);
|
||||||
|
}
|
||||||
|
Log($"GET 请求Url:{url}");
|
||||||
|
var httpResponse = await client.GetAsync($"{url}");
|
||||||
|
var response = await httpResponse.Content.ReadAsStringAsync();
|
||||||
|
Log($"请求结果:{response}");
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "GET 方法请求错误!");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="url"></param>
|
||||||
|
/// <param name="param"></param>
|
||||||
|
/// <param name="appId"></param>
|
||||||
|
/// <param name="appSecret"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<string> GetAsync(string url, Dictionary<string, object> param, string appId = "", string appSecret = "")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var client = _httpClientFactory.CreateClient();
|
||||||
|
client.Timeout = TimeSpan.FromSeconds(30);
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
var urlParam = string.Join("&", param.Select(m => m.Key + "=" + m.Value));
|
||||||
|
if (url.IndexOf('?') > -1)
|
||||||
|
{
|
||||||
|
url += urlParam;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
url = url + "?" + urlParam;
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(appId))
|
||||||
|
{
|
||||||
|
client.DefaultRequestHeaders.Add("appid", appId);
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(appId) && !string.IsNullOrEmpty(appSecret))
|
||||||
|
{
|
||||||
|
var timeStamp = GetTimeStamp();
|
||||||
|
param = param.OrderBy(m => m.Key).ToDictionary(m => m.Key, n => n.Value);
|
||||||
|
var paramStr = JsonSerializer.Serialize(param, options);
|
||||||
|
var sign = CreateSign(appId, paramStr, appSecret, timeStamp);
|
||||||
|
var authorization = $"{appId}:{sign}";
|
||||||
|
client.DefaultRequestHeaders.TryAddWithoutValidation("authorization", authorization);
|
||||||
|
client.DefaultRequestHeaders.Add("timestamps", timeStamp);
|
||||||
|
}
|
||||||
|
Log($"GET 请求Url:{url}");
|
||||||
|
var httpResponse = await client.GetAsync($"{url}");
|
||||||
|
var response = await httpResponse.Content.ReadAsStringAsync();
|
||||||
|
Log($"请求结果:{response}");
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "GET 方法请求错误!");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 生成签名
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="appId"></param>
|
||||||
|
/// <param name="bodyJson"></param>
|
||||||
|
/// <param name="secret"></param>
|
||||||
|
/// <param name="timestamps"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static string CreateSign(string appId, string bodyJson, string secret, string timestamps)
|
||||||
|
{
|
||||||
|
var enStrList = new string[] { appId, bodyJson, secret, timestamps };
|
||||||
|
Array.Sort(enStrList, string.CompareOrdinal);
|
||||||
|
var enStr = string.Join("", enStrList);
|
||||||
|
var md = GetMd5Hash(enStr);
|
||||||
|
return md;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 计算 md5
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="enCode"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static string GetMd5Hash(string enCode)
|
||||||
|
{
|
||||||
|
string res = "";
|
||||||
|
byte[] data = Encoding.GetEncoding("utf-8").GetBytes(enCode);
|
||||||
|
MD5 md5 = MD5.Create();
|
||||||
|
byte[] bytes = md5.ComputeHash(data);
|
||||||
|
for (int i = 0; i < bytes.Length; i++)
|
||||||
|
{
|
||||||
|
res += bytes[i].ToString("x2");
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取时间戳
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetTimeStamp()
|
||||||
|
{
|
||||||
|
TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0);
|
||||||
|
return Convert.ToInt64(ts.TotalSeconds).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 加密
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ciphertext"></param>
|
||||||
|
/// <param name="accessKey"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static string EncyptData(string ciphertext, string accessKey)
|
||||||
|
{
|
||||||
|
SymmetricAlgorithm des = DES.Create();
|
||||||
|
|
||||||
|
Encoding utf = new UTF8Encoding();
|
||||||
|
byte[] key = utf.GetBytes(accessKey);
|
||||||
|
byte[] iv = { 0x75, 0x70, 0x63, 0x68, 0x69, 0x6e, 0x61, 0x31 };
|
||||||
|
ICryptoTransform encryptor = des.CreateEncryptor(key, iv);
|
||||||
|
byte[] data = utf.GetBytes(ciphertext);
|
||||||
|
byte[] encData = encryptor.TransformFinalBlock(data, 0, data.Length);
|
||||||
|
return Convert.ToBase64String(encData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解密
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cryptograph"></param>
|
||||||
|
/// <param name="accessKey"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static string DecyptData(string cryptograph, string accessKey)
|
||||||
|
{
|
||||||
|
SymmetricAlgorithm des = DES.Create();
|
||||||
|
|
||||||
|
Encoding utf = new UTF8Encoding();
|
||||||
|
byte[] key = utf.GetBytes(accessKey);
|
||||||
|
byte[] iv = { 0x75, 0x70, 0x63, 0x68, 0x69, 0x6e, 0x61, 0x31 };
|
||||||
|
ICryptoTransform decryptor = des.CreateDecryptor(key, iv);
|
||||||
|
byte[] encData = Convert.FromBase64String(cryptograph);
|
||||||
|
byte[] data = decryptor.TransformFinalBlock(encData, 0, encData.Length);
|
||||||
|
return utf.GetString(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AES加密算法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">明文字符串</param>
|
||||||
|
/// <returns>字符串</returns>
|
||||||
|
private static string EncryptByAES(string input, string key, string iv)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(input))
|
||||||
|
{
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
Aes aes = Aes.Create();
|
||||||
|
aes.Mode = CipherMode.CBC;
|
||||||
|
aes.Padding = PaddingMode.PKCS7;
|
||||||
|
aes.FeedbackSize = 128;
|
||||||
|
aes.Key = Encoding.UTF8.GetBytes(key);
|
||||||
|
aes.IV = Encoding.UTF8.GetBytes(iv);
|
||||||
|
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
|
||||||
|
using MemoryStream msEncrypt = new();
|
||||||
|
using CryptoStream csEncrypt = new(msEncrypt, encryptor, CryptoStreamMode.Write);
|
||||||
|
using (StreamWriter swEncrypt = new(csEncrypt))
|
||||||
|
{
|
||||||
|
swEncrypt.Write(input);
|
||||||
|
}
|
||||||
|
byte[] bytes = msEncrypt.ToArray();
|
||||||
|
return Convert.ToBase64String(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion 正常请求
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<T> PostSecurityAsync<T>(string url, object data, string clientid, string accessKey, string iv);
|
||||||
|
|
||||||
|
Task<T> PostSecurityAsync<T>(string url, object param, object data, string clientid, string accessKey);
|
||||||
|
|
||||||
|
Task<string> PostSecurityAsync(string url, object data, string clientid, string accessKey, string iv);
|
||||||
|
|
||||||
|
Task<string> PostSecurityAsync(string url, object param, object data, string clientid, string accessKey);
|
||||||
|
|
||||||
|
Task<T> PostAsync<T>(string url, object? data = null, string? appId = "", string? appSecret = "", string? mediaType = "application/json");
|
||||||
|
|
||||||
|
Task<string> PostAsync(string url, object? data = null, string? appId = "", string? appSecret = "", string? mediaType = "application/json");
|
||||||
|
|
||||||
|
Task<T> GetAsync<T>(string url, string appId = "", string appSecret = "", int timeout = 10000);
|
||||||
|
|
||||||
|
Task<T> GetAsync<T>(string url, Dictionary<string, object> param, string appId = "", string appSecret = "");
|
||||||
|
|
||||||
|
Task<string> GetAsync(string url, string appId = "", string appSecret = "");
|
||||||
|
|
||||||
|
Task<string> GetAsync(string url, Dictionary<string, object> param, string appId = "", string appSecret = "");
|
||||||
|
|
||||||
|
Task<T> UploadFileAsync<T>(string url, string fileName, string fullName, Dictionary<string, string>? headers = null);
|
||||||
|
|
||||||
|
Task<string> UploadFileAsync(string url, string fileName, string fullName, Dictionary<string, string>? headers = null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public static class IMvcBuilderApiResultExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 启用API标准返回值模式
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="ArgumentNullException"></exception>
|
||||||
|
public static IMvcBuilder AddApiResult(this IMvcBuilder builder)
|
||||||
|
{
|
||||||
|
if (builder == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(builder));
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.AddMvcOptions(options => {
|
||||||
|
options.Filters.Add(typeof(ApiExceptionFilterAttribute));
|
||||||
|
options.Filters.Add(typeof(ApiResultFilterAttribute));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启用API签名模式
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="ArgumentNullException"></exception>
|
||||||
|
public static IMvcBuilder AddApiSignature(this IMvcBuilder builder)
|
||||||
|
{
|
||||||
|
if (builder == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(builder));
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.AddMvcOptions(options => {
|
||||||
|
options.Filters.Add(typeof(ApiSecurityAsyncFilter));
|
||||||
|
options.Filters.Add(typeof(ApiSignatureAsyncFilterAttribute));
|
||||||
|
options.Filters.Add(typeof(ApiTimeSecurityAsyncFilter));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public interface IMapper
|
||||||
|
{
|
||||||
|
TTarget Map<TSource, TTarget>(TSource source);
|
||||||
|
|
||||||
|
List<TTarget> Map<TSource, TTarget>(List<TSource> source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
internal class Mapper
|
||||||
|
{
|
||||||
|
public static readonly Mapper Instance = new Mapper();
|
||||||
|
|
||||||
|
private Mapper()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public TTarget Map<TSource, TTarget>(TSource source) => CacheModel<TSource, TTarget>.Invoke(source);
|
||||||
|
|
||||||
|
public List<TTarget> Map<TSource, TTarget>(List<TSource> sources) => sources.AsParallel().Select(CacheModel<TSource, TTarget>.Invoke).ToList();
|
||||||
|
|
||||||
|
internal class CacheModel<TSource, TTarget>
|
||||||
|
{
|
||||||
|
private static readonly Func<TSource, TTarget> Func;
|
||||||
|
|
||||||
|
static CacheModel()
|
||||||
|
{
|
||||||
|
var parameterExpression = Expression.Parameter(typeof(TSource), "x");
|
||||||
|
var sourcePropNames = typeof(TSource).GetProperties()
|
||||||
|
.Where(x => !x.IsDefined(typeof(NotMapAttribute), true))
|
||||||
|
.Select(x => x.Name)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
var memberBindings = typeof(TTarget).GetProperties()
|
||||||
|
.Where(x => x.CanWrite && sourcePropNames.Any(y => y.ToUpper() == x.Name.ToUpper()))
|
||||||
|
.Select(x => Expression.Bind(typeof(TTarget).GetProperty(x.Name),
|
||||||
|
Expression.Property(parameterExpression,
|
||||||
|
typeof(TSource).GetProperty(sourcePropNames.FirstOrDefault(y => y.ToUpper() == x.Name.ToUpper())))));
|
||||||
|
|
||||||
|
Func = Expression.Lambda<Func<TSource, TTarget>>(Expression.MemberInit(Expression.New(typeof(TTarget)), memberBindings), parameterExpression).Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TTarget Invoke(TSource source) => Func(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public static class MapperExtendsions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 映射到
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSource"></typeparam>
|
||||||
|
/// <typeparam name="TTarget"></typeparam>
|
||||||
|
/// <param name="source"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static TTarget Map<TSource, TTarget>(this TSource source) => Mapper.Instance.Map<TSource, TTarget>(source);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 映射到
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSource"></typeparam>
|
||||||
|
/// <typeparam name="TTarget"></typeparam>
|
||||||
|
/// <param name="sources"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static List<TTarget> Map<TSource, TTarget>(this List<TSource> sources) => Mapper.Instance.Map<TSource, TTarget>(sources);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 复制到
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSource"></typeparam>
|
||||||
|
/// <typeparam name="TTarget"></typeparam>
|
||||||
|
/// <param name="source"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <remarks>因为重名了,所以对方法取别名,同 MapTo</remarks>
|
||||||
|
public static TTarget Replicate<TSource, TTarget>(this TSource source) => source.Map<TSource, TTarget>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 复制到
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSource"></typeparam>
|
||||||
|
/// <typeparam name="TTarget"></typeparam>
|
||||||
|
/// <param name="sources"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <remarks>因为重名了,所以对方法取别名,同 MapTo</remarks>
|
||||||
|
public static List<TTarget> Replicate<TSource, TTarget>(this List<TSource> sources) => sources.Map<TSource, TTarget>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public class MapperManager : IMapper
|
||||||
|
{
|
||||||
|
public TTarget Map<TSource, TTarget>(TSource source) => source.Map<TSource, TTarget>();
|
||||||
|
|
||||||
|
public List<TTarget> Map<TSource, TTarget>(List<TSource> source) => source.Map<TSource, TTarget>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 表示处理API异常的筛选器。
|
||||||
|
/// </summary>
|
||||||
|
public class ApiExceptionFilterAttribute : Attribute, IExceptionFilter
|
||||||
|
{
|
||||||
|
private readonly ILogger<ApiExceptionFilterAttribute> _logger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ApiExceptionFilterAttribute" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">The logger</param>
|
||||||
|
public ApiExceptionFilterAttribute(ILogger<ApiExceptionFilterAttribute> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when [exception].
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The context.</param>
|
||||||
|
public void OnException(ExceptionContext context)
|
||||||
|
{
|
||||||
|
|
||||||
|
_logger.LogError(0, context.Exception, $"ip={context.HttpContext.Connection.RemoteIpAddress}, path={context.HttpContext.Request.Path}, error={JsonSerializer.Serialize(context.Exception.Message)}");
|
||||||
|
|
||||||
|
if (context.Exception.Data != null && context.Exception.Data["code"] != null)
|
||||||
|
{
|
||||||
|
var code = (int)context.Exception.Data["code"];
|
||||||
|
context.Result = new ObjectResult(ApiResult.Failed(context.Exception.Message, code));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Result = new ObjectResult(ApiResult.Failed(context.Exception.Message));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
context.ExceptionHandled = true;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
{
|
||||||
|
// 执行中的过滤器管道
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public class ApiResult : IApiResult
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an empty <see cref="IApiResult"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly IApiResult Empty = new ApiResult
|
||||||
|
{
|
||||||
|
Code = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the status code.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The status code.</value>
|
||||||
|
[JsonPropertyName("code")]
|
||||||
|
public int Code { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the message.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The message.</value>
|
||||||
|
[JsonPropertyName("message")]
|
||||||
|
public string? Message { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="IApiResult{TData}"/> by the specified result.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TData">The type of the result.</typeparam>
|
||||||
|
/// <param name="data">The result.</param>
|
||||||
|
/// <returns>An instance inherited from <see cref="IApiResult{TResult}"/> interface.</returns>
|
||||||
|
public static IApiResult<TData> Succeed<TData>(TData data) => new ApiResult<TData>
|
||||||
|
{
|
||||||
|
Code = 0,
|
||||||
|
Data = data
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="IApiResult"/> by the specified error message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">The message.</param>
|
||||||
|
/// <param name="code">The status code</param>
|
||||||
|
/// <returns>An instance inherited from <see cref="IApiResult"/> interface.</returns>
|
||||||
|
public static IApiResult Failed(string message, int? code = null) => new ApiResult
|
||||||
|
{
|
||||||
|
Code = code ?? -1,
|
||||||
|
Message = message
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="IApiResult{TResult}"/> by the specified error message.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TData">The type of the result.</typeparam>
|
||||||
|
/// <param name="data">The error result.</param>
|
||||||
|
/// <param name="message">The message.</param>
|
||||||
|
/// <param name="code">The status code.</param>
|
||||||
|
/// <returns>An instance inherited from <see cref="IApiResult"/> interface.</returns>
|
||||||
|
public static IApiResult<TData> Failed<TData>(TData data, string message, int? code = null) => new ApiResult<TData>
|
||||||
|
{
|
||||||
|
Code = code ?? -1,
|
||||||
|
Message = message,
|
||||||
|
Data = data
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="IApiResult"/> by the specified status code and message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code">The status code.</param>
|
||||||
|
/// <param name="message">The message.</param>
|
||||||
|
/// <returns>An instance inherited from <see cref="IApiResult"/> interface.</returns>
|
||||||
|
public static IApiResult From(int code, string message = null) => new ApiResult
|
||||||
|
{
|
||||||
|
Code = code,
|
||||||
|
Message = message
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="IApiResult{TResult}"/> by the specified result.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TData">The type of the result.</typeparam>
|
||||||
|
/// <param name="data">The result.</param>
|
||||||
|
/// <param name="code">The status code.</param>
|
||||||
|
/// <param name="message">The message.</param>
|
||||||
|
/// <returns>An instance inherited from <see cref="IApiResult{TResult}"/> interface.</returns>
|
||||||
|
public static IApiResult<TData> From<TData>(TData data, int code, string message) => new ApiResult<TData>
|
||||||
|
{
|
||||||
|
Code = code,
|
||||||
|
Message = message,
|
||||||
|
Data = data
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public class ApiResult<TData> : ApiResult, IApiResult<TData>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ApiResult{TResult}"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public ApiResult() { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ApiResult{TResult}" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">The result.</param>
|
||||||
|
/// <param name="code">The status code.</param>
|
||||||
|
public ApiResult(TData data, int? code)
|
||||||
|
{
|
||||||
|
Code = code ?? 0;
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the result.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The result.</value>
|
||||||
|
[JsonPropertyName("data")]
|
||||||
|
public TData Data { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public interface IApiResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the status code.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The status code.</value>
|
||||||
|
int Code { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the message.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The message.</value>
|
||||||
|
string Message { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public interface IApiResult<TData> : IApiResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the result.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The result.</value>
|
||||||
|
TData Data { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public class PageResult<TData> where TData : class
|
||||||
|
{
|
||||||
|
public PageResult(int pageIndex, int pageSize, int total, IList<TData>? data)
|
||||||
|
{
|
||||||
|
PageIndex = pageIndex;
|
||||||
|
PageSize = pageSize;
|
||||||
|
Total = total;
|
||||||
|
Data = data;
|
||||||
|
TotalCount = total == 0 ? 0 : (Total / PageSize) + (Total % PageSize) > 0 ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 页数
|
||||||
|
/// </summary>
|
||||||
|
public int PageIndex { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 分页大小
|
||||||
|
/// </summary>
|
||||||
|
public int PageSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 总数量
|
||||||
|
/// </summary>
|
||||||
|
public int Total { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 分页总数量
|
||||||
|
/// </summary>
|
||||||
|
public int TotalCount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 数据
|
||||||
|
/// </summary>
|
||||||
|
public IList<TData>? Data { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public class SearchPageBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 页数
|
||||||
|
/// </summary>
|
||||||
|
public int PageIndex { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 分页大小
|
||||||
|
/// </summary>
|
||||||
|
public int PageSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 排序字段,支持逗号隔开
|
||||||
|
/// </summary>
|
||||||
|
public string? Sort { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 升降序,Asc/Desc
|
||||||
|
/// </summary>
|
||||||
|
public string? Order { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否导出
|
||||||
|
/// </summary>
|
||||||
|
public bool? Export { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,133 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public class ApiSecurityAsyncFilter : IAsyncAuthorizationFilter
|
||||||
|
{
|
||||||
|
private readonly IConfiguration _configuration;
|
||||||
|
private readonly ILogger<ApiSecurityAsyncFilter> _logger;
|
||||||
|
|
||||||
|
public ApiSecurityAsyncFilter(IConfiguration configuration,
|
||||||
|
ILogger<ApiSecurityAsyncFilter> logger)
|
||||||
|
{
|
||||||
|
_configuration = configuration;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
||||||
|
{
|
||||||
|
if (!context.Filters.Any(filterMetadata => filterMetadata.GetType() == typeof(ApiSecurityAttribute)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var request = context.HttpContext.Request;
|
||||||
|
if (!request.Method.ToLower().Equals("post"))
|
||||||
|
{
|
||||||
|
context.Result = new ObjectResult(ApiResult.Failed("ApiSecurityAsyncFilter只支持POST方法!", 10004));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var clientKeys = _configuration.GetSection("ClientKey").Get<List<ClientKey>>();
|
||||||
|
if (clientKeys == null || !clientKeys.Any())
|
||||||
|
{
|
||||||
|
context.Result = new ObjectResult(ApiResult.Failed("ClientKey没有配置!", 10003));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var clientid = request.Headers["clientid"].ToString();
|
||||||
|
var sign = request.Headers["sign"].ToString();
|
||||||
|
if (string.IsNullOrEmpty(clientid) || string.IsNullOrEmpty(sign))
|
||||||
|
{
|
||||||
|
context.Result = new ObjectResult(ApiResult.Failed("请求头clientid或sign不能为空!", 10003));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var client = clientKeys.First(x => x.Id == clientid);
|
||||||
|
request.EnableBuffering();
|
||||||
|
var stream = request.Body;
|
||||||
|
var buffer = new byte[request.ContentLength.Value];
|
||||||
|
await stream.ReadAsync(buffer);
|
||||||
|
var bodyJson = Encoding.UTF8.GetString(buffer);
|
||||||
|
stream.Position = 0;
|
||||||
|
var signData = SignData(bodyJson, client.NewAccessKey);
|
||||||
|
if (!signData.Equals(sign))
|
||||||
|
{
|
||||||
|
context.Result = new ObjectResult(ApiResult.Failed("签名不合法!", 10001));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var contextAes = DecryptByAES(client, bodyJson);
|
||||||
|
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var dataContext = Encoding.UTF8.GetBytes(contextAes);
|
||||||
|
var requestBodyStream = new MemoryStream();//创建一个流
|
||||||
|
requestBodyStream.Seek(0, SeekOrigin.Begin);//设置从0开始读取
|
||||||
|
requestBodyStream.Write(dataContext, 0, dataContext.Length);//把修改写入流中
|
||||||
|
request.Body = requestBodyStream;//把修改后的内容赋值给请求body
|
||||||
|
request.Body.Seek(0, SeekOrigin.Begin);
|
||||||
|
request.Body.Position = 0;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogInformation(ex, "报错解密");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string SignData(string ciphertext, string accessKey)
|
||||||
|
{
|
||||||
|
Encoding utf = new UTF8Encoding();
|
||||||
|
HMACMD5 hmac = new HMACMD5(utf.GetBytes(accessKey));
|
||||||
|
byte[] hashValue = hmac.ComputeHash(utf.GetBytes(ciphertext));
|
||||||
|
return Convert.ToBase64String(hashValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string DecryptByAES(ClientKey client, string bodyJson)
|
||||||
|
{
|
||||||
|
return DecryptByAES(bodyJson, client.NewAccessKey, client.Vi);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AES解密
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">密文字节数组</param>
|
||||||
|
/// <returns>返回解密后的字符串</returns>
|
||||||
|
private static string DecryptByAES(string input, string key, string iv)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(input))
|
||||||
|
{
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
var buffer = Convert.FromBase64String(input);
|
||||||
|
using Aes aes = Aes.Create();
|
||||||
|
aes.Mode = CipherMode.CBC;
|
||||||
|
aes.Padding = PaddingMode.PKCS7;
|
||||||
|
aes.FeedbackSize = 128;
|
||||||
|
aes.Key = Encoding.UTF8.GetBytes(key);
|
||||||
|
aes.IV = Encoding.UTF8.GetBytes(iv);
|
||||||
|
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
|
||||||
|
using MemoryStream msEncrypt = new(buffer);
|
||||||
|
using CryptoStream csEncrypt = new(msEncrypt, decryptor, CryptoStreamMode.Read);
|
||||||
|
using StreamReader srEncrypt = new(csEncrypt);
|
||||||
|
return srEncrypt.ReadToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 安全API
|
||||||
|
/// </summary>
|
||||||
|
public class ApiSecurityAttribute : Attribute, IFilterMetadata
|
||||||
|
{
|
||||||
|
public ApiSecurityAttribute()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddMapper(this IServiceCollection services)
|
||||||
|
=> services.AddSingleton<IMapper, MapperManager>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add auto ioc services
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
/// <param name="baseType"></param>
|
||||||
|
/// <param name="lifeCycle"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IServiceCollection AddAutoIoc(this IServiceCollection services, Type baseType, LifeCycle lifeCycle)
|
||||||
|
{
|
||||||
|
if (!baseType.IsInterface)
|
||||||
|
{
|
||||||
|
throw new TypeLoadException("The status code must be an enumerated type");
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
|
||||||
|
var referencedAssemblies = System.IO.Directory.GetFiles(path, "*.dll").Select(Assembly.LoadFrom).ToArray();
|
||||||
|
var types = referencedAssemblies
|
||||||
|
.SelectMany(a => a.DefinedTypes)
|
||||||
|
.Select(type => type.AsType())
|
||||||
|
.Where(x => x != baseType && baseType.IsAssignableFrom(x)).ToArray();
|
||||||
|
var implementTypes = types.Where(x => x.IsClass).ToArray();
|
||||||
|
var interfaceTypes = types.Where(x => x.IsInterface).ToArray();
|
||||||
|
foreach (var implementType in implementTypes)
|
||||||
|
{
|
||||||
|
var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
|
||||||
|
if (interfaceType != null)
|
||||||
|
switch (lifeCycle)
|
||||||
|
{
|
||||||
|
case LifeCycle.Singleton:
|
||||||
|
services.AddSingleton(interfaceType, implementType);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LifeCycle.Transient:
|
||||||
|
services.AddTransient(interfaceType, implementType);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LifeCycle.Scoped:
|
||||||
|
services.AddScoped(interfaceType, implementType);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(lifeCycle), lifeCycle, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static IServiceCollection AddDGHttpClient(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
return services.AddHttpClient()
|
||||||
|
.AddTransient<IHttpClient, HttpClient>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public class ApiSignatureAsyncFilterAttribute : IAsyncAuthorizationFilter
|
||||||
|
{
|
||||||
|
private readonly IConfiguration _configuration;
|
||||||
|
|
||||||
|
public ApiSignatureAsyncFilterAttribute(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
_configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
||||||
|
{
|
||||||
|
if (context.Filters.Any(filterMetadata =>
|
||||||
|
filterMetadata.GetType() == typeof(ApiSignatureFilterForbidAttribute) ||
|
||||||
|
filterMetadata.GetType() == typeof(ApiSecurityAttribute) ||
|
||||||
|
filterMetadata.GetType() == typeof(ApiTimeSecurityAttribute)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var request = context.HttpContext.Request;
|
||||||
|
var appId = _configuration.GetSection("SignConfig:AppId").Value;
|
||||||
|
var secret = _configuration.GetSection("SignConfig:Secret").Value;
|
||||||
|
if (string.IsNullOrWhiteSpace(appId) || string.IsNullOrWhiteSpace(secret))
|
||||||
|
{
|
||||||
|
context.Result = new ObjectResult(ApiResult.Failed("appId或secret没有配置!", 10003));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var authorization = request.Headers["Authorization"].ToString();
|
||||||
|
var timestamps = request.Headers["timestamps"].ToString();
|
||||||
|
if (string.IsNullOrEmpty(authorization) || string.IsNullOrEmpty(timestamps))
|
||||||
|
{
|
||||||
|
context.Result = new ObjectResult(ApiResult.Failed("请求头authorization或timestamps不能为空!", 10003));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
string? bodyJson;
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
if (request.Method.ToLower().Equals("get") || request.Method.ToLower().Equals("delete"))
|
||||||
|
{
|
||||||
|
var query = request.Query;
|
||||||
|
var parames = new Dictionary<string, object>();
|
||||||
|
foreach (var item in query)
|
||||||
|
{
|
||||||
|
parames.Add(item.Key, item.Value.ToString());
|
||||||
|
}
|
||||||
|
parames = parames.OrderBy(m => m.Key).ToDictionary(m => m.Key, n => n.Value);
|
||||||
|
bodyJson = JsonSerializer.Serialize(parames, options);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
request.EnableBuffering();
|
||||||
|
var stream = request.Body;
|
||||||
|
var buffer = new byte[request.ContentLength.Value];
|
||||||
|
await stream.ReadAsync(buffer);
|
||||||
|
bodyJson = Encoding.UTF8.GetString(buffer);
|
||||||
|
stream.Position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var md = CreateSign(appId, bodyJson, secret, timestamps);
|
||||||
|
if (authorization != $"{appId}:{md}")
|
||||||
|
{
|
||||||
|
context.Result = new ObjectResult(ApiResult.Failed("签名不合法!", 10001));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var nowTime = GetTimeStamp();
|
||||||
|
var diff = Convert.ToInt32(nowTime) - Convert.ToInt32(timestamps);
|
||||||
|
if (diff > 1800)
|
||||||
|
{
|
||||||
|
context.Result = new ObjectResult(ApiResult.Failed("签名已过期!", 10002));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 生成签名
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="appId"></param>
|
||||||
|
/// <param name="bodyJson"></param>
|
||||||
|
/// <param name="secret"></param>
|
||||||
|
/// <param name="timestamps"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static string CreateSign(string appId, string bodyJson, string secret, string timestamps)
|
||||||
|
{
|
||||||
|
var enStrList = new string[] { appId, bodyJson, secret, timestamps };
|
||||||
|
Array.Sort(enStrList, string.CompareOrdinal);
|
||||||
|
var enStr = string.Join("", enStrList);
|
||||||
|
var md = GetMd5Hash(enStr);
|
||||||
|
return md;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 计算 md5
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="enCode"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static string GetMd5Hash(string enCode)
|
||||||
|
{
|
||||||
|
string res = "";
|
||||||
|
byte[] data = Encoding.GetEncoding("utf-8").GetBytes(enCode);
|
||||||
|
MD5 md5 = MD5.Create();
|
||||||
|
byte[] bytes = md5.ComputeHash(data);
|
||||||
|
for (int i = 0; i < bytes.Length; i++)
|
||||||
|
{
|
||||||
|
res += bytes[i].ToString("x2");
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取时间戳
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetTimeStamp()
|
||||||
|
{
|
||||||
|
TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0);
|
||||||
|
return Convert.ToInt64(ts.TotalSeconds).ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// API屏蔽签名
|
||||||
|
/// </summary>
|
||||||
|
public class ApiSignatureFilterForbidAttribute : Attribute, IAuthorizationFilter
|
||||||
|
{
|
||||||
|
public void OnAuthorization(AuthorizationFilterContext context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,171 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public class ApiTimeSecurityAsyncFilter : IAsyncAuthorizationFilter
|
||||||
|
{
|
||||||
|
private readonly IConfiguration _configuration;
|
||||||
|
private readonly ILogger<ApiTimeSecurityAsyncFilter> _logger;
|
||||||
|
|
||||||
|
public ApiTimeSecurityAsyncFilter(IConfiguration configuration,
|
||||||
|
ILogger<ApiTimeSecurityAsyncFilter> logger)
|
||||||
|
{
|
||||||
|
_configuration = configuration;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
||||||
|
{
|
||||||
|
if (!context.Filters.Any(filterMetadata => filterMetadata.GetType() == typeof(ApiTimeSecurityAttribute)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var request = context.HttpContext.Request;
|
||||||
|
var clientKeys = _configuration.GetSection("ClientKey").Get<List<ClientKey>>();
|
||||||
|
if (clientKeys == null || !clientKeys.Any())
|
||||||
|
{
|
||||||
|
context.Result = new ObjectResult(ApiResult.Failed("ClientKey没有配置!", 10003));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var clientid = request.Headers["clientid"].ToString();
|
||||||
|
var sign = request.Headers["sign"].ToString();
|
||||||
|
var timestamps = request.Headers["timestamps"].ToString();
|
||||||
|
if (string.IsNullOrEmpty(clientid) || string.IsNullOrEmpty(sign) || string.IsNullOrEmpty(timestamps))
|
||||||
|
{
|
||||||
|
context.Result = new ObjectResult(ApiResult.Failed("请求头clientid或sign或timestamps不能为空!", 10003));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var client = clientKeys.First(x => x.Id == clientid);
|
||||||
|
|
||||||
|
string? bodyJson;
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
if (request.Method.ToLower().Equals("get") || request.Method.ToLower().Equals("delete"))
|
||||||
|
{
|
||||||
|
var query = request.Query;
|
||||||
|
var parames = new Dictionary<string, object>();
|
||||||
|
foreach (var item in query)
|
||||||
|
{
|
||||||
|
parames.Add(item.Key, item.Value.ToString());
|
||||||
|
}
|
||||||
|
bodyJson = JsonSerializer.Serialize(parames, options);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
request.EnableBuffering();
|
||||||
|
var stream = request.Body;
|
||||||
|
var buffer = new byte[request.ContentLength.Value];
|
||||||
|
await stream.ReadAsync(buffer);
|
||||||
|
bodyJson = Encoding.UTF8.GetString(buffer);
|
||||||
|
stream.Position = 0;
|
||||||
|
|
||||||
|
var content = JsonSerializer.Deserialize<ContentDto>(bodyJson);
|
||||||
|
bodyJson = content.Content;
|
||||||
|
}
|
||||||
|
var signData = SignData(bodyJson, client.NewAccessKey);
|
||||||
|
if (!signData.Equals(sign))
|
||||||
|
{
|
||||||
|
context.Result = new ObjectResult(ApiResult.Failed("签名不合法!", 10001));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var nowTime = GetTimeStamp();
|
||||||
|
var diff = Convert.ToInt32(nowTime) - Convert.ToInt32(timestamps);
|
||||||
|
if (diff > 1800)
|
||||||
|
{
|
||||||
|
context.Result = new ObjectResult(ApiResult.Failed("签名已过期!", 10002));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var contextAes = DecryptByAES(client, bodyJson);
|
||||||
|
|
||||||
|
var dataContext = Encoding.UTF8.GetBytes(contextAes);
|
||||||
|
var requestBodyStream = new MemoryStream();//创建一个流
|
||||||
|
requestBodyStream.Seek(0, SeekOrigin.Begin);//设置从0开始读取
|
||||||
|
requestBodyStream.Write(dataContext, 0, dataContext.Length);//把修改写入流中
|
||||||
|
request.Body = requestBodyStream;//把修改后的内容赋值给请求body
|
||||||
|
request.Body.Seek(0, SeekOrigin.Begin);
|
||||||
|
request.Body.Position = 0;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogInformation(ex, "报错解密");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string SignData(string ciphertext, string accessKey)
|
||||||
|
{
|
||||||
|
Encoding utf = new UTF8Encoding();
|
||||||
|
HMACMD5 hmac = new HMACMD5(utf.GetBytes(accessKey));
|
||||||
|
byte[] hashValue = hmac.ComputeHash(utf.GetBytes(ciphertext));
|
||||||
|
return Convert.ToBase64String(hashValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string DecryptByAES(ClientKey client, string bodyJson)
|
||||||
|
{
|
||||||
|
return DecryptByAES(bodyJson, client.NewAccessKey, client.Vi);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AES解密
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">密文字节数组</param>
|
||||||
|
/// <returns>返回解密后的字符串</returns>
|
||||||
|
private static string DecryptByAES(string input, string key, string iv)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(input))
|
||||||
|
{
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
var buffer = Convert.FromBase64String(input);
|
||||||
|
using Aes aes = Aes.Create();
|
||||||
|
aes.Mode = CipherMode.CBC;
|
||||||
|
aes.Padding = PaddingMode.PKCS7;
|
||||||
|
aes.FeedbackSize = 128;
|
||||||
|
aes.Key = Encoding.UTF8.GetBytes(key);
|
||||||
|
aes.IV = Encoding.UTF8.GetBytes(iv);
|
||||||
|
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
|
||||||
|
using MemoryStream msEncrypt = new(buffer);
|
||||||
|
using CryptoStream csEncrypt = new(msEncrypt, decryptor, CryptoStreamMode.Read);
|
||||||
|
using StreamReader srEncrypt = new(csEncrypt);
|
||||||
|
return srEncrypt.ReadToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取时间戳
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetTimeStamp()
|
||||||
|
{
|
||||||
|
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
|
||||||
|
return Convert.ToInt64(ts.TotalSeconds).ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ContentDto
|
||||||
|
{
|
||||||
|
public string Content { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 安全API
|
||||||
|
/// </summary>
|
||||||
|
public class ApiTimeSecurityAttribute : Attribute, IFilterMetadata
|
||||||
|
{
|
||||||
|
public ApiTimeSecurityAttribute()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public class DecimalValidationAttribute : ValidationAttribute
|
||||||
|
{
|
||||||
|
public int? Length { get; }
|
||||||
|
private bool LengthError { get; set; }
|
||||||
|
|
||||||
|
public DecimalValidationAttribute()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DecimalValidationAttribute(int? length)
|
||||||
|
{
|
||||||
|
Length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IsValid 为 false 时,提示得 error 信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override string FormatErrorMessage(string name)
|
||||||
|
{
|
||||||
|
var lengthMessage = LengthError ? $",且长度不能超过{Length}" : "";
|
||||||
|
return $"{name}必须输入数字{lengthMessage},请重新输入!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证当前字段得结果
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override bool IsValid(object? value)
|
||||||
|
{
|
||||||
|
if (value == null) return true;
|
||||||
|
if (decimal.TryParse(Convert.ToString(value), out decimal num))
|
||||||
|
{
|
||||||
|
if (Length != null && num.ToString().Length > Length)
|
||||||
|
{
|
||||||
|
LengthError = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public class IntValidationAttribute : ValidationAttribute
|
||||||
|
{
|
||||||
|
public int? Length { get; }
|
||||||
|
private bool LengthError { get; set; }
|
||||||
|
|
||||||
|
public IntValidationAttribute()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntValidationAttribute(int? length)
|
||||||
|
{
|
||||||
|
Length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IsValid 为 false 时,提示得 error 信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override string FormatErrorMessage(string name)
|
||||||
|
{
|
||||||
|
var lengthMessage = LengthError ? $",且长度不能超过{Length}" : "";
|
||||||
|
return $"{name}必须输入数字{lengthMessage},请重新输入!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证当前字段得结果
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override bool IsValid(object? value)
|
||||||
|
{
|
||||||
|
if (value == null) return true;
|
||||||
|
if (int.TryParse(Convert.ToString(value), out int num))
|
||||||
|
{
|
||||||
|
if (Length != null && num.ToString().Length > Length)
|
||||||
|
{
|
||||||
|
LengthError = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Core
|
||||||
|
{
|
||||||
|
public class LongValidationAttribute : ValidationAttribute
|
||||||
|
{
|
||||||
|
public int? Length { get; }
|
||||||
|
private bool LengthError { get; set; }
|
||||||
|
|
||||||
|
public LongValidationAttribute()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public LongValidationAttribute(int? length)
|
||||||
|
{
|
||||||
|
Length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IsValid 为 false 时,提示得 error 信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override string FormatErrorMessage(string name)
|
||||||
|
{
|
||||||
|
var lengthMessage = LengthError ? $",且长度不能超过{Length}" : "";
|
||||||
|
return $"{name}必须输入数字{lengthMessage},请重新输入!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证当前字段得结果
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override bool IsValid(object? value)
|
||||||
|
{
|
||||||
|
if (value == null) return true;
|
||||||
|
if (long.TryParse(Convert.ToString(value), out long num))
|
||||||
|
{
|
||||||
|
if (Length != null && num.ToString().Length > Length)
|
||||||
|
{
|
||||||
|
LengthError = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<AppDbContext> options,
|
||||||
|
IAppManager appManager, IConfiguration configuration)
|
||||||
|
: base(options, appManager, configuration) => _configuration = configuration;
|
||||||
|
|
||||||
|
public DbSet<BAS_COMPANY> BAS_COMPANY { get; set; }
|
||||||
|
|
||||||
|
public DbSet<BAS_BUSINESSDEPARTMENT> BAS_BUSINESSDEPARTMENT { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<TestController> _logger;
|
||||||
|
private readonly IScopeDbContextManager _manager;
|
||||||
|
private readonly IEventBus<TestEvent> _eventBus;
|
||||||
|
private readonly IHttpClient _httpClient;
|
||||||
|
private readonly IRedisManager _redisManager;
|
||||||
|
|
||||||
|
public TestController(IScopeDbContextManager manager,
|
||||||
|
ILogger<TestController> logger,
|
||||||
|
IEventBus<TestEvent> eventBus,
|
||||||
|
IHttpClient httpClient,
|
||||||
|
IRedisManager redisManager)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_manager = manager;
|
||||||
|
_eventBus = eventBus;
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_redisManager = redisManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("[action]")]
|
||||||
|
[ApiSignatureFilterForbid]
|
||||||
|
public async Task<List<BAS_COMPANY>> Post([FromBody] BAS_COMPANY dto)
|
||||||
|
{
|
||||||
|
//TestEvent testEvent = new TestEvent();
|
||||||
|
//await _eventBus.publicAsync(testEvent);
|
||||||
|
using var repository = _manager.CreateRepository<AppDbContext>();
|
||||||
|
return await repository.GetRepository<BAS_COMPANY>().QueryListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("[action]")]
|
||||||
|
[ApiSignatureFilterForbid]
|
||||||
|
public async Task<List<BAS_COMPANY>> Get([FromHeader] string? appid = "crm_tg_dng8")
|
||||||
|
{
|
||||||
|
var d = await _redisManager.ExistsAsync("dat");
|
||||||
|
var list = new List<BAS_COMPANY>();
|
||||||
|
using (var repository = _manager.CreateRepository<AppDbContext>())
|
||||||
|
{
|
||||||
|
using var transaction = await repository.BeginTransactionAsync();
|
||||||
|
list = await repository.GetRepository<BAS_COMPANY>().QueryListAsync();
|
||||||
|
list.ForEach(x =>
|
||||||
|
{
|
||||||
|
if (x.COMPANYID == 600000173)
|
||||||
|
{
|
||||||
|
x.CREATEUSER = 600000207;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await repository.GetRepository<BAS_COMPANY>().BatchUpdateAsync(list, x => new { x.CREATEUSER });
|
||||||
|
var data = await repository.GetRepository<BAS_BUSINESSDEPARTMENT>().QueryListAsync();
|
||||||
|
data.ForEach(x =>
|
||||||
|
{
|
||||||
|
x.UTIME = DateTime.Now;
|
||||||
|
});
|
||||||
|
await repository.GetRepository<BAS_BUSINESSDEPARTMENT>().BatchUpdateAsync(data, x => new { x.UTIME });
|
||||||
|
await transaction.CommitAsync();
|
||||||
|
}
|
||||||
|
using (var repository = _manager.CreateRepository<AppDbContext>("crm_d1_dnzz"))
|
||||||
|
{
|
||||||
|
var list2 = await repository.GetRepository<BAS_COMPANY>().QueryListAsync();
|
||||||
|
list.AddRange(list2);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("[action]")]
|
||||||
|
[ApiSignatureFilterForbid]
|
||||||
|
public async Task<List<BAS_COMPANY>> GetInnerusers([FromHeader] string appid)
|
||||||
|
{
|
||||||
|
var data = await _httpClient.GetAsync<ApiResult<List<BAS_COMPANY>>>("https://localhost:7150/Test/GetInnerusers?eid=5004,4028,123", "qt_compliance", "CqlNBxRof0yl7eJM1f4IbOBhgribfooZJ1zwuIj5NzQ=");
|
||||||
|
return data.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("[action]")]
|
||||||
|
[ApiSignatureFilterForbid]
|
||||||
|
public async Task<string> 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<ApiResult<string>>("http://post.hc.dn8188.com/Wework/sendmsg.html", model, clientId, "1622a92d");
|
||||||
|
//return result.Data;
|
||||||
|
var result = await _httpClient.PostAsync<ApiResult>("https://certcontract.wantest.tcfortune.com:11188qt_compliance?content=8fYIIKMSFVxFY5i2AWz7Awu5dDpKxrp0HGf%2bm1Ij3fa6DcfLHvZbag%3d%3d&sign=9jgR248r5vBlH3yZqrz9%2bg%3d%3d&clientId=UPWEBSITE");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<UserSecretsId>78e44202-6303-41c8-99b3-6abe5c750ef5</UserSecretsId>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.15.1" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
|
||||||
|
<PackageReference Include="Oracle.EntityFrameworkCore" Version="6.21.61" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\DG.Core\DG.Core.csproj" />
|
||||||
|
<ProjectReference Include="..\DG.EventBus\DG.EventBus.csproj" />
|
||||||
|
<ProjectReference Include="..\DG.Oracle.EntityFrameworkCore\DG.Oracle.EntityFramework.csproj" />
|
||||||
|
<ProjectReference Include="..\DG.Redis\DG.Redis.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="DG.EntityFramework" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -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"]
|
||||||
|
|
@ -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; }
|
||||||
|
/// <summary>
|
||||||
|
/// 部门ID
|
||||||
|
/// </summary>
|
||||||
|
public decimal DEPTID { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 1、主部门 2、子部门
|
||||||
|
/// </summary>
|
||||||
|
public int DEPTTYPE { get; set; }
|
||||||
|
|
||||||
|
public DateTime CTIME { get; set; }
|
||||||
|
|
||||||
|
public DateTime UTIME { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<MeetingAccessory>();
|
||||||
|
MeetingParticipants = new List<MeetingParticipant>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[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<MeetingAccessory> MeetingAccessories { get; set; }
|
||||||
|
|
||||||
|
public virtual List<MeetingParticipant> MeetingParticipants { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 参与人
|
||||||
|
/// </summary>
|
||||||
|
public string? Paricipant { get; set; }
|
||||||
|
|
||||||
|
public virtual Meeting? Meeting { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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; }//是否绑定客服
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<AppDbContext>("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<TestWorker>();
|
||||||
|
builder.Services.AddSingleton<IEventHandler<TestEvent>, 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();
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
using DG.EventBus;
|
||||||
|
|
||||||
|
namespace DG.DotNet.Sample
|
||||||
|
{
|
||||||
|
public class TestEvent : IEvent
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
using DG.EventBus;
|
||||||
|
|
||||||
|
namespace DG.DotNet.Sample
|
||||||
|
{
|
||||||
|
public class TestHandler : IEventHandler<TestEvent>
|
||||||
|
{
|
||||||
|
public bool CanHandle(TestEvent @event)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> HandleAsync(TestEvent @event, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
await Task.Delay(10000);
|
||||||
|
Console.WriteLine("test");
|
||||||
|
return await Task.FromResult(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<TestWorker> _logger;
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
private readonly IEventBus<TestEvent> _eventBus;
|
||||||
|
private readonly IHttpClient _httpClient;
|
||||||
|
|
||||||
|
public TestWorker(IServiceProvider serviceProvider,
|
||||||
|
ILogger<TestWorker> logger,
|
||||||
|
IEventBus<TestEvent> 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<IOracleRepository<AppDbContext>>();
|
||||||
|
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<BAS_BUSINESSDEPARTMENT>(sql, param);
|
||||||
|
_logger.LogInformation(JsonSerializer.Serialize(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
|
<Version>1.0.32</Version>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.6" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.6" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="6.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace DG.EntityFramework
|
||||||
|
{
|
||||||
|
public class DbContextProvider<TDbContext> : IDbContextProvider<TDbContext>
|
||||||
|
where TDbContext : DbContext
|
||||||
|
{
|
||||||
|
private readonly TDbContext _dbContext;
|
||||||
|
|
||||||
|
public DbContextProvider(TDbContext dbContext)
|
||||||
|
{
|
||||||
|
_dbContext = dbContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TDbContext GetDbContext()
|
||||||
|
{
|
||||||
|
return _dbContext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data.Common;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace DG.EntityFramework
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Entity Framework Core Options
|
||||||
|
/// </summary>
|
||||||
|
public class EFCoreOptions<TDbContext>
|
||||||
|
where TDbContext : DbContext
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the database's connection string that will be used to store database entities.
|
||||||
|
/// </summary>
|
||||||
|
public string ConnectionString { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public DbConnection ExistingConnection { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public DbContextOptionsBuilder<TDbContext> DbContextOptions { get; }
|
||||||
|
|
||||||
|
internal string Version { get; set; }
|
||||||
|
|
||||||
|
public EFCoreOptions()
|
||||||
|
{
|
||||||
|
DbContextOptions = new DbContextOptionsBuilder<TDbContext>();
|
||||||
|
Version = "1.0.0";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace DG.EntityFramework
|
||||||
|
{
|
||||||
|
public class EFCoreOptionsExtension<TDbContext>
|
||||||
|
where TDbContext : DbContext
|
||||||
|
{
|
||||||
|
private readonly Action<EFCoreOptions<TDbContext>> _configure;
|
||||||
|
|
||||||
|
public EFCoreOptionsExtension(Action<EFCoreOptions<TDbContext>> configure)
|
||||||
|
{
|
||||||
|
_configure = configure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
var options = new EFCoreOptions<TDbContext>();
|
||||||
|
_configure(options);
|
||||||
|
services.AddDbContext<TDbContext>();
|
||||||
|
services.Configure(_configure);
|
||||||
|
services.AddScoped<IUnitOfWorkManager, UnitOfWorkManager<TDbContext>>();
|
||||||
|
services.AddScoped<IUnitOfWorkCompleteHandle, UnitOfWorkCompleteHandle<TDbContext>>();
|
||||||
|
services.AddScoped<IDbContextProvider<TDbContext>, DbContextProvider<TDbContext>>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> 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>(TState state) => null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace DG.EntityFramework
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TDbContext"></typeparam>
|
||||||
|
public interface IDbContextProvider<out TDbContext>
|
||||||
|
where TDbContext : DbContext
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
TDbContext GetDbContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<TDbContext> : IBaseRepository<TDbContext>
|
||||||
|
where TDbContext : DbContext
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
private readonly TDbContext _context;
|
||||||
|
public BaseRepository(
|
||||||
|
IServiceProvider serviceProvider,
|
||||||
|
TDbContext dbContext
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
_context = dbContext;
|
||||||
|
}
|
||||||
|
public IRepositoryBase<TDbContext, TEntity> GetRepository<TEntity>()
|
||||||
|
where TEntity : class
|
||||||
|
=> _serviceProvider.GetRequiredService<IRepositoryBase<TDbContext, TEntity>>();
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<IDbContextTransaction> BeginTransactionAsync()
|
||||||
|
=> await _context.Database.BeginTransactionAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建SqlCommand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sql"></param>
|
||||||
|
/// <param name="transaction"></param>
|
||||||
|
/// <param name="parameters"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private async Task<DbCommand> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行存储过程的扩展方法ExecuteSqlCommand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="commandType"></param>
|
||||||
|
/// <param name="sql"></param>
|
||||||
|
/// <param name="parameters"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<int> 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<List<T>> ExecuteSqlCommandAsync<T>(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<T> 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<List<T>> ExecuteSqlToListAsync<T>(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<T> 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<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<T> ExecuteSqlToEntityAsync<T>(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<int> 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<long> 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<IDbContextTransaction> BeginTransactionAsync(IsolationLevel isolationLevel)
|
||||||
|
=> await _context.Database.BeginTransactionAsync(isolationLevel);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<TDbContext>
|
||||||
|
where TDbContext : DbContext
|
||||||
|
{
|
||||||
|
IRepositoryBase<TDbContext, TEntity> GetRepository<TEntity>()
|
||||||
|
where TEntity : class;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行存储过程的扩展方法ExecuteSqlCommand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="commandType"></param>
|
||||||
|
/// <param name="sql"></param>
|
||||||
|
/// <param name="parameters"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<int> ExecuteSqlCommandNonQueryAsync(CommandType commandType, string sql, [NotNull] params object[] parameters);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行存储过程的扩展方法ExecuteSqlCommand
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="commandType"></param>
|
||||||
|
/// <param name="sql"></param>
|
||||||
|
/// <param name="parameters"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<List<T>> ExecuteSqlCommandAsync<T>(CommandType commandType, string sql, [NotNull] params object[] parameters)
|
||||||
|
where T : class, new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 开启事务
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<IDbContextTransaction> BeginTransactionAsync();
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isolationLevel"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<IDbContextTransaction> BeginTransactionAsync(IsolationLevel isolationLevel);
|
||||||
|
|
||||||
|
Task<List<T>> ExecuteSqlToListAsync<T>(string sql, [MaybeNull] params object[] parameters)
|
||||||
|
where T : class, new();
|
||||||
|
|
||||||
|
Task<T> ExecuteSqlToEntityAsync<T>(string sql, [MaybeNull] params object[] parameters)
|
||||||
|
where T : class, new();
|
||||||
|
|
||||||
|
Task<int> ExecuteSqlToCountAsync(string sql, [MaybeNull] params object[] parameters);
|
||||||
|
|
||||||
|
Task<long> ExecuteSqlToCountLongAsync(string sql, [MaybeNull] params object[] parameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.EntityFramework
|
||||||
|
{
|
||||||
|
public interface IRepositoryBase<TDbContext, TEntity>
|
||||||
|
where TDbContext : DbContext
|
||||||
|
where TEntity : class
|
||||||
|
{
|
||||||
|
#region Qurey
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to get a IQueryable that is used to retrieve entities from entire table.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>IQueryable to be used to select entities from database</returns>
|
||||||
|
IQueryable<TEntity> Query();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to get a IQueryable that is used to retrieve entities from entire table.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertySelectors"></param>
|
||||||
|
/// <returns>IQueryable to be used to select entities from database</returns>
|
||||||
|
IQueryable<TEntity> QueryIncluding(params Expression<Func<TEntity, object>>[] propertySelectors);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to query a array of entities from datatable
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Array of entities</returns>
|
||||||
|
Task<TEntity[]> QueryArrayAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to query a array of entities from datatable by predicate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="predicate"></param>
|
||||||
|
/// <returns>Array of entities</returns>
|
||||||
|
//Task<TEntity[]> QueryArrayAsync(Expression<Func<TEntity, bool>> predicate);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to query a list of entities from datatable
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>List of entities</returns>
|
||||||
|
Task<List<TEntity>> QueryListAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to query a list of entities from datatable by predicate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="predicate"></param>
|
||||||
|
/// <returns>List of entities</returns>
|
||||||
|
//Task<List<TEntity>> QueryListAsync(Expression<Func<TEntity, bool>> predicate);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to query a single entity from datatable by predicate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="predicate"></param>
|
||||||
|
/// <returns>Entity</returns>
|
||||||
|
Task<TEntity> SingleAsync(Expression<Func<TEntity, bool>> predicate = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an entity with given given predicate or null if not found.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="predicate">Predicate to filter entities</param>
|
||||||
|
Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate = null);
|
||||||
|
|
||||||
|
#endregion Qurey
|
||||||
|
|
||||||
|
#region Insert
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Insert a new entity
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entity"></param>
|
||||||
|
/// <param name="submit"></param>
|
||||||
|
/// <returns>Inserted entity</returns>
|
||||||
|
Task<TEntity> InsertAsync(TEntity entity, bool submit = true);
|
||||||
|
|
||||||
|
Task<List<TEntity>> BatchInsertAsync(List<TEntity> entities, bool submit = true);
|
||||||
|
|
||||||
|
#endregion Insert
|
||||||
|
|
||||||
|
#region Update
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates an existing entity
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entity"></param>
|
||||||
|
/// <param name="submit"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<TEntity> UpdateAsync(TEntity entity, bool submit = true);
|
||||||
|
|
||||||
|
Task<TEntity> UpdateAsync(TEntity entity, Expression<Func<TEntity, object>> expression, bool submit = true);
|
||||||
|
|
||||||
|
Task BatchUpdateAsync(List<TEntity> entities, bool submit = true);
|
||||||
|
|
||||||
|
Task BatchUpdateAsync(List<TEntity> entities, Expression<Func<TEntity, object>> expression, bool submit = true);
|
||||||
|
#endregion Update
|
||||||
|
|
||||||
|
#region Delete
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes an entity
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entity">Entity</param>
|
||||||
|
/// <param name="submit"></param>
|
||||||
|
/// <returns>Entity to be deleted</returns>
|
||||||
|
Task DeleteAsync(TEntity entity, bool submit = true);
|
||||||
|
|
||||||
|
Task BatchDeleteAsync(List<TEntity> entities, bool submit = true);
|
||||||
|
|
||||||
|
#endregion Delete
|
||||||
|
|
||||||
|
#region Expression
|
||||||
|
|
||||||
|
TDbContext Context { get; }
|
||||||
|
|
||||||
|
DbSet<TEntity> Table { get; }
|
||||||
|
|
||||||
|
void Attach(TEntity entity);
|
||||||
|
|
||||||
|
#endregion Expression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,298 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||||
|
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.EntityFramework
|
||||||
|
{
|
||||||
|
public class RepositoryBase<TDbContext, TEntity> : IRepositoryBase<TDbContext, TEntity>
|
||||||
|
where TDbContext : DbContext
|
||||||
|
where TEntity : class
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public TDbContext Context { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public DbSet<TEntity> Table => Context.Set<TEntity>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dbContextProvider"></param>
|
||||||
|
public RepositoryBase(TDbContext context)
|
||||||
|
{
|
||||||
|
Context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Qurey
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to get a IQueryable that is used to retrieve entities from entire table.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>IQueryable to be used to select entities from database</returns>
|
||||||
|
public IQueryable<TEntity> Query()
|
||||||
|
{
|
||||||
|
if (!(Context.Set<TEntity>() is IQueryable<TEntity> query))
|
||||||
|
throw new Exception($"{typeof(TEntity)} TEntity cannot be empty!");
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to get a IQueryable that is used to retrieve entities from entire table.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertySelectors"></param>
|
||||||
|
/// <returns>IQueryable to be used to select entities from database</returns>
|
||||||
|
public IQueryable<TEntity> QueryIncluding(params Expression<Func<TEntity, object>>[] propertySelectors)
|
||||||
|
{
|
||||||
|
if (propertySelectors == null || !propertySelectors.Any())
|
||||||
|
{
|
||||||
|
return Query();
|
||||||
|
}
|
||||||
|
|
||||||
|
var query = Query();
|
||||||
|
|
||||||
|
return propertySelectors.Aggregate(query, (current, propertySelector) => current.Include(propertySelector));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to query a array of entities from data table
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Array of entities</returns>
|
||||||
|
public async Task<TEntity[]> QueryArrayAsync()
|
||||||
|
{
|
||||||
|
return await Query().ToArrayAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to query a array of entities from data table by predicate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="predicate"></param>
|
||||||
|
/// <returns>Array of entities</returns>
|
||||||
|
public async Task<TEntity[]> QueryArrayAsync(Expression<Func<TEntity, bool>> predicate)
|
||||||
|
{
|
||||||
|
return await Query().Where(predicate).ToArrayAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to query a list of entities from data table
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>List of entities</returns>
|
||||||
|
public async Task<List<TEntity>> QueryListAsync()
|
||||||
|
{
|
||||||
|
return await Query().ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to query a list of entities from data table by predicate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="predicate"></param>
|
||||||
|
/// <returns>List of entities</returns>
|
||||||
|
public async Task<List<TEntity>> QueryListAsync(Expression<Func<TEntity, bool>> predicate = null)
|
||||||
|
{
|
||||||
|
return await Query().Where(predicate).ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to query a single entity from datatable by predicate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="predicate"></param>
|
||||||
|
/// <returns>Entity</returns>
|
||||||
|
public async Task<TEntity> SingleAsync(Expression<Func<TEntity, bool>> predicate = null)
|
||||||
|
{
|
||||||
|
if (predicate == null)
|
||||||
|
{
|
||||||
|
return await Query().SingleAsync();
|
||||||
|
}
|
||||||
|
return await Query().SingleAsync(predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an entity with given given predicate or null if not found.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="predicate">Predicate to filter entities</param>
|
||||||
|
public async Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate = null)
|
||||||
|
{
|
||||||
|
if (predicate == null)
|
||||||
|
{
|
||||||
|
return await Query().FirstOrDefaultAsync();
|
||||||
|
}
|
||||||
|
return await Query().FirstOrDefaultAsync(predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Qurey
|
||||||
|
|
||||||
|
#region Insert
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Insert a new entity
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entity"></param>
|
||||||
|
/// <param name="submit"></param>
|
||||||
|
/// <returns>Inserted entity</returns>
|
||||||
|
public virtual async Task<TEntity> InsertAsync(TEntity entity, bool submit = true)
|
||||||
|
{
|
||||||
|
AttachIfNot(entity);
|
||||||
|
Context.Entry(entity).State = EntityState.Added;
|
||||||
|
if (submit)
|
||||||
|
{
|
||||||
|
await Context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual async Task<List<TEntity>> BatchInsertAsync(List<TEntity> entities, bool submit = true)
|
||||||
|
{
|
||||||
|
foreach (var entity in entities)
|
||||||
|
{
|
||||||
|
AttachIfNot(entity);
|
||||||
|
Context.Entry(entity).State = EntityState.Added;
|
||||||
|
}
|
||||||
|
if (submit)
|
||||||
|
{
|
||||||
|
await Context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
return entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Insert
|
||||||
|
|
||||||
|
#region Update
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates an existing entity
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entity"></param>
|
||||||
|
/// <param name="submit"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public virtual async Task<TEntity> UpdateAsync(TEntity entity, bool submit = true)
|
||||||
|
{
|
||||||
|
Attach(entity);
|
||||||
|
Context.Entry(entity).State = EntityState.Modified;
|
||||||
|
if (submit)
|
||||||
|
{
|
||||||
|
await Context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
return await Task.FromResult(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual async Task<TEntity> UpdateAsync(TEntity entity, Expression<Func<TEntity, object>> expression, bool submit = true)
|
||||||
|
{
|
||||||
|
Attach(entity);
|
||||||
|
var entry = Context.Entry(entity);
|
||||||
|
//entry.State = EntityState.Unchanged;
|
||||||
|
foreach (var proInfo in expression.GetPropertyAccessList())
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(proInfo.Name))
|
||||||
|
entry.Property(proInfo.Name).IsModified = true;
|
||||||
|
}
|
||||||
|
if (submit)
|
||||||
|
{
|
||||||
|
await Context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
return await Task.FromResult(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual async Task BatchUpdateAsync(List<TEntity> entities, bool submit = true)
|
||||||
|
{
|
||||||
|
foreach(var entity in entities)
|
||||||
|
{
|
||||||
|
Attach(entity);
|
||||||
|
Context.Entry(entity).State = EntityState.Modified;
|
||||||
|
}
|
||||||
|
if (submit)
|
||||||
|
{
|
||||||
|
await Context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual async Task BatchUpdateAsync(List<TEntity> entities, Expression<Func<TEntity, object>> expression, bool submit = true)
|
||||||
|
{
|
||||||
|
foreach (var entity in entities)
|
||||||
|
{
|
||||||
|
Attach(entity);
|
||||||
|
var entry = Context.Entry(entity);
|
||||||
|
//entry.State = EntityState.Unchanged;
|
||||||
|
foreach (var proInfo in expression.GetPropertyAccessList())
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(proInfo.Name))
|
||||||
|
//4.4将每个 被修改的属性的状态 设置为已修改状态;后面生成update语句时,就只为已修改的属性 更新
|
||||||
|
entry.Property(proInfo.Name).IsModified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (submit)
|
||||||
|
{
|
||||||
|
await Context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Update
|
||||||
|
|
||||||
|
#region Delete
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes an entity
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entity">Entity</param>
|
||||||
|
/// <param name="submit"></param>
|
||||||
|
/// <returns>Entity to be deleted</returns>
|
||||||
|
public virtual async Task DeleteAsync(TEntity entity, bool submit = true)
|
||||||
|
{
|
||||||
|
AttachIfNot(entity);
|
||||||
|
await Task.FromResult(Table.Remove(entity));
|
||||||
|
if (submit)
|
||||||
|
{
|
||||||
|
await Context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual async Task BatchDeleteAsync(List<TEntity> entities, bool submit = true)
|
||||||
|
{
|
||||||
|
foreach (var entity in entities)
|
||||||
|
{
|
||||||
|
AttachIfNot(entity);
|
||||||
|
Context.Entry(entity).State = EntityState.Deleted;
|
||||||
|
}
|
||||||
|
Table.RemoveRange(entities);
|
||||||
|
if (submit)
|
||||||
|
{
|
||||||
|
await Context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion Delete
|
||||||
|
|
||||||
|
#region Expression
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entity"></param>
|
||||||
|
public void AttachIfNot(TEntity entity)
|
||||||
|
{
|
||||||
|
if (!Table.Local.Contains(entity))
|
||||||
|
{
|
||||||
|
Table.Attach(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Attach(TEntity entity)
|
||||||
|
{
|
||||||
|
Table.Attach(entity);
|
||||||
|
}
|
||||||
|
#endregion Expression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace DG.EntityFramework
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extensions method
|
||||||
|
/// </summary>
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Add EntityFramework
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TDbContext"></typeparam>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
/// <param name="isUseLogger"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IServiceCollection AddDGEntityFramework<TDbContext>(this IServiceCollection services,
|
||||||
|
Action<DbContextOptionsBuilder> options)
|
||||||
|
where TDbContext : DbContext
|
||||||
|
{
|
||||||
|
if (options == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(options));
|
||||||
|
}
|
||||||
|
services.AddDbContext<TDbContext>(options, ServiceLifetime.Scoped, ServiceLifetime.Scoped);
|
||||||
|
services.AddScoped<IUnitOfWorkManager, UnitOfWorkManager<TDbContext>>();
|
||||||
|
services.AddScoped<IDbContextProvider<TDbContext>, DbContextProvider<TDbContext>>();
|
||||||
|
services.AddScoped(typeof(IRepositoryBase<,>), typeof(RepositoryBase<,>));
|
||||||
|
services.AddScoped(typeof(IBaseRepository<>), typeof(BaseRepository<>));
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.EntityFramework
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Used to complete a unit of work. This interface can not be injected or directly used.
|
||||||
|
/// </summary>
|
||||||
|
public interface IUnitOfWorkCompleteHandle : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Completes this unit of work. It saves all changes and commit transaction if exists.
|
||||||
|
/// </summary>
|
||||||
|
void Complete();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Completes this unit of work. It saves all changes and commit transaction if exists.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task CompleteAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void Rollback();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task RollbackAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Transactions;
|
||||||
|
|
||||||
|
namespace DG.EntityFramework
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Unit of work manager. Used to begin and control a unit of work.
|
||||||
|
/// </summary>
|
||||||
|
public interface IUnitOfWorkManager
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Begins a new unit of work.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A handle to be able to complete the unit of work</returns>
|
||||||
|
IUnitOfWorkCompleteHandle Begin();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Begins a new unit of work.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A handle to be able to complete the unit of work</returns>
|
||||||
|
IUnitOfWorkCompleteHandle Begin(TransactionScopeOption scope);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Begins a new unit of work.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A handle to be able to complete the unit of work</returns>
|
||||||
|
IUnitOfWorkCompleteHandle Begin(UnitOfWorkOptions options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.EntityFramework
|
||||||
|
{
|
||||||
|
public class UnitOfWorkCompleteHandle<TDbContext> : IUnitOfWorkCompleteHandle
|
||||||
|
where TDbContext : DbContext
|
||||||
|
{
|
||||||
|
private readonly IDbContextTransaction _dbContextTransaction;
|
||||||
|
|
||||||
|
public UnitOfWorkCompleteHandle(IDbContextTransaction dbContextTransaction)
|
||||||
|
{
|
||||||
|
_dbContextTransaction = dbContextTransaction ?? throw new ArgumentNullException(nameof(dbContextTransaction));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Complete()
|
||||||
|
{
|
||||||
|
_dbContextTransaction.Commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CompleteAsync()
|
||||||
|
{
|
||||||
|
await _dbContextTransaction.CommitAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Rollback()
|
||||||
|
{
|
||||||
|
_dbContextTransaction.Rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RollbackAsync()
|
||||||
|
{
|
||||||
|
await _dbContextTransaction.RollbackAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_dbContextTransaction.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Transactions;
|
||||||
|
|
||||||
|
namespace DG.EntityFramework
|
||||||
|
{
|
||||||
|
public class UnitOfWorkCompleteScopeHandle<TDbContext> : IUnitOfWorkCompleteHandle
|
||||||
|
where TDbContext : DbContext
|
||||||
|
{
|
||||||
|
private readonly TransactionScope _transactionScope;
|
||||||
|
|
||||||
|
public UnitOfWorkCompleteScopeHandle(TransactionScope transactionScope)
|
||||||
|
{
|
||||||
|
_transactionScope = transactionScope ?? throw new ArgumentNullException(nameof(transactionScope));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Complete()
|
||||||
|
{
|
||||||
|
_transactionScope.Complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CompleteAsync()
|
||||||
|
{
|
||||||
|
await Task.Run(() => _transactionScope.Complete());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Rollback()
|
||||||
|
{
|
||||||
|
_transactionScope.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RollbackAsync()
|
||||||
|
{
|
||||||
|
await Task.Run(() => _transactionScope.Dispose());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_transactionScope.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Transactions;
|
||||||
|
|
||||||
|
namespace DG.EntityFramework
|
||||||
|
{
|
||||||
|
public class UnitOfWorkManager<TDbContext> : IUnitOfWorkManager
|
||||||
|
where TDbContext : DbContext
|
||||||
|
{
|
||||||
|
private readonly TDbContext _dbContext;
|
||||||
|
|
||||||
|
public UnitOfWorkManager(TDbContext dbContext)
|
||||||
|
{
|
||||||
|
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUnitOfWorkCompleteHandle Begin()
|
||||||
|
{
|
||||||
|
var tran = _dbContext.Database.BeginTransaction();
|
||||||
|
var handle = new UnitOfWorkCompleteHandle<TDbContext>(tran);
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUnitOfWorkCompleteHandle Begin(TransactionScopeOption scope)
|
||||||
|
{
|
||||||
|
return Begin(new UnitOfWorkOptions { Scope = scope });
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUnitOfWorkCompleteHandle Begin(UnitOfWorkOptions options)
|
||||||
|
{
|
||||||
|
var scope = new TransactionScope(
|
||||||
|
options.Scope.GetValueOrDefault(),
|
||||||
|
new TransactionOptions {
|
||||||
|
Timeout = options.Timeout.GetValueOrDefault(),
|
||||||
|
IsolationLevel = options.IsolationLevel.GetValueOrDefault()
|
||||||
|
});
|
||||||
|
var handle = new UnitOfWorkCompleteScopeHandle<TDbContext>(scope);
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Transactions;
|
||||||
|
|
||||||
|
namespace DG.EntityFramework
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Unit of work options.
|
||||||
|
/// </summary>
|
||||||
|
public class UnitOfWorkOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new UnitOfWorkOptions object.
|
||||||
|
/// </summary>
|
||||||
|
public UnitOfWorkOptions()
|
||||||
|
{
|
||||||
|
IsTransactional = true;
|
||||||
|
Scope = TransactionScopeOption.Required;
|
||||||
|
Timeout = TimeSpan.FromMinutes(2);
|
||||||
|
IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scope option.
|
||||||
|
/// </summary>
|
||||||
|
public TransactionScopeOption? Scope { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is this Unit of work transactional? Uses default value if not supplied.
|
||||||
|
/// </summary>
|
||||||
|
public bool? IsTransactional { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Timeout of Unit of work As milliseconds. Uses default value if not supplied.
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan? Timeout { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If this Unit of work is transactional, this option indicated the isolation level of the transaction. Uses default value if not supplied.
|
||||||
|
/// </summary>
|
||||||
|
public IsolationLevel? IsolationLevel { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This option should be set to System.Transactions.TransactionScopeAsyncFlowOption.Enabled if unit of work is used in an async scope.
|
||||||
|
/// </summary>
|
||||||
|
public TransactionScopeAsyncFlowOption? AsyncFlowOption { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
|
<Version>1.0.1</Version>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.EventBus
|
||||||
|
{
|
||||||
|
public class EventBus<TEvent> : IEventBus<TEvent> where TEvent : IEvent
|
||||||
|
{
|
||||||
|
private readonly EventQueue eventQueue = new EventQueue();
|
||||||
|
private readonly IEnumerable<IEventHandler<TEvent>> eventHandlers;
|
||||||
|
|
||||||
|
public EventBus(IEnumerable<IEventHandler<TEvent>> eventHandlers)
|
||||||
|
{
|
||||||
|
this.eventHandlers = eventHandlers;
|
||||||
|
Subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发布事件到队列时触发处理事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sendere"></param>
|
||||||
|
/// <param name="e"></param>
|
||||||
|
private void EventQueue_EventPushed(object sendere, EventProcessedEventArgs e)
|
||||||
|
{
|
||||||
|
(from eh in this.eventHandlers
|
||||||
|
where
|
||||||
|
eh.CanHandle((TEvent)e.Event)
|
||||||
|
select eh).ToList().ForEach(async eh => await eh.HandleAsync((TEvent)e.Event));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task publicAsync(TEvent @event, CancellationToken cancellationToken = default)
|
||||||
|
=> Task.Factory.StartNew(() => eventQueue.Push(@event));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 事件订阅(订阅队列上的事件)
|
||||||
|
/// </summary>
|
||||||
|
public void Subscribe()
|
||||||
|
{
|
||||||
|
eventQueue.EventPushed += EventQueue_EventPushed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace DG.EventBus
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 消息事件参数
|
||||||
|
/// </summary>
|
||||||
|
public class EventProcessedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public IEvent Event { get; }
|
||||||
|
|
||||||
|
public EventProcessedEventArgs(IEvent @event)
|
||||||
|
{
|
||||||
|
Event = @event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace DG.EventBus
|
||||||
|
{
|
||||||
|
public class EventQueue
|
||||||
|
{
|
||||||
|
public event EventHandler<EventProcessedEventArgs> EventPushed;
|
||||||
|
|
||||||
|
public EventQueue()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Push(IEvent @event)
|
||||||
|
{
|
||||||
|
OnMessagePushed(new EventProcessedEventArgs(@event));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMessagePushed(EventProcessedEventArgs e)
|
||||||
|
{
|
||||||
|
this.EventPushed?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace DG.EventBus
|
||||||
|
{
|
||||||
|
public interface IEvent
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace DG.EventBus
|
||||||
|
{
|
||||||
|
public interface IEventBus<TEvent> : IEventSubscriber, IEventpublicer<TEvent> where TEvent : IEvent
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.EventBus
|
||||||
|
{
|
||||||
|
public interface IEventHandler<TEvent> where TEvent : IEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 处理事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="event"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<bool> HandleAsync(TEvent @event, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 可否处理
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="event"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool CanHandle(TEvent @event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.EventBus
|
||||||
|
{
|
||||||
|
public interface IEventpublicer<TEvent> where TEvent : IEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 发布事件
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TEvent"></typeparam>
|
||||||
|
/// <param name="event"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task publicAsync(TEvent @event, CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace DG.EventBus
|
||||||
|
{
|
||||||
|
public interface IEventSubscriber
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 事件订阅
|
||||||
|
/// </summary>
|
||||||
|
void Subscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
using DG.EventBus;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.DependencyInjection
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extensions method
|
||||||
|
/// </summary>
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// EventBus service registered
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
/// <param name="options">Redis config Options</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IServiceCollection AddEventBus(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddSingleton(typeof(IEventBus<>), typeof(EventBus<>));
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<UserSecretsId>18ffe2b4-7ab9-40b9-8f3e-00211cd240e0</UserSecretsId>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.2" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
|
||||||
|
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
|
||||||
|
<PackageReference Include="Hangfire.HttpJob" Version="3.7.9" />
|
||||||
|
<PackageReference Include="Hangfire.HttpJob.Agent" Version="1.4.7" />
|
||||||
|
<PackageReference Include="Hangfire.HttpJob.Client" Version="1.2.9" />
|
||||||
|
<PackageReference Include="Hangfire.MySqlStorage" Version="2.0.3" />
|
||||||
|
<PackageReference Include="HangFire.Redis" Version="2.0.1" />
|
||||||
|
<PackageReference Include="Hangfire.Redis.StackExchange" Version="1.9.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Controllers\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 80
|
||||||
|
EXPOSE 443
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["DG.HttpJob/DG.HttpJob.csproj", "DG.HttpJob/"]
|
||||||
|
RUN dotnet restore "DG.HttpJob/DG.HttpJob.csproj"
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/src/DG.HttpJob"
|
||||||
|
RUN dotnet build "DG.HttpJob.csproj" -c Release -o /app/build
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
RUN dotnet publish "DG.HttpJob.csproj" -c Release -o /app/publish /p:UseAppHost=false
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
ENTRYPOINT ["dotnet", "DG.HttpJob.dll"]
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
using Hangfire.Dashboard.BasicAuthorization;
|
||||||
|
using Hangfire;
|
||||||
|
using System.Configuration;
|
||||||
|
using Hangfire.HttpJob;
|
||||||
|
using Hangfire.MySql;
|
||||||
|
using System.Transactions;
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
// Add services to the container.
|
||||||
|
|
||||||
|
builder.Services.AddControllers();
|
||||||
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddSwaggerGen();
|
||||||
|
builder.Services.AddHangfire(Configuration);
|
||||||
|
builder.Services.AddHangfireServer();
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
void Configuration(IGlobalConfiguration globalConfiguration)
|
||||||
|
{
|
||||||
|
// ConfigManagerConf.SetConfiguration(_appConfiguration);
|
||||||
|
var mailServer = builder.Configuration.GetValue<string>("JobMail:Server");
|
||||||
|
var mailPort = builder.Configuration.GetValue<int>("JobMail:Port");
|
||||||
|
var mailUseSsl = builder.Configuration.GetValue<bool>("JobMail:UseSsl");
|
||||||
|
var mailUser = builder.Configuration.GetValue<string>("JobMail:User");
|
||||||
|
var mailPassword = builder.Configuration.GetValue<string>("JobMail:Password");
|
||||||
|
var jobOptions = new HangfireHttpJobOptions
|
||||||
|
{
|
||||||
|
MailOption = new MailOption
|
||||||
|
{
|
||||||
|
Server = mailServer,
|
||||||
|
Port = mailPort,
|
||||||
|
UseSsl = mailUseSsl,
|
||||||
|
User = mailUser,
|
||||||
|
Password = mailPassword,
|
||||||
|
},
|
||||||
|
DefaultRecurringQueueName = "recurring" //这个是在下面设置的queue列表中的其中一个
|
||||||
|
};
|
||||||
|
|
||||||
|
var tablePrefix = builder.Configuration.GetValue<string>("JobConfig:TablePrefix");
|
||||||
|
var mySqlStorage = new MySqlStorageOptions
|
||||||
|
{
|
||||||
|
TransactionIsolationLevel = IsolationLevel.ReadCommitted,
|
||||||
|
QueuePollInterval = TimeSpan.FromSeconds(15),
|
||||||
|
JobExpirationCheckInterval = TimeSpan.FromHours(1),
|
||||||
|
CountersAggregateInterval = TimeSpan.FromMinutes(5),
|
||||||
|
PrepareSchemaIfNecessary = true,
|
||||||
|
DashboardJobListLimit = 50000,
|
||||||
|
TransactionTimeout = TimeSpan.FromMinutes(1)
|
||||||
|
};
|
||||||
|
var redisString = builder.Configuration.GetConnectionString("Jobs");
|
||||||
|
globalConfiguration.UseStorage(new MySqlStorage(redisString, mySqlStorage))
|
||||||
|
.UseHangfireHttpJob();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the HTTP request pipeline.
|
||||||
|
if (app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI();
|
||||||
|
}
|
||||||
|
//强制显示中文
|
||||||
|
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("zh-CN");
|
||||||
|
|
||||||
|
var serverName = "HttpJobs";
|
||||||
|
var queues = new List<string> { "default", "apis", "recurring" };
|
||||||
|
var jobOptions = new BackgroundJobServerOptions
|
||||||
|
{
|
||||||
|
Queues = queues.ToArray(),//队列名称,只能为小写
|
||||||
|
SchedulePollingInterval = TimeSpan.FromSeconds(15),//秒级任务需要配置短点,一般任务可以配置默认时间,默认15秒
|
||||||
|
ShutdownTimeout = TimeSpan.FromMinutes(30),//超时时间
|
||||||
|
WorkerCount = Environment.ProcessorCount * 5, //并发任务数
|
||||||
|
ServerName = serverName,//服务器名称
|
||||||
|
ServerTimeout = TimeSpan.FromMinutes(5)
|
||||||
|
};
|
||||||
|
|
||||||
|
var userName = builder.Configuration.GetValue<string>("JobConfig:UserName");
|
||||||
|
var password = builder.Configuration.GetValue<string>("JobConfig:Password");
|
||||||
|
var hangfireStartUpPath = "/jobs";
|
||||||
|
var hangfireFilterOptions = new DashboardOptions
|
||||||
|
{
|
||||||
|
Authorization = new[] { new BasicAuthAuthorizationFilter(new BasicAuthAuthorizationFilterOptions
|
||||||
|
{
|
||||||
|
RequireSsl = false,
|
||||||
|
SslRedirect = false,
|
||||||
|
LoginCaseSensitive = true,
|
||||||
|
Users = new []
|
||||||
|
{
|
||||||
|
new BasicAuthAuthorizationUser
|
||||||
|
{
|
||||||
|
Login = userName,
|
||||||
|
PasswordClear = password,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) },
|
||||||
|
DisplayStorageConnectionString = false,
|
||||||
|
AppPath = "#"
|
||||||
|
};
|
||||||
|
//app.UseHangfireServer();
|
||||||
|
app.UseHangfireDashboard(hangfireStartUpPath, hangfireFilterOptions);
|
||||||
|
|
||||||
|
var hangfireReadOnlyPath = "/jobs-read";
|
||||||
|
//只读面板,只能读取不能操作
|
||||||
|
app.UseHangfireDashboard(hangfireReadOnlyPath, new DashboardOptions
|
||||||
|
{
|
||||||
|
IgnoreAntiforgeryToken = true,
|
||||||
|
AppPath = hangfireStartUpPath,//返回时跳转的地址
|
||||||
|
DisplayStorageConnectionString = false,//是否显示数据库连接信息
|
||||||
|
IsReadOnlyFunc = Context => true
|
||||||
|
});
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.MapControllers();
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"DG.HttpJob": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "jobs",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"applicationUrl": "https://localhost:7209;http://localhost:5080"
|
||||||
|
},
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "jobs",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Docker": {
|
||||||
|
"commandName": "Docker",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/jobs",
|
||||||
|
"publishAllPorts": true,
|
||||||
|
"useSSL": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:53403",
|
||||||
|
"sslPort": 44333
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*",
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"Jobs": "Server=192.168.11.41;Database=job;UserId=root;Password=sa123456.;port=3306;Convert Zero Datetime=True;Allow User Variables=True;pooling=true"
|
||||||
|
},
|
||||||
|
"JobConfig": {
|
||||||
|
"TablePrefix": "Dev",
|
||||||
|
"UserName": "admin",
|
||||||
|
"Password": "admin"
|
||||||
|
},
|
||||||
|
"JobMail": {
|
||||||
|
"Server": "smtp.exmail.qq.com",
|
||||||
|
"Port": 465,
|
||||||
|
"UseSsl": true,
|
||||||
|
"User": "",
|
||||||
|
"Password": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Kafka.Worker
|
||||||
|
{
|
||||||
|
public abstract class BatchKafkaWorkerBase<T>
|
||||||
|
{
|
||||||
|
private readonly ILogger<BatchKafkaWorkerBase<T>> _logger;
|
||||||
|
private TaskConfig _taskConfig;
|
||||||
|
private int _milliSecondsDelay;
|
||||||
|
|
||||||
|
public BatchKafkaWorkerBase(ILogger<BatchKafkaWorkerBase<T>> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_taskConfig = KafkaClient.Default.ConfigurationManager.GetSection("TaskConfig").Get<TaskConfig>();
|
||||||
|
if (_taskConfig == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(_taskConfig));
|
||||||
|
}
|
||||||
|
_milliSecondsDelay = _taskConfig.MilliSecondsDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Start(List<T> t)
|
||||||
|
{
|
||||||
|
Stopwatch watch = new Stopwatch();
|
||||||
|
watch.Reset();
|
||||||
|
watch.Start();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_milliSecondsDelay <= 0)
|
||||||
|
{
|
||||||
|
_logger.LogWarning($"[{DateTimeOffset.Now}] [{_taskConfig.TaskName}] 任务定时时间不能少于0!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!_taskConfig.Enable)
|
||||||
|
{
|
||||||
|
await Task.Delay(_milliSecondsDelay);
|
||||||
|
_logger.LogWarning($"[{DateTimeOffset.Now}] [{_taskConfig.TaskName}] 任务停止启动!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await DoWorkAsync(t);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, $"[{DateTimeOffset.Now}] [{_taskConfig.TaskName}]");
|
||||||
|
}
|
||||||
|
watch.Stop();
|
||||||
|
double costtime = watch.ElapsedMilliseconds;
|
||||||
|
_logger.LogDebug($"[{DateTimeOffset.Now}] [{_taskConfig.TaskName}] 任务执行结束,用时:{costtime}ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual async Task DoWorkAsync(List<T> t)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual async Task ShopAsync()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
|
<Version>1.0.21</Version>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\DG.Kafka\DG.Kafka.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DG.Kafka.Worker
|
||||||
|
{
|
||||||
|
public interface IKafkaWorkerManager
|
||||||
|
{
|
||||||
|
Task RegisterWorker<TWorker, T>(string? topic) where TWorker : KafkaWorkerBase<T>;
|
||||||
|
|
||||||
|
Task RegisterBatchWorker<TWorker, T>(string? topic, int batchsize = 1000) where TWorker : BatchKafkaWorkerBase<T>;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue